File size: 3,972 Bytes
9108faf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import json
import os
import urllib.request

import gradio as gr
from openai import OpenAI

BASE_URL = "https://api.unorouter.com/v1"
PRICING_URL = "https://unorouter.com/api/models/pricing"
DEMO_KEY = os.environ.get("UNOROUTER_API_KEY", "")
DEFAULT_MODEL = "step-3.7-flash:free"

FALLBACK_MODELS = [
    "step-3.7-flash:free",
    "qwen3.5-397b-a17b:free",
    "mistral-medium-3.5:free",
    "gemma-4-31b-it:free",
    "llama-3.3-70b:free",
    "kimi-k2.6:free",
    "glm-4.5-flash:free",
    "deepseek-v3.2:free",
]


def load_free_models():
    try:
        req = urllib.request.Request(
            PRICING_URL, headers={"User-Agent": "unorouter-hf-space"}
        )
        with urllib.request.urlopen(req, timeout=15) as r:
            data = json.load(r)
        models = sorted(
            m["name"]
            for m in data.get("models", [])
            if m.get("isFree")
            and m.get("type") == "text"
            and "openai" in (m.get("endpointTypes") or [])
        )
        return models or FALLBACK_MODELS
    except Exception:
        return FALLBACK_MODELS


MODELS = load_free_models()
if DEFAULT_MODEL not in MODELS:
    DEFAULT_MODEL = MODELS[0]


def respond(message, history, model, api_key):
    key = (api_key or "").strip() or DEMO_KEY
    if not key:
        yield (
            "No API key configured. Get a free one at https://unorouter.com "
            "(Discord or GitHub signup) and paste it below."
        )
        return
    client = OpenAI(base_url=BASE_URL, api_key=key)
    messages = [
        {"role": t["role"], "content": t["content"]}
        for t in history
        if t.get("role") in ("user", "assistant") and isinstance(t.get("content"), str)
    ]
    messages.append({"role": "user", "content": message})
    try:
        stream = client.chat.completions.create(
            model=model, messages=messages, stream=True, max_tokens=2048
        )
        text = ""
        for chunk in stream:
            if chunk.choices and chunk.choices[0].delta.content:
                text += chunk.choices[0].delta.content
                yield text
        if not text:
            yield "(the model returned no visible text, try again or switch models)"
    except Exception as e:
        msg = str(e)
        if "busy" in msg or "rate limit" in msg.lower():
            yield (
                f"All free providers for {model} are at capacity right now. "
                "Free pools drain under load and recover automatically. "
                "Pick another model, or grab your own key at https://unorouter.com "
                "for full limits."
            )
        else:
            yield f"Error: {msg}"


with gr.Blocks(title="UnoRouter Free Chat") as demo:
    gr.Markdown(
        f"""# UnoRouter Free Chat
Chat with **{len(MODELS)} free models** through [UnoRouter](https://unorouter.com), one OpenAI-compatible key for 200+ AI models.
The same key also powers coding agents (Claude Code, Cline, OpenCode) and drops into SillyTavern or Janitor.
[Get your own free key](https://unorouter.com) | [Discord](https://discord.com/invite/eRAeFd9aqy) | [GitHub (fully open source)](https://github.com/unorouter)"""
    )
    model = gr.Dropdown(MODELS, value=DEFAULT_MODEL, label="Model (all free)")
    api_key = gr.Textbox(
        label="Your own UnoRouter API key (optional)",
        type="password",
        placeholder="sk-...  leave empty to use the shared demo key",
    )
    gr.ChatInterface(
        respond,
        type="messages",
        additional_inputs=[model, api_key],
        examples=[
            ["Explain what an LLM gateway does in two sentences.", DEFAULT_MODEL, ""],
            ["Write a haiku about free AI models.", DEFAULT_MODEL, ""],
        ],
    )
    gr.Markdown(
        "Shared demo key is limited to free models. If a model reports at capacity, "
        "its free pool is drained and will recover automatically."
    )

demo.queue(default_concurrency_limit=4).launch()