2 Commits

Author SHA1 Message Date
a868025a9e add minimal fix 2025-08-03 08:30:12 +00:00
9e86008ba0 add fetures change models in web 2025-08-03 08:03:19 +00:00
3 changed files with 113 additions and 38 deletions

View File

@@ -37,4 +37,4 @@ RUN pip install --no-cache-dir -r /app/requirements.txt
COPY app /app
# Запуск приложения
CMD ["python", "main.py"]
#CMD ["python", "main.py"]

View File

@@ -12,39 +12,45 @@ from config import settings
class ChatWithAI:
def __init__(self, provider: str = "qwen3"):
self.provider = provider
def __init__(self, model_name: str):
self.model_name = model_name
self.embeddings = HuggingFaceEmbeddings(
model_name=settings.LM_MODEL_NAME,
model_kwargs={"device": "cuda"},
encode_kwargs={"normalize_embeddings": True},
)
if provider == "qwen3":
model_name = getattr(settings, "LOCAL_LLM_NAME", "/models/Qwen3-4B")
logger.info(f"Загрузка локальной модели: {model_name}")
self.chroma_db = Chroma(
persist_directory=settings.DOCS_CHROMA_PATH,
embedding_function=self.embeddings,
collection_name=settings.DOCS_COLLECTION_NAME,
)
self.tokenizer = None
self.model = None
self.streamer = None
self.load_model(model_name)
def load_model(self, model_name: str):
"""Загружает модель и токенизатор, освобождает предыдущие ресурсы."""
if hasattr(self, "model") and self.model is not None:
del self.model
torch.cuda.empty_cache()
logger.info("Предыдущая модель удалена.")
logger.info(f"Загрузка модели: {model_name}")
try:
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float32,
device_map="cuda",
)
# Streamer для потоковой генерации
self.streamer = TextIteratorStreamer(
self.tokenizer,
skip_prompt=True,
skip_special_tokens=True
)
else:
raise ValueError(f"Неподдерживаемый провайдер: {provider}")
self.chroma_db = Chroma(
persist_directory=settings.DOCS_CHROMA_PATH,
embedding_function=self.embeddings,
collection_name=settings.DOCS_COLLECTION_NAME,
)
logger.info(f"Модель {model_name} успешно загружена.")
except Exception as e:
logger.error(f"Ошибка при загрузке модели {model_name}: {e}")
raise
def get_relevant_context(self, query: str, k: int = 3) -> List[Dict[str, Any]]:
"""Получение релевантного контекста из базы данных."""
@@ -74,7 +80,6 @@ class ChatWithAI:
def generate_response_stream(self, query: str):
"""Генерация ответа с потоковой передачей токенов."""
try:
logger.info(f"Пользовательский запрос: {query}")
context = self.get_relevant_context(query)
if not context:
yield "Извините, не удалось найти релевантный контекст для ответа."
@@ -85,7 +90,7 @@ class ChatWithAI:
messages = [
{
"role": "system",
"content": """Ты — внутренний менеджер компании Mazban. Отвечаешь по делу без лишних вступлений.
"content": """Ты — внутренний менеджер компании Amvera Cloud. Отвечаешь по делу без лишних вступлений.
Правила:
1. Сразу переходи к сути, без фраз типа "На основе контекста"
@@ -118,13 +123,14 @@ class ChatWithAI:
# Подготавливаем вход
inputs = self.tokenizer(prompt, return_tensors="pt").to("cuda")
# Очищаем streamer и запускаем генерацию в отдельном потоке
# Создаём новый streamer
self.streamer = TextIteratorStreamer(
self.tokenizer,
skip_prompt=True,
skip_special_tokens=True
)
# Параметры генерации
generate_kwargs = {
"input_ids": inputs["input_ids"],
"max_new_tokens": 512,
@@ -135,6 +141,7 @@ class ChatWithAI:
"streamer": self.streamer,
}
# Запускаем генерацию в отдельном потоке
thread = threading.Thread(target=self.model.generate, kwargs=generate_kwargs)
thread.start()
@@ -142,32 +149,100 @@ class ChatWithAI:
buffer = ""
for token in self.streamer:
buffer += token
yield buffer # Отправляем частичный ответ
yield buffer
except Exception as e:
logger.error(f"Ошибка при генерации ответа: {e}")
yield "Произошла ошибка при генерации ответа."
# === Gradio интерфейс ===
# === Gradio интерфейс с выбором модели ===
def main():
chat = ChatWithAI(provider="qwen3")
# Список доступных моделей (можно вынести в settings)
MODEL_OPTIONS = {
"Qwen3-4B (локальная)": getattr(settings, "LOCAL_LLM_NAME", "/models/Qwen3-4B"),
"Alibaba/Qwen2.5-1.8B-Chat": "Alibaba/Qwen2.5-1.8B-Chat",
"HuggingFaceH4/zephyr-7b-beta": "HuggingFaceH4/zephyr-7b-beta",
# Добавь другие модели по желанию
}
chat = None # Будет инициализирована при выборе модели
def initialize_model(selected_model_key):
"""Создаёт или перезагружает экземпляр ChatWithAI с выбранной моделью."""
nonlocal chat
if chat is not None:
logger.info(f"Смена модели")
# Удаляем экземпляр
del chat
torch.cuda.empty_cache()
model_path = MODEL_OPTIONS[selected_model_key]
try:
chat = ChatWithAI(model_path)
return gr.update(interactive=True, placeholder="Введите свой вопрос..."), \
gr.update(visible=True), \
f"✅ Модель загружена: {selected_model_key}"
except Exception as e:
logger.error(f"Не удалось загрузить модель {selected_model_key}: {e}")
return gr.update(interactive=False), \
gr.update(visible=False), \
f"❌ Ошибка загрузки модели: {str(e)}"
def respond(message, history):
# Генерируем ответ по частям
for token in chat.generate_response_stream(message):
yield token
demo = gr.ChatInterface(
fn=respond,
title="Помощник настройки Marzban",
description="Задайте вопрос — получите ответ от внутреннего менеджера.",
examples=[
"Как подключить Marzban?",
"Как настроить telegram бота?",
"Что такое marzban?"
],
)
with gr.Blocks(title="Amvera Cloud Assistant") as demo:
gr.Markdown("# 🤖 Amvera Cloud Assistant")
gr.Markdown("Выберите модель и задайте вопрос — получите ответ с контекстом.")
with gr.Row():
with gr.Column(scale=1):
model_dropdown = gr.Dropdown(
choices=list(MODEL_OPTIONS.keys()),
value=list(MODEL_OPTIONS.keys())[0],
label="Выберите модель",
interactive=True
)
load_button = gr.Button("Загрузить модель", variant="primary")
status_text = gr.Textbox(
label="Состояние",
value="Выберите модель и нажмите 'Загрузить модель'",
interactive=False
)
with gr.Column(scale=3):
chatbot = gr.Chatbot(
label="Чат",
height=600,
bubble_full_width=False,
)
msg = gr.Textbox(
label="Сообщение",
placeholder="Введите ваш вопрос...",
interactive=False
)
clear = gr.Button("Очистить чат")
# Логика выбора модели
load_button.click(
initialize_model,
inputs=[model_dropdown],
outputs=[msg, chatbot, status_text]
)
# Отправка сообщения
msg.submit(
respond,
inputs=[msg, chatbot],
outputs=[chatbot]
)
# Очистка чата
clear.click(lambda: None, None, chatbot, queue=False)
demo.launch(server_name="0.0.0.0", server_port=8080, share=False)

View File

@@ -20,7 +20,7 @@ services:
- ./cache/cache:/root/.cache
- ./cache/site-packages:/usr/local/lib/python3.10/site-packages
- ./offline_packages:/offline_packages
# entrypoint: sleep 1000000 #./app/entrypoint.sh
entrypoint: sleep 1000000 #./app/entrypoint.sh
ports:
- "8080:8080"
networks: