File size: 5,338 Bytes
ca0b91b
1715337
 
 
 
 
 
5e618a7
1715337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109cadc
1715337
 
5e618a7
1715337
 
 
 
 
 
 
 
 
 
 
 
 
5e618a7
 
 
 
 
ca0b91b
 
5e618a7
1715337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0429733
 
ddc557f
1715337
5e618a7
1715337
 
 
 
 
 
 
 
 
109cadc
1715337
5e618a7
 
 
ca0b91b
5e618a7
1715337
 
 
 
0429733
1715337
5e618a7
1715337
 
 
 
109cadc
1715337
 
 
109cadc
1715337
109cadc
 
1715337
 
ca0b91b
1715337
 
 
 
ca0b91b
1715337
109cadc
 
 
8baa2b5
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# app.py
import os
import json
import re
import torch
from difflib import get_close_matches
from transformers import AutoTokenizer, AutoModelForCausalLM
from ddgs import DDGS
import gradio as gr

# === Загрузка patterns.json ===
PATTERNS = {}
HAS_PATTERNS = False
try:
    with open("patterns.json", "r", encoding="utf-8") as f:
        PATTERNS = json.load(f)
    HAS_PATTERNS = True
except:
    pass

KEYWORDS = {
    "привет": ["привет", "здравствуй", "хай"],
    "как дела": ["дела", "как ты", "настроение"],
    "имя": ["имя", "кто ты", "зовут"],
    "пока": ["пока", "выход", "до свидания", "стоп"]
}

def preprocess(text):
    return re.sub(r'[^а-яё\s]', ' ', text.lower()).strip()

# === Загрузка TinyLlama (без токена!) ===
print("Загрузка TinyLlama с Hugging Face Hub...")
MODEL_ID = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=False)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
    device_map="auto",
    low_cpu_mem_usage=True
)
print("✅ TinyLlama готова!")

# === Веб-поиск ===
def web_search(query, max_results=3):
    try:
        with DDGS() as ddgs:
            results = ddgs.text(query, region="ru-ru", max_results=max_results)
            return "\n".join([f"{r['title']}: {r['body']}" for r in results])
    except Exception as e:
        return f"Ошибка поиска: {str(e)[:100]}"

# === Генерация через TinyLlama ===
def generate_with_tinyllama(prompt_text, max_tokens=256):
    try:
        inputs = tokenizer(prompt_text, return_tensors="pt").to(model.device)
        outputs = model.generate(
            **inputs,
            max_new_tokens=max_tokens,
            do_sample=True,
            temperature=0.6,
            top_p=0.92,
            pad_token_id=tokenizer.eos_token_id,
            repetition_penalty=1.1
        )
        full = tokenizer.decode(outputs[0], skip_special_tokens=True)
        if "<|assistant|>" in full:
            return full.split("<|assistant|>")[-1].strip()
        else:
            return full[len(prompt_text):].strip()
    except Exception as e:
        return f"Ошибка генерации: {str(e)[:100]}"

# === Fallback через patterns.json ===
def get_fallback_response(user_input):
    if not HAS_PATTERNS:
        return None
    clean = preprocess(user_input)
    if not clean:
        return None
    if any(w in clean for w in ["пока", "выход", "стоп", "до свидания"]):
        return random.choice(PATTERNS["пока"])
    knowledge = PATTERNS.get("knowledge", {})
    if clean in knowledge:
        return knowledge[clean]
    matches = get_close_matches(clean, knowledge.keys(), n=1, cutoff=0.6)
    if matches:
        return knowledge[matches[0]]
    for intent, words in KEYWORDS.items():
        if any(get_close_matches(token, words, n=1, cutoff=0.6) for token in clean.split()):
            return random.choice(PATTERNS[intent])
    return None

# === Основная логика ===
def respond(message, history):
    user_input = message.strip()
    user_lower = user_input.lower()

    # === Системные команды (заглушка) ===
    if user_lower.startswith("система:"):
        return "🔒 Управление ОС недоступно в демо."

    # === Перевод ===
    if user_lower.startswith("перевод:"):
        text = user_input[8:].strip()
        if not text:
            return "🔤 Пример: `перевод: Hello, how are you?`"
        prompt = f"<|user|>\nПереведи на русский язык: {text}\n<|assistant|>\n"
        return generate_with_tinyllama(prompt, max_tokens=128)

    # === Веб-поиск ===
    if user_lower.startswith("поиск:"):
        query = user_input[6:].strip()
        if not query:
            return "🔍 Пример: `поиск: погода в Москве`"
        context = web_search(query)
        prompt = (
            f"<|user|>\nИнформация из интернета:\n{context}\n\n"
            f"Кратко ответь на русском: {query}\n"
            f"<|assistant|>\n"
        )
        return generate_with_tinyllama(prompt, max_tokens=256)

    # === Fallback через patterns.json ===
    fallback = get_fallback_response(user_input)
    if fallback:
        return fallback

    # === Обычный режим ===
    prompt = f"<|user|>\n{user_input}\nОтветь кратко на русском.\n<|assistant|>\n"
    return generate_with_tinyllama(prompt, max_tokens=256)

# === Gradio интерфейс ===
chatbot = gr.ChatInterface(
    respond,
    title="🚀 Newton MAX (TinyLlama + поиск + перевод)",
    description="Поддержка команд: `поиск: ...`, `перевод: ...`, `система: ...`",
    examples=[
        "Привет!",
        "Что такое квантовый компьютер?",
        "поиск: курс доллара",
        "перевод: How are you doing today?"
    ],
    theme="soft"
)

if __name__ == "__main__":
    chatbot.launch()