init
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/app/.gradio
|
||||
/app/core
|
||||
/models
|
||||
/cache
|
||||
40
Dockerfile
Normal file
40
Dockerfile
Normal file
@@ -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"]
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Для запуска используй команду:
|
||||
|
||||
docker compose up -d --configure
|
||||
77
app/main.py
Normal file
77
app/main.py
Normal file
@@ -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
|
||||
)
|
||||
36
docker-compose.yml
Normal file
36
docker-compose.yml
Normal file
@@ -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"
|
||||
5
offline_packages/requirements.txt
Normal file
5
offline_packages/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
torch>=2.6.0
|
||||
transformers>=4.50.0
|
||||
gradio>=4.29.0
|
||||
safetensors>=0.4.3
|
||||
accelerate>=0.29.3
|
||||
Reference in New Issue
Block a user