File size: 5,321 Bytes
a943362
b0b9691
 
2293c14
02b1411
dd87cb1
 
 
b0b9691
 
02b1411
dd87cb1
02b1411
dbce7ce
 
 
f995bdc
9817165
dd87cb1
02b1411
dd87cb1
 
 
 
f995bdc
b0b9691
02b1411
dd87cb1
 
 
 
 
 
 
 
 
85f1ed2
dd87cb1
02b1411
dd87cb1
02b1411
dd87cb1
dbce7ce
 
 
b0b9691
dd87cb1
b0b9691
dbce7ce
 
 
dd87cb1
 
 
 
 
b0b9691
dd87cb1
dbce7ce
dd87cb1
 
 
 
dbce7ce
 
dd87cb1
dbce7ce
 
 
02b1411
dbce7ce
 
02b1411
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbce7ce
 
dd87cb1
 
 
 
dbce7ce
 
dd87cb1
02b1411
 
 
 
dd87cb1
02b1411
dbce7ce
 
b0b9691
02b1411
dbce7ce
 
f995bdc
 
dbce7ce
 
 
 
 
f995bdc
dd87cb1
 
b0b9691
 
 
dbce7ce
dd87cb1
dbce7ce
dd87cb1
dbce7ce
 
 
 
 
dd87cb1
baa3d07
dbce7ce
 
9817165
dd87cb1
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
import os
import gradio as gr
from openai import OpenAI

# API endpoints
API_HOSTS = {
    "Domestic": "https://api.chatanywhere.tech/v1",
    "Overseas": "https://api.chatanywhere.org/v1"
}

# Model info
MODELS_INFO = {
    "gpt-3.5-turbo": {"input_price": "0.0035", "output_price": "0.0105", "features": "Fast, affordable"},
    "gpt-4o": {"input_price": "0.0175", "output_price": "0.07", "features": "Cheaper & faster GPT-4O"},
    "gpt-4-turbo": {"input_price": "0.07", "output_price": "0.21", "features": "Multimodal, tool use"},
    "gpt-4o-ca": {"input_price": "0.01", "output_price": "0.04", "features": "CA variant, daily free limit"},
}

def create_client(host):
    """Create OpenAI API client with given host."""
    key = os.getenv("OPENAI_API_KEY")
    if not key:
        raise ValueError("Missing environment variable: OPENAI_API_KEY")
    return OpenAI(api_key=key, base_url=host)

def get_model_card(model_name):
    """Return markdown info for the selected model."""
    info = MODELS_INFO.get(model_name, {})
    if not info:
        return "Model info not available."
    return (
        f"**{model_name}**\n\n"
        f"Input price (/1K tokens): {info['input_price']}\n\n"
        f"Output price (/1K tokens): {info['output_price']}\n\n"
        f"Features: {info['features']}"
    )

def respond(user, history, host_choice, model_name, temperature, top_p, max_tokens, sys_prompt):
    """Main chat handler with streaming and error handling."""
    history = history or []

    if not user.strip():
        yield history + [("", "⚠️ Please enter a message.")]
        return

    try:
        client = create_client(API_HOSTS[host_choice])
    except Exception as e:
        yield history + [("", f"❌ {e}")]
        return

    messages = [{"role": "system", "content": sys_prompt or "You are a helpful assistant."}]
    for u, a in history:
        messages.append({"role": "user", "content": u})
        messages.append({"role": "assistant", "content": a})
    messages.append({"role": "user", "content": user})

    try:
        stream = client.chat.completions.create(
            model=model_name,
            messages=messages,
            temperature=temperature,
            top_p=top_p,
            max_tokens=max_tokens,
            stream=True
        )

        partial = ""
        history.append((user, partial))
        yield history  # initial blank assistant message

        for chunk in stream:
            try:
                if not chunk.choices:
                    continue
                choice = chunk.choices[0]
                if not hasattr(choice, "delta") or not choice.delta:
                    continue
                delta = getattr(choice.delta, "content", "") or ""
                if delta:
                    partial += delta
                    history[-1] = (user, partial)
                    yield history
            except Exception:
                continue  # skip malformed chunks

        if not partial.strip():
            history[-1] = (user, "⚠️ No response received from the model.")
            yield history

    except Exception as e:
        err = str(e)
        if "429" in err:
            out = (
                "🚫 Daily quota reached for this model.\n"
                "Please try again after 00:00 China time or switch model/host."
            )
        elif "401" in err or "invalid_api_key" in err.lower():
            out = "❌ Invalid or missing API key. Check your OPENAI_API_KEY."
        elif "timed out" in err.lower():
            out = "βŒ› Request timed out. Please try again."
        else:
            out = f"❌ API Error: {err}"
        history.append((user, out))
        yield history


with gr.Blocks(title="ChatAnywhere Realtime Chatbot", theme=gr.themes.Soft()) as demo:
    gr.Markdown("## πŸ’¬ ChatAnywhere Realtime Chatbot\nPowered by GPT-5 via ChatAnywhere API")
    with gr.Row():
        with gr.Column(scale=3):
            chat = gr.Chatbot(label="Conversation", height=500, show_copy_button=True, render_markdown=True)
            with gr.Row():
                msg = gr.Textbox(placeholder="Type your message...", lines=2, scale=4)
                send = gr.Button("Send", scale=1)
                clear = gr.Button("Clear", scale=1)
        with gr.Column(scale=1):
            host = gr.Radio(list(API_HOSTS.keys()), value="Domestic", label="API Host")
            model = gr.Dropdown(list(MODELS_INFO.keys()), value="gpt-3.5-turbo", label="Model")
            model_card = gr.Markdown(get_model_card("gpt-3.5-turbo"))
            temperature = gr.Slider(0.0, 1.5, value=0.7, step=0.05, label="Temperature")
            top_p = gr.Slider(0.05, 1.0, value=1.0, step=0.05, label="Top-p")
            max_tokens = gr.Slider(64, 4096, value=512, step=64, label="Max Tokens")
            sys_prompt = gr.Textbox(label="System Prompt (optional)", lines=2)

    model.change(lambda m: get_model_card(m), model, model_card)

    send.click(respond, [msg, chat, host, model, temperature, top_p, max_tokens, sys_prompt], chat)
    msg.submit(respond, [msg, chat, host, model, temperature, top_p, max_tokens, sys_prompt], chat)

    send.click(lambda _: "", msg, msg)
    msg.submit(lambda _: "", msg, msg)

    clear.click(lambda: [], None, chat)

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