File size: 5,394 Bytes
abb6d23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
---
title: UnlimitedAI ToolCall Proxy
emoji: 🛠️
colorFrom: blue
colorTo: purple
sdk: docker
app_port: 7860
pinned: false
---

# UnlimitedAI ToolCall Proxy for Hugging Face Spaces

这个 Space 对外提供 OpenAI 兼容接口,并通过 Toolify 给不原生支持函数调用的上游模型模拟 `tools` / `tool_calls` 能力。

架构:

```text
OpenAI SDK / Cherry Studio / 你的应用
        ↓ /v1/chat/completions, tools
Toolify, port 7860,对外暴露
        ↓ 内部 OpenAI 兼容请求
UnlimitedAI upstream proxy, 127.0.0.1:9000

https://app.unlimitedai.chat/api/chat
```

## 上传到 Hugging Face

1. 新建 Hugging Face Space,SDK 选择 **Docker**2. 上传本项目全部文件。
3. 在 Space 的 **Settings → Secrets** 添加:

| Secret | 必填 | 说明 |
| --- | --- | --- |
| `UAI_COOKIES` | 是 | 浏览器 Network 里 `/api/chat` 请求的完整 Cookie,不要带 `cookie:` 前缀 |
| `UAI_DEVICE_ID` | 是 | Cookie 里的 `u_device_id` 值 |
| `PROXY_API_KEY` | 强烈建议 | 客户端调用这个代理时使用的 API Key |
| `DEFAULT_MODEL` | 否 | 默认 `chat-model-reasoning` |
| `ALLOWED_MODELS` | 否 | 逗号分隔,例如 `chat-model-reasoning,my-model` |
| `UAI_LOCALE` | 否 | 默认 `zh` |
| `TARGET_API` | 否 | 默认 `https://app.unlimitedai.chat/api/chat` |
| `CHAT_ID_STRATEGY` | 否 | 默认 `stable`,让同一段 OpenAI 历史消息复用同一个 UnlimitedAI `chatId` |
| `UAI_CHAT_ID` | 否 | 配合 `CHAT_ID_STRATEGY=fixed``cookie` 使用;也可手动指定固定会话 ID |
| `LOG_LEVEL` | 否 | 默认 `INFO`,可设 `DEBUG` |

> 你如果不设置 `PROXY_API_KEY`,默认 key 是 `sk-change-me`。公开 Space 一定要改。


## Chat ID 复用说明

这版修复了原始示例里每次请求都随机生成 `chatId` 的问题。默认:

```text
CHAT_ID_STRATEGY=stable
```

网关会根据 OpenAI 消息历史里的第一条 `user` 消息生成稳定 UUID,所以第一次工具调用请求和后续带 `role=tool` 结果的请求会复用同一个 UnlimitedAI 上游会话。

可选策略:

| 值 | 行为 |
| --- | --- |
| `stable` | 默认;根据第一条用户消息稳定生成 `chatId`,推荐用于 tool calling |
| `cookie` | 优先使用 Cookie 里的 `home_chat_id`,没有则回退到 `stable` |
| `fixed` / `static` | 固定使用 `UAI_CHAT_ID`,所有请求共享一个上游会话 |
| `random` | 老行为;每次请求生成新的上游会话,不推荐 tool calling |

如果客户端支持额外参数,也可以显式固定某个会话:

```python
resp = client.chat.completions.create(
    model="chat-model-reasoning",
    messages=[{"role": "user", "content": "查一下上海天气"}],
    tools=tools,
    tool_choice="auto",
    extra_body={"chat_id": "my-weather-session-001"},
)
```

显式 `chat_id` 会被转换成稳定 UUID,优先级高于 `CHAT_ID_STRATEGY`## OpenAI SDK 调用

把 base_url 改成你的 Space 地址:

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://YOUR-SPACE.hf.space/v1",
    api_key="你的 PROXY_API_KEY",
)

resp = client.chat.completions.create(
    model="chat-model-reasoning",
    messages=[{"role": "user", "content": "你好"}],
)

print(resp.choices[0].message.content)
```

## Tool Call 示例

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://YOUR-SPACE.hf.space/v1",
    api_key="你的 PROXY_API_KEY",
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取城市天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名"}
                },
                "required": ["city"]
            }
        }
    }
]

resp = client.chat.completions.create(
    model="chat-model-reasoning",
    messages=[{"role": "user", "content": "查一下上海天气"}],
    tools=tools,
    tool_choice="auto",
)

msg = resp.choices[0].message
print(msg.tool_calls)
```

如果返回了 `tool_calls`,你的客户端需要执行对应函数,然后把结果作为 `role='tool'` 消息继续发回模型。这和标准 OpenAI tool calling 流程一致。

## 流式 Tool Call

```python
stream = client.chat.completions.create(
    model="chat-model-reasoning",
    messages=[{"role": "user", "content": "查一下北京天气"}],
    tools=tools,
    tool_choice="auto",
    stream=True,
)

for chunk in stream:
    print(chunk)
```

## 本地 Docker 测试

```bash
docker build -t uai-toolcall-proxy .
docker run --rm -p 7860:7860 \
  -e UAI_COOKIES='你的完整 Cookie' \
  -e UAI_DEVICE_ID='你的 device id' \
  -e PROXY_API_KEY='test-key' \
  uai-toolcall-proxy
```

然后访问:

```text
http://localhost:7860/v1/models
```

调用时加 header:

```text
Authorization: Bearer test-key
```

## 注意

- 这里的 tool calling 是 Toolify 通过提示词和解析实现的“兼容层”,不是 UnlimitedAI 官方原生工具调用。
- 模型有概率不按格式输出;本项目已启用 Toolify 的函数调用解析失败重试,但仍不能保证 100%。
- 不要把 Cookie 提交到仓库;只放到 Hugging Face Secrets。
- 建议把 Space 设置为 Private。
- 本包包含 Toolify 源码,遵循其 GPL-3.0-or-later 许可证。