File size: 5,820 Bytes
82f18d5
19c5734
 
 
82f18d5
 
19c5734
 
 
82f18d5
19c5734
 
 
 
 
76e29f8
19c5734
 
 
 
 
76e29f8
19c5734
 
 
76e29f8
19c5734
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76e29f8
19c5734
 
 
 
 
76e29f8
19c5734
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76e29f8
19c5734
 
 
76e29f8
19c5734
 
 
 
 
 
 
 
 
 
 
76e29f8
19c5734
 
 
 
 
 
 
 
 
76e29f8
3d856bc
 
19c5734
 
 
82f18d5
76e29f8
19c5734
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
148
149
150
151
152
153
import gradio as gr
import requests
import os
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

# ========== 从环境变量读取密钥 ==========
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
CEREBRAS_API_KEY = os.getenv("CEREBRAS_API_KEY")

# ========== 模型定义 ==========
LOCAL_MODELS = {
    "DistilGPT2 (最快)": "distilgpt2",
    "DialoGPT-small (轻量)": "microsoft/DialoGPT-small",
    "DialoGPT-medium (均衡)": "microsoft/DialoGPT-medium",
}

OPENROUTER_MODELS = {
    "Llama 3.3 70B (免费)": "meta-llama/llama-3.3-70b-instruct:free",
    "Nemotron 3 Super (免费)": "nvidia/nemotron-3-super:free",
    "Qwen3 Next 80B (免费)": "qwen/qwen3-next-80b:free",
}

CEREBRAS_MODELS = {
    "Llama 3.1 8B (Cerebras)": "llama3.1-8b",
}

local_model_cache = {}

def load_local_model(model_key):
    if model_key in local_model_cache:
        return local_model_cache[model_key]
    model_id = LOCAL_MODELS[model_key]
    tokenizer = AutoTokenizer.from_pretrained(model_id)
    model = AutoModelForCausalLM.from_pretrained(model_id)
    if tokenizer.pad_token is None:
        tokenizer.pad_token = tokenizer.eos_token
    local_model_cache[model_key] = (model, tokenizer)
    return model, tokenizer

def is_chinese(text):
    return any('\u4e00' <= c <= '\u9fff' for c in text)

def local_predict(message, model_key):
    if is_chinese(message):
        return "⚠️ This model only supports English input. 请使用英文。"
    model, tokenizer = load_local_model(model_key)
    inputs = tokenizer.encode(message + tokenizer.eos_token, return_tensors='pt')
    outputs = model.generate(
        inputs,
        max_new_tokens=100,
        do_sample=True,
        temperature=0.7,
        repetition_penalty=1.2,
        pad_token_id=tokenizer.eos_token_id
    )
    reply = tokenizer.decode(outputs[0][inputs.shape[-1]:], skip_special_tokens=True)
    return reply.strip() or "I'm not sure how to respond."

def openrouter_predict(message, model_name):
    if not OPENROUTER_API_KEY:
        return "❌ OpenRouter API key 未设置,请在 Space Secrets 中添加 OPENROUTER_API_KEY"
    headers = {
        "Authorization": f"Bearer {OPENROUTER_API_KEY}",
        "HTTP-Referer": "https://your-space.hf.space",
        "X-Title": "AI ChatBot",
        "Content-Type": "application/json"
    }
    payload = {
        "model": model_name,
        "messages": [{"role": "user", "content": message}],
        "max_tokens": 500,
        "temperature": 0.7
    }
    try:
        resp = requests.post("https://openrouter.ai/api/v1/chat/completions", headers=headers, json=payload, timeout=60)
        if resp.status_code == 200:
            return resp.json()["choices"][0]["message"]["content"]
        elif resp.status_code == 402:
            return "⚠️ 免费额度已用完或需启用付费模型。"
        else:
            return f"❌ API 错误 {resp.status_code}"
    except Exception as e:
        return f"❌ 请求失败: {e}"

def cerebras_predict(message, model_name):
    if not CEREBRAS_API_KEY:
        return "❌ Cerebras API key 未设置,请在 Space Secrets 中添加 CEREBRAS_API_KEY"
    headers = {
        "Authorization": f"Bearer {CEREBRAS_API_KEY}",
        "Content-Type": "application/json"
    }
    payload = {
        "model": model_name,
        "messages": [{"role": "user", "content": message}],
        "max_tokens": 500,
        "temperature": 0.7
    }
    try:
        resp = requests.post("https://api.cerebras.ai/v1/chat/completions", headers=headers, json=payload, timeout=60)
        if resp.status_code == 200:
            return resp.json()["choices"][0]["message"]["content"]
        else:
            return f"❌ Cerebras 错误 {resp.status_code}"
    except Exception as e:
        return f"❌ 请求失败: {e}"

def predict(message, history, backend, model_choice):
    if not message.strip():
        return ""
    if backend == "本地 CPU 模型 (限英文)":
        return local_predict(message, model_choice)
    elif backend == "OpenRouter (不限语言)":
        return openrouter_predict(message, model_choice)
    elif backend == "Cerebras (不限语言)":
        return cerebras_predict(message, model_choice)
    else:
        return "未知后端"

with gr.Blocks(title="多后端 AI 聊天机器人", theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 🧠 多后端 AI 聊天机器人")
    gr.Markdown("**本地 CPU 模型**仅限英文;**OpenRouter / Cerebras** 不限语言,且质量更高。")

    with gr.Row():
        backend_radio = gr.Radio(
            choices=["本地 CPU 模型 (限英文)", "OpenRouter (不限语言)", "Cerebras (不限语言)"],
            label="选择后端",
            value="本地 CPU 模型 (限英文)"
        )
        model_dropdown = gr.Dropdown(
            choices=list(LOCAL_MODELS.keys()),
            label="模型选择",
            value=list(LOCAL_MODELS.keys())[2]
        )

    def update_model_dropdown(backend):
        if backend == "本地 CPU 模型 (限英文)":
            return gr.update(choices=list(LOCAL_MODELS.keys()), value=list(LOCAL_MODELS.keys())[2], visible=True)
        elif backend == "OpenRouter (不限语言)":
            return gr.update(choices=list(OPENROUTER_MODELS.keys()), value=list(OPENROUTER_MODELS.keys())[0], visible=True)
        else:
            return gr.update(choices=list(CEREBRAS_MODELS.keys()), value=list(CEREBRAS_MODELS.keys())[0], visible=True)

    backend_radio.change(fn=update_model_dropdown, inputs=backend_radio, outputs=model_dropdown)

    chatbot = gr.ChatInterface(
        fn=predict,
        additional_inputs=[backend_radio, model_dropdown],
        title=None,
        description="多轮对话,自动记忆上下文。"
    )

demo.launch(server_port=7860)