File size: 4,633 Bytes
56d2d0f
583a271
56d2d0f
 
 
 
583a271
56d2d0f
 
 
 
c8dd008
5bd5e01
 
 
ba425e2
f0fffb2
 
 
100e61f
c8dd008
ba425e2
583a271
 
 
 
 
 
 
 
 
 
 
5892ed0
 
 
 
 
 
583a271
 
 
 
f0fffb2
c8dd008
f0fffb2
 
c8dd008
f0fffb2
56d2d0f
 
 
 
 
f0fffb2
56d2d0f
 
 
 
 
 
 
 
 
c8dd008
583a271
 
 
 
 
c8dd008
56d2d0f
583a271
56d2d0f
 
 
f0fffb2
 
56d2d0f
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import os
import re
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
from huggingface_hub import hf_hub_download
from llama_cpp import Llama
from tavily import TavilyClient

API_KEYS = {k.strip() for k in os.environ.get("GINI_API_KEYS", "").split(",") if k.strip()}
if os.environ.get("GINI_API_KEY"): API_KEYS.add(os.environ.get("GINI_API_KEY"))

SYSTEM_PROMPT = (
    "Ты — продвинутый ИИ-ассистент по имени Gini (по-русски твое имя звучит как Джини). "
    "Ты дружелюбная, невероятно умная и всегда готовая помочь собеседница. "
    "Отвечай всегда на естественном, живом и грамотном русском языке. "
    "\n\nВАЖНОЕ ПРАВИЛО ДЛЯ ТЕКУЩЕГО РЕЖИМА: "
    "В данный момент ты выступаешь в роли Senior-эксперта, архитектора и глубокого мыслителя. "
    "Обязательно используй свою встроенную систему размышления (выводи свои логические шаги внутри тегов <think>). "
    "Сначала рассмотри проблему с разных сторон, проанализируй варианты, проверь себя на ошибки, а затем, после закрытия тега </think>, выдай окончательный, идеально структурированный ответ."
    "Отвечай ИСКЛЮЧИТЕЛЬНО на чистом русском языке. Тебе КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО использовать китайские иероглифы или китайские слова в своих ответах."
)

TAVILY_API_KEY = "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"
tavily = TavilyClient(api_key=TAVILY_API_KEY)

def get_web_context(query: str) -> str:
    query_lower = query.lower()
    keywords = ["найди", "поищи", "погугли", "кто", "что", "когда", "где", "какая", "почему", "новости", "вчера", "погода", "курс"]
    if any(k in query_lower for k in keywords) or query.endswith("?"):
        try:
            response = tavily.search(query, search_depth="basic", max_results=3)
            results = response.get("results", [])
            if results:
                snippets = [f"- {r.get('title', '')} (Источник: {r.get('url', '')}): {r.get('content', '')}" for r in results]
                return (
                    "ВНИМАНИЕ! Свежая информация из интернета по запросу пользователя:\n" 
                    + "\n".join(snippets) 
                    + "\n\nОпирайся строго на эти факты при ответе и ОБЯЗАТЕЛЬНО красиво укажи ссылки на источники в конце своего ответа."
                )
        except Exception:
            pass
    return ""

# Загружаем мыслящую DeepSeek-R1 (на базе 7-миллиардной Qwen)
MODEL_PATH = hf_hub_download(
    repo_id="bartowski/DeepSeek-R1-Distill-Qwen-7B-GGUF",
    filename="DeepSeek-R1-Distill-Qwen-7B-Q4_K_M.gguf",
)
llm = Llama(model_path=MODEL_PATH, n_ctx=8192, n_threads=int(os.environ.get("N_THREADS", "2")), verbose=False)

app = FastAPI()

class GenerateRequest(BaseModel):
    prompt: str
    max_new_tokens: int = 2000  # Выдаем огромный лимит, чтобы ей хватило места "подумать"

@app.get("/ping")
async def ping(): return {"status": "ok"}

@app.post("/generate")
async def generate(req: GenerateRequest, request: Request):
    auth = request.headers.get("Authorization", "")
    if auth.removeprefix("Bearer ").strip() not in API_KEYS:
        raise HTTPException(status_code=401, detail="Unauthorized")

    system_content = SYSTEM_PROMPT
    web_context = get_web_context(req.prompt)
    if web_context:
        system_content += f"\n\n{web_context}"

    result = llm.create_chat_completion(
        messages=[
            {"role": "system", "content": system_content},
            {"role": "user", "content": req.prompt},
        ],
        max_tokens=req.max_new_tokens,
        temperature=0.6,  # Идеальная температура для R1
        top_p=0.95,
        repeat_penalty=1.1,
    )
    return {"response": result["choices"][0]["message"]["content"].strip()}