doyeqkl commited on
Commit
d2c5d19
·
verified ·
1 Parent(s): 5398f51

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +95 -56
main.py CHANGED
@@ -1,90 +1,129 @@
 
1
  import json
2
  from fastapi import FastAPI, HTTPException
3
- from fastapi.responses import StreamingResponse
 
4
  from pydantic import BaseModel
5
  from llama_cpp import Llama
6
  from huggingface_hub import hf_hub_download
 
7
 
8
- app = FastAPI(title="Fast Gemma API")
9
 
10
  # --- КОНФИГУРАЦИЯ ---
 
 
11
  REPO_ID = "bartowski/Qwen2.5-1.5B-Instruct-GGUF"
12
- # Используем Q4_K_M - это золотая середина скорости и ума
13
  FILENAME = "Qwen2.5-1.5B-Instruct-Q6_K.gguf"
14
 
15
  llm = None
 
16
 
 
17
  @app.on_event("startup")
18
  def startup_event():
19
- global llm
 
 
 
 
 
 
 
20
  print("🚀 Загрузка модели...")
21
  try:
22
- model_path = hf_hub_download(
23
- repo_id=REPO_ID,
24
- filename=FILENAME,
25
- cache_dir="./models"
26
- )
27
-
28
- # --- ОПТИМИЗАЦИЯ ЗАГРУЗКИ ---
29
  llm = Llama(
30
  model_path=model_path,
31
- n_ctx=4096, # Увеличим контекст
32
- n_threads=2, # Лимит Hugging Face
33
- n_batch=1024, # <--- УСКОРЕНИЕ: Читаем промпт большими кусками
34
- verbose=False # Меньше мусора в логах
35
  )
36
- print("✅ Модель готова и ускорена!")
37
  except Exception as e:
38
  print(f"❌ Ошибка: {e}")
39
 
40
- # --- МОДЕЛИ ДАННЫХ ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  class Message(BaseModel):
42
  role: str
43
  content: str
44
 
45
  class ChatRequest(BaseModel):
46
  messages: list[Message]
47
- temperature: float = 0.7
48
- max_tokens: int = 500
49
- stream: bool = False # <--- Добавили флаг стриминга
 
50
 
51
- # --- ЭНДПОИНТ ---
52
  @app.post("/v1/chat/completions")
53
- def chat_completions(request: ChatRequest):
54
- if not llm:
55
- raise HTTPException(status_code=503, detail="Модель грузится...")
56
-
57
- # Подготовка сообщений
58
- messages_payload = [{"role": m.role, "content": m.content} for m in request.messages]
59
-
60
- # === РЕЖИМ 1: STREAMING (МГНОВЕННЫЙ ОТВЕТ) ===
61
- if request.stream:
62
- def iter_response():
63
- # Запрашиваем поток у модели
64
- stream_gen = llm.create_chat_completion(
65
- messages=messages_payload,
66
- temperature=request.temperature,
67
- max_tokens=request.max_tokens,
68
- stream=True # Включаем стрим в движке
69
- )
70
- # Читаем генератор по кусочкам
71
- for chunk in stream_gen:
72
- # Формируем формат Server-Sent Events (как у OpenAI)
73
- yield f"data: {json.dumps(chunk)}\n\n"
74
- yield "data: [DONE]\n\n"
75
-
76
- return StreamingResponse(iter_response(), media_type="text/event-stream")
77
-
78
- # === РЕЖИМ 2: ОБЫЧНЫЙ (ЖДЕМ ВЕСЬ ТЕКСТ) ===
79
  else:
80
- response = llm.create_chat_completion(
81
- messages=messages_payload,
82
- temperature=request.temperature,
83
- max_tokens=request.max_tokens,
84
- stream=False
 
 
 
 
85
  )
86
- return response
 
 
 
 
 
 
 
 
 
 
87
 
88
- @app.get("/")
89
- def home():
90
- return {"status": "running", "optimization": "enabled"}
 
1
+ import os
2
  import json
3
  from fastapi import FastAPI, HTTPException
4
+ from fastapi.responses import StreamingResponse, FileResponse
5
+ from fastapi.staticfiles import StaticFiles
6
  from pydantic import BaseModel
7
  from llama_cpp import Llama
8
  from huggingface_hub import hf_hub_download
9
+ from tavily import TavilyClient
10
 
11
+ app = FastAPI(title="Qwen Turbo Search API")
12
 
13
  # --- КОНФИГУРАЦИЯ ---
14
+ TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")
15
+
16
  REPO_ID = "bartowski/Qwen2.5-1.5B-Instruct-GGUF"
 
17
  FILENAME = "Qwen2.5-1.5B-Instruct-Q6_K.gguf"
18
 
19
  llm = None
20
+ tavily_client = None
21
 
22
+ # --- ИНИЦИАЛИЗАЦИЯ ---
23
  @app.on_event("startup")
24
  def startup_event():
25
+ global llm, tavily_client
26
+
27
+ if TAVILY_API_KEY:
28
+ tavily_client = TavilyClient(api_key=TAVILY_API_KEY)
29
+ print("✅ Tavily Search подключен")
30
+ else:
31
+ print("⚠️ Нет TAVILY_API_KEY. Поиск работать не будет.")
32
+
33
  print("🚀 Загрузка модели...")
34
  try:
35
+ model_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME, cache_dir="./models")
 
 
 
 
 
 
36
  llm = Llama(
37
  model_path=model_path,
38
+ n_ctx=8192,
39
+ n_threads=2,
40
+ n_batch=1024,
41
+ verbose=False
42
  )
43
+ print("✅ Модель готова!")
44
  except Exception as e:
45
  print(f"❌ Ошибка: {e}")
46
 
47
+ # --- ПОДКЛЮЧАЕМ ИНТЕРФЕЙС ---
48
+ # Создай папку static рядом с main.py!
49
+ app.mount("/static", StaticFiles(directory="static"), name="static")
50
+
51
+ @app.get("/")
52
+ def read_root():
53
+ # Отдаем наш HTML файл при входе на главную
54
+ return FileResponse('static/index.html')
55
+
56
+ # --- ЛОГИКА ПОИСКА ---
57
+ def perform_search(query: str):
58
+ if not tavily_client: return "Нет ключа Tavily.", []
59
+ print(f"🔎 Ищу: {query}")
60
+ try:
61
+ res = tavily_client.search(query=query, search_depth="advanced", max_results=5)
62
+ text = ""
63
+ sources = []
64
+ for i, r in enumerate(res['results']):
65
+ idx = i + 1
66
+ text += f"ИСТОЧНИК [{idx}]: {r['title']}\nТЕКСТ: {r['content']}\n\n"
67
+ sources.append({"id": idx, "title": r['title'], "url": r['url']})
68
+ return text, sources
69
+ except Exception as e:
70
+ print(f"Err: {e}")
71
+ return "Ошибка поиска.", []
72
+
73
+ # --- API ---
74
  class Message(BaseModel):
75
  role: str
76
  content: str
77
 
78
  class ChatRequest(BaseModel):
79
  messages: list[Message]
80
+ temperature: float = 0.6
81
+ max_tokens: int = 2048
82
+ stream: bool = True
83
+ use_search: bool = False
84
 
 
85
  @app.post("/v1/chat/completions")
86
+ def chat_completions(req: ChatRequest):
87
+ if not llm: raise HTTPException(503, "Loading...")
88
+
89
+ msgs = [{"role": m.role, "content": m.content} for m in req.messages]
90
+
91
+ # Поиск
92
+ if req.use_search:
93
+ query = msgs[-1]['content']
94
+ context, sources = perform_search(query)
95
+
96
+ sys_prompt = (
97
+ "Ты умный помощник. Отвечай на вопрос, используя ТОЛЬКО эти данные из интернета.\n"
98
+ "Обязательно указывай источники [1], [2].\n"
99
+ f"=== ДАННЫЕ ===\n{context}"
100
+ )
101
+ # Добавляем источники в конец последнего сообщения (для UI)
102
+ sources_md = "\n\n**Источники:**\n" + "\n".join([f"{s['id']}. [{s['title']}]({s['url']})" for s in sources])
103
+
104
+ # Инъекция системного промпта
105
+ msgs.insert(0, {"role": "system", "content": sys_prompt})
 
 
 
 
 
 
106
  else:
107
+ sources_md = ""
108
+
109
+ # Генерация
110
+ def iter_response():
111
+ stream = llm.create_chat_completion(
112
+ messages=msgs,
113
+ temperature=req.temperature,
114
+ max_tokens=req.max_tokens,
115
+ stream=True
116
  )
117
+ for chunk in stream:
118
+ yield f"data: {json.dumps(chunk)}\n\n"
119
+
120
+ # Если были источники, отправим их отдельным чанком в конце
121
+ if sources_md:
122
+ final_chunk = {
123
+ "choices": [{"delta": {"content": sources_md}, "finish_reason": None}]
124
+ }
125
+ yield f"data: {json.dumps(final_chunk)}\n\n"
126
+
127
+ yield "data: [DONE]\n\n"
128
 
129
+ return StreamingResponse(iter_response(), media_type="text/event-stream")