From 187bcc61bd5fa845bad85efda9b117a980576822 Mon Sep 17 00:00:00 2001 From: drholy Date: Sat, 2 Aug 2025 14:55:41 +0000 Subject: [PATCH] init --- .gitignore | 4 ++ Dockerfile | 40 ++++++++++++++++ README.md | 3 ++ app/main.py | 77 +++++++++++++++++++++++++++++++ docker-compose.yml | 36 +++++++++++++++ offline_packages/requirements.txt | 5 ++ 6 files changed, 165 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 app/main.py create mode 100644 docker-compose.yml create mode 100644 offline_packages/requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f1f1c44 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/app/.gradio +/app/core +/models +/cache \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8f93be0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# Базовый образ с поддержкой CUDA +FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04 + +# Установка метаданных +LABEL maintainer="dr.holyblack@gmail.com" +LABEL description="Qwen3-8B-AWQ with Gradio interface" + +# Установка системных зависимостей +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + python3.10 \ + python3-pip \ + git \ + curl \ + wget \ + libgl1 \ + libglib2.0-0 \ + && rm -rf /var/lib/apt/lists/* + +# Установка Python 3.10 как основного +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1 && \ + update-alternatives --install /usr/bin/pip pip /usr/bin/pip3.10 1 + +# Установка базовых Python-зависимостей +RUN pip install --upgrade pip && \ + pip install setuptools wheel + +WORKDIR /app + +# Копируем только requirements.txt для кэширования слоя +COPY offline_packages/requirements.txt /app/requirements.txt + +# Установка зависимостей (включая GPU-версии) +RUN pip install --no-cache-dir -r /app/requirements.txt + +# Копируем приложение +COPY app /app + +# Запуск приложения +CMD ["python", "main.py"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a4c8010 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +Для запуска используй команду: + +docker compose up -d --configure \ No newline at end of file diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..5185220 --- /dev/null +++ b/app/main.py @@ -0,0 +1,77 @@ +import gradio as gr +from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer +import torch +import threading + +# Пути к модели +model_name = "/models/Qwen3-4B-Base" + +# Загрузка токенизатора и модели (один раз при старте) +tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) +model = AutoModelForCausalLM.from_pretrained( + model_name, + torch_dtype=torch.float16, + device_map="cuda", + trust_remote_code=True +) + +# Отключим кэширование в истории, чтобы каждый запрос был независимым +def generate_response(message, history): + # Форматируем диалог: используем только текущую историю + prompt = "" + for human, assistant in history: + prompt += f"<|im_start|>user\n{human}<|im_end|>\n<|im_start|>assistant\n{assistant}<|im_end|>\n" + prompt += f"<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n" + + # Токенизация + inputs = tokenizer(prompt, return_tensors="pt").to(model.device) + + # Создаём уникальный streamer для каждого запроса + streamer = TextIteratorStreamer( + tokenizer, + skip_prompt=True, + skip_special_tokens=True + ) + + # Параметры генерации + generation_kwargs = { + "input_ids": inputs["input_ids"], + "max_new_tokens": 1024, + "temperature": 0.6, + "top_p": 0.9, + "do_sample": True, + "pad_token_id": tokenizer.eos_token_id, + "streamer": streamer, + } + + # Запускаем генерацию в отдельном потоке + thread = threading.Thread(target=model.generate, kwargs=generation_kwargs) + thread.start() + + # Постепенно возвращаем результат + buffer = "" + for new_text in streamer: + buffer += new_text + yield buffer.strip() + +# Создаем интерфейс +demo = gr.ChatInterface( + fn=generate_response, + title="Qwen3-4B-Base Chat", + description="Общайтесь с моделью Qwen3-4B-Base в режиме реального времени с потоковой генерацией", + examples=[ + "Объясни, как работает квантование AWQ?", + "Напиши стихотворение про ИИ", + "Какие преимущества у Qwen3 перед предыдущими версиями?" + ], + theme="soft", +) + +if __name__ == "__main__": + # ВАЖНО: используем .queue() для поддержки асинхронной обработки + demo.queue(max_size=20, default_concurrency_limit=10).launch( + server_port=8080, + server_name="0.0.0.0", + share=False, + # Можно добавить: max_batch_size=1, concurrency_count=4 + ) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..47966b0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +services: + llm-chat: + build: + context: . + dockerfile: Dockerfile + platform: linux/amd64 + privileged: true + container_name: llm-chat + restart: always + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] + volumes: + - ./app:/app + - ./models:/models + - ./cache/cache:/root/.cache + - ./cache/site-packages:/usr/local/lib/python3.10/site-packages + - ./offline_packages:/offline_packages + # entrypoint: sleep 1000000 #./app/entrypoint.sh + ports: + - "8080:8080" + networks: + - apps +networks: + apps: + name: apps + driver: bridge + ipam: + driver: default + config: + - subnet: "172.100.0.0/24" + gateway: "172.100.0.1" \ No newline at end of file diff --git a/offline_packages/requirements.txt b/offline_packages/requirements.txt new file mode 100644 index 0000000..b4ed109 --- /dev/null +++ b/offline_packages/requirements.txt @@ -0,0 +1,5 @@ +torch>=2.6.0 +transformers>=4.50.0 +gradio>=4.29.0 +safetensors>=0.4.3 +accelerate>=0.29.3 \ No newline at end of file