lightspeed commited on
Commit
b0b42d4
·
verified ·
1 Parent(s): 01672ce

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +803 -6
  2. app.py +253 -0
  3. requirements.txt +15 -0
README.md CHANGED
@@ -1,10 +1,807 @@
1
  ---
2
- title: Gcli2api
3
- emoji: 🔥
4
- colorFrom: pink
5
- colorTo: pink
6
- sdk: docker
 
7
  pinned: false
 
 
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: GCLI2API
3
+ emoji: 🚀
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ app_file: app.py
8
  pinned: false
9
+ license: other
10
+ short_description: Gemini API proxy with OpenAI compatibility
11
+ tags:
12
+ - gemini
13
+ - openai
14
+ - api-proxy
15
+ - fastapi
16
+ - google-ai
17
  ---
18
 
19
+ # GeminiCLI to API
20
+
21
+ **将 GeminiCLI 转换为 OpenAI 和 GEMINI API 接口**
22
+
23
+ [English](docs/README_EN.md) | 中文
24
+
25
+ ## 🚀 快速部署
26
+
27
+ [![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/2QLQC2?referralCode=su-kaka)
28
+ ---
29
+
30
+ ## ⚠️ 许可证声明
31
+
32
+ **本项目采用 Cooperative Non-Commercial License (CNC-1.0)**
33
+
34
+ 这是一个反商业化的严格开源协议,详情请查看 [LICENSE](LICENSE) 文件。
35
+
36
+ ### ✅ 允许的用途:
37
+ - 个人学习、研究、教育用途
38
+ - 非营利组织使用
39
+ - 开源项目集成(需遵循相同协议)
40
+ - 学术研究和论文发表
41
+
42
+ ### ❌ 禁止的用途:
43
+ - 任何形式的商业使用
44
+ - 年收入超过100万美元的企业使用
45
+ - 风投支持或公开交易的公司使用
46
+ - 提供付费服务或产品
47
+ - 商业竞争用途
48
+
49
+ ---
50
+
51
+ ## 控制面板演示网址:https://gcli2api-9xbf.onrender.com 密码:pwd
52
+
53
+ ## 核心功能
54
+
55
+ ### 🔄 API 端点和格式支持
56
+
57
+ **多端点双格式支持**
58
+ - **OpenAI 兼容端点**:`/v1/chat/completions` 和 `/v1/models`
59
+ - 支持标准 OpenAI 格式(messages 结构)
60
+ - 支持 Gemini 原生格式(contents 结构)
61
+ - 自动格式检测和转换,无需手动切换
62
+ - 支持多模态输入(文本 + 图像)
63
+ - **Gemini 原生端点**:`/v1/models/{model}:generateContent` 和 `streamGenerateContent`
64
+ - 支持完整的 Gemini 原生 API 规范
65
+ - 多种认证方式:Bearer Token、x-goog-api-key 头部、URL 参数 key
66
+
67
+ ### 🔐 认证和安全管理
68
+
69
+ **灵活的密码管理**
70
+ - **分离密码支持**:API 密码(聊天端点)和控制面板密码可独立设置
71
+ - **多种认证方式**:支持 Authorization Bearer、x-goog-api-key 头部、URL 参数等
72
+ - **JWT Token 认证**:控制面板支持 JWT 令牌认证
73
+ - **用户邮箱获取**:自动获取和显示 Google 账户邮箱地址
74
+
75
+ ### 📊 智能凭证管理系统
76
+
77
+ **高级凭证管理**
78
+ - 多个 Google OAuth 凭证自动轮换
79
+ - 通过冗余认证增强稳定性
80
+ - 负载均衡与并发请求支持
81
+ - 自动故障检测和凭证禁用
82
+ - 凭证使用统计和配额管理
83
+ - 支持手动启用/禁用凭证文件
84
+ - 批量凭证文件操作(启用、禁用、删除)
85
+
86
+ **凭证状态监控**
87
+ - 实时凭证健康检查
88
+ - 错误码追踪(429、403、500 等)
89
+ - 自动封禁机制(可配置)
90
+ - 凭证轮换策略(基于调用次数)
91
+ - 使用统计和配额监控
92
+
93
+ ### 🌊 流式传输和响应处理
94
+
95
+ **多种流式支持**
96
+ - 真正的实时流式响应
97
+ - 假流式模式(用于兼容性)
98
+ - 流式抗截断功能(防止回答被截断)
99
+ - 异步任务管理和超时处理
100
+
101
+ **响应优化**
102
+ - 思维链(Thinking)内容分离
103
+ - 推理过程(reasoning_content)处理
104
+ - 多轮对话上下文管理
105
+ - 兼容性模式(将 system 消息转换为 user 消息)
106
+
107
+ ### 🎛️ Web 管理控制台
108
+
109
+ **全功能 Web 界面**
110
+ - OAuth 认证流程管理
111
+ - 凭证文件上传、下载、管理
112
+ - 实时日志查看(WebSocket)
113
+ - 系统配置管理
114
+ - 使用统计和监控面板
115
+ - 移动端适配界面
116
+
117
+ **批量操作支持**
118
+ - ZIP 文件批量上传凭证
119
+ - 批量启用/禁用/删除凭证
120
+ - 批量获取用户邮箱
121
+ - 批量配置管理
122
+
123
+ ### 📈 使用统计和监控
124
+
125
+ **详细使用统计**
126
+ - 按凭证文件统计调用次数
127
+ - Gemini 2.5 Pro 模型专项统计
128
+ - 每日配额管理(UTC+7 重置)
129
+ - 聚合统计和分析
130
+ - 自定义每日限制配置
131
+
132
+ **实时监控**
133
+ - WebSocket 实时日志流
134
+ - 系统状态监控
135
+ - 凭证健康状态
136
+ - API 调用成功率统计
137
+
138
+ ### 🔧 高级配置和自定义
139
+
140
+ **网络和代理配置**
141
+ - HTTP/HTTPS 代理支持
142
+ - 代理端点配置(OAuth、Google APIs、元数据服务)
143
+ - 超时和重试配置
144
+ - 网络错误处理和恢复
145
+
146
+ **性能和稳定性配置**
147
+ - 429 错误自动重试(可配置间隔和次数)
148
+ - 抗截断最大重试次数
149
+ - 凭证轮换策略
150
+ - 并发请求管理
151
+
152
+ **日志和调试**
153
+ - 多级日志系统(DEBUG、INFO、WARNING、ERROR)
154
+ - 日志文件管理
155
+ - 实时日志流
156
+ - 日志下载和清空
157
+
158
+ ### 🔄 环境变量和配置管理
159
+
160
+ **灵活的配置方式**
161
+ - TOML 配置文件支持
162
+ - 环境变量配置
163
+ - 热配置更新(部分配置项)
164
+ - 配置锁定(环境变量优先级)
165
+
166
+ **环境变量凭证支持**
167
+ - `GCLI_CREDS_*` 格式环境变量导入
168
+ - 自动加载环境变量凭证
169
+ - Base64 编码凭证支持
170
+ - Docker 容器友好
171
+
172
+ ## 支持的模型
173
+
174
+ 所有模型均具备 1M 上下文窗口容量。每个凭证文件提供 1000 次请求额度。
175
+
176
+ ### �� 基础模型
177
+ - `gemini-2.5-pro`
178
+ - `gemini-2.5-pro-preview-06-05`
179
+ - `gemini-2.5-pro-preview-05-06`
180
+
181
+ ### 🧠 思维模型(Thinking Models)
182
+ - `gemini-2.5-pro-maxthinking`:最大思考预算模式
183
+ - `gemini-2.5-pro-nothinking`:无思考模式
184
+ - 支持自定义思考预算配置
185
+ - 自动分离思维内容和最终回答
186
+
187
+ ### 🔍 搜索增强模型
188
+ - `gemini-2.5-pro-search`:集成搜索功能的模型
189
+
190
+ ### 🌊 特殊功能变体
191
+ - **假流式模式**:在任何模型名称后添加 `-假流式` 后缀
192
+ - 例:`gemini-2.5-pro-假流式`
193
+ - 用于需要流式响应但服务端不支持真流式的场景
194
+ - **流式抗截断模式**:在模型名称前添加 `流式抗截断/` 前缀
195
+ - 例:`流式抗截断/gemini-2.5-pro`
196
+ - 自动检测响应截断并重试,确保完整回答
197
+
198
+ ### 🔧 模型功能自动检测
199
+ - 系统自动识别模型名称中的功能标识
200
+ - 透明地处理功能模式转换
201
+ - 支持功能组合使用
202
+
203
+ ---
204
+
205
+ ## 安装指南
206
+
207
+ ### Termux 环境
208
+
209
+ **初始安装**
210
+ ```bash
211
+ curl -o termux-install.sh "https://raw.githubusercontent.com/su-kaka/gcli2api/refs/heads/master/termux-install.sh" && chmod +x termux-install.sh && ./termux-install.sh
212
+ ```
213
+
214
+ **重启服务**
215
+ ```bash
216
+ cd gcli2api
217
+ bash termux-start.sh
218
+ ```
219
+
220
+ ### Windows 环境
221
+
222
+ **初始安装**
223
+ ```powershell
224
+ iex (iwr "https://raw.githubusercontent.com/su-kaka/gcli2api/refs/heads/master/install.ps1" -UseBasicParsing).Content
225
+ ```
226
+
227
+ **重启服务**
228
+ 双击执行 `start.bat`
229
+
230
+ ### Linux 环境
231
+
232
+ **初始安装**
233
+ ```bash
234
+ curl -o install.sh "https://raw.githubusercontent.com/su-kaka/gcli2api/refs/heads/master/install.sh" && chmod +x install.sh && ./install.sh
235
+ ```
236
+
237
+ **重启服务**
238
+ ```bash
239
+ cd gcli2api
240
+ bash start.sh
241
+ ```
242
+
243
+ ### Docker 环境
244
+
245
+ **Docker 运行命令**
246
+ ```bash
247
+ # 使用通用密码
248
+ docker run -d --name gcli2api --network host -e PASSWORD=pwd -e PORT=7861 -v $(pwd)/data/creds:/app/creds ghcr.io/su-kaka/gcli2api:latest
249
+
250
+ # 使用分离密码
251
+ docker run -d --name gcli2api --network host -e API_PASSWORD=api_pwd -e PANEL_PASSWORD=panel_pwd -e PORT=7861 -v $(pwd)/data/creds:/app/creds ghcr.io/su-kaka/gcli2api:latest
252
+ ```
253
+
254
+ **Docker Compose 运行命令**
255
+ 1. 将以下内容保存为 `docker-compose.yml` 文件:
256
+ ```yaml
257
+ version: '3.8'
258
+
259
+ services:
260
+ gcli2api:
261
+ image: ghcr.io/su-kaka/gcli2api:latest
262
+ container_name: gcli2api
263
+ restart: unless-stopped
264
+ network_mode: host
265
+ environment:
266
+ # 使用通用密码(推荐用于简单部署)
267
+ - PASSWORD=pwd
268
+ - PORT=7861
269
+ # 或使用分离密码(推荐用于生产环境)
270
+ # - API_PASSWORD=your_api_password
271
+ # - PANEL_PASSWORD=your_panel_password
272
+ volumes:
273
+ - ./data/creds:/app/creds
274
+ healthcheck:
275
+ test: ["CMD-SHELL", "python -c \"import sys, urllib.request, os; port = os.environ.get('PORT', '7861'); req = urllib.request.Request(f'http://localhost:{port}/v1/models', headers={'Authorization': 'Bearer ' + os.environ.get('PASSWORD', 'pwd')}); sys.exit(0 if urllib.request.urlopen(req, timeout=5).getcode() == 200 else 1)\""]
276
+ interval: 30s
277
+ timeout: 10s
278
+ retries: 3
279
+ start_period: 40s
280
+ ```
281
+ 2. 启动服务:
282
+ ```bash
283
+ docker-compose up -d
284
+ ```
285
+
286
+ ---
287
+
288
+ ## ⚠️ 注意事项
289
+
290
+ - 当前 OAuth 验证流程**仅支持本地主机(localhost)访问**,即须通过 `http://127.0.0.1:7861/auth` 完成认证(默认端口 7861,可通过 PORT 环境变量修改)。
291
+ - **如需在云服务器或其他远程环境部署,请先在本地运行服务并完成 OAuth 验证,获得生成的 json 凭证文件(位于 `./geminicli/creds` 目录)后,再在auth面板将该文件上传即可。**
292
+ - **请严格遵守使用限制,仅用于个人学习和非商业用途**
293
+
294
+ ---
295
+
296
+ ## 配置说明
297
+
298
+ 1. 访问 `http://127.0.0.1:7861/auth` (默认端口,可通过 PORT 环境变量修改)
299
+ 2. 完成 OAuth 认证流程(默认密码:`pwd`,可通过环境变量修改)
300
+ 3. 配置客户端:
301
+
302
+ **OpenAI 兼容客户端:**
303
+ - **端点地址**:`http://127.0.0.1:7861/v1`
304
+ - **API 密钥**:`pwd`(默认值,可通过 API_PASSWORD 或 PASSWORD 环境变量修改)
305
+
306
+ **Gemini 原生客户端:**
307
+ - **端点地址**:`http://127.0.0.1:7861`
308
+ - **认证方式**:
309
+ - `Authorization: Bearer your_api_password`
310
+ - `x-goog-api-key: your_api_password`
311
+ - URL 参数:`?key=your_api_password`
312
+
313
+ ## 💾 分布式存储模式
314
+
315
+ ### 🌟 存储后端优先级
316
+
317
+ gcli2api 支持多种存储后端,按优先级自动选择:**Redis > Postgres > MongoDB > 本地文件**
318
+
319
+ ### ⚡ Redis 分布式存储模式
320
+
321
+ ### ⚙️ 启用 Redis 模式
322
+
323
+ **步骤 1: 配置 Redis 连接**
324
+ ```bash
325
+ # 本地 Redis
326
+ export REDIS_URI="redis://localhost:6379"
327
+
328
+ # 带密码的 Redis
329
+ export REDIS_URI="redis://:password@localhost:6379"
330
+
331
+ # SSL 连接(推荐生产环境)
332
+ export REDIS_URI="rediss://default:password@host:6380"
333
+
334
+ # Upstash Redis(免费云服务)
335
+ export REDIS_URI="rediss://default:token@your-host.upstash.io:6379"
336
+
337
+ # 可选:自定义数据库索引(默认: 0)
338
+ export REDIS_DATABASE="1"
339
+ ```
340
+
341
+ **步骤 2: 启动应用**
342
+ ```bash
343
+ # 应用会自动检测 Redis 配置并优先使用 Redis 存储
344
+ python web.py
345
+ ```
346
+
347
+ ### 🐘 Postgres 分布式存储模式
348
+
349
+ 如果未配置 Redis,或者你希望使用关系型数据库作为主要存储方案,gcli2api 也支持 Postgres(位于 Redis 之后,优先于 MongoDB)。
350
+
351
+ ⚙️ 启用 Postgres 模式
352
+
353
+ 步骤 1: 配置 Postgres 连接
354
+ ```bash
355
+ # 使用标准 DSN(示例)
356
+ export POSTGRES_DSN="postgresql://user:password@localhost:5432/gcli2api"
357
+
358
+ # 也可以使用 socket 或其他 DSN 格式,取决于你的部署方式
359
+ ```
360
+
361
+ 步骤 2: 启动应用
362
+ ```bash
363
+ # 应用会自动检测 POSTGRES_DSN 并在 Redis 未启用时优先使用 Postgres 存储
364
+ python web.py
365
+ ```
366
+
367
+ ### 🍃 MongoDB 分布式存储模式
368
+
369
+ ### 🌟 备选存储方案
370
+
371
+ 如果未配置 Redis,gcli2api 将尝试使用 **MongoDB 存储模式**,
372
+
373
+ ### ⚙️ 启用 MongoDB 模式
374
+
375
+ **步骤 1: 配置 MongoDB 连接**
376
+ ```bash
377
+ # 本地 MongoDB
378
+ export MONGODB_URI="mongodb://localhost:27017"
379
+
380
+ # MongoDB Atlas 云服务
381
+ export MONGODB_URI="mongodb+srv://username:password@cluster.mongodb.net"
382
+
383
+ # 带认证的 MongoDB
384
+ export MONGODB_URI="mongodb://admin:password@localhost:27017/admin"
385
+
386
+ # 可选:自定义数据库名称(默认: gcli2api)
387
+ export MONGODB_DATABASE="my_gcli_db"
388
+ ```
389
+
390
+ **步骤 2: 启动应用**
391
+ ```bash
392
+ # 应用会自动检测 MongoDB 配置并使用 MongoDB 存储
393
+ python web.py
394
+ ```
395
+
396
+ **Docker 环境使用 MongoDB**
397
+ ```bash
398
+ # 单机 MongoDB 部署
399
+ docker run -d --name gcli2api \
400
+ -e MONGODB_URI="mongodb://mongodb:27017" \
401
+ -e API_PASSWORD=your_password \
402
+ --network your_network \
403
+ ghcr.io/su-kaka/gcli2api:latest
404
+
405
+ # 使用 MongoDB Atlas
406
+ docker run -d --name gcli2api \
407
+ -e MONGODB_URI="mongodb+srv://user:pass@cluster.mongodb.net/gcli2api" \
408
+ -e API_PASSWORD=your_password \
409
+ -p 7861:7861 \
410
+ ghcr.io/su-kaka/gcli2api:latest
411
+ ```
412
+
413
+ **Docker Compose 示例**
414
+ ```yaml
415
+ version: '3.8'
416
+
417
+ services:
418
+ mongodb:
419
+ image: mongo:7
420
+ container_name: gcli2api-mongodb
421
+ restart: unless-stopped
422
+ environment:
423
+ MONGO_INITDB_ROOT_USERNAME: admin
424
+ MONGO_INITDB_ROOT_PASSWORD: password123
425
+ volumes:
426
+ - mongodb_data:/data/db
427
+ ports:
428
+ - "27017:27017"
429
+
430
+ gcli2api:
431
+ image: ghcr.io/su-kaka/gcli2api:latest
432
+ container_name: gcli2api
433
+ restart: unless-stopped
434
+ depends_on:
435
+ - mongodb
436
+ environment:
437
+ - MONGODB_URI=mongodb://admin:password123@mongodb:27017/admin
438
+ - MONGODB_DATABASE=gcli2api
439
+ - API_PASSWORD=your_api_password
440
+ - PORT=7861
441
+ ports:
442
+ - "7861:7861"
443
+
444
+ volumes:
445
+ mongodb_data:
446
+ ```
447
+
448
+
449
+ ### 🔧 高级配置
450
+
451
+ **MongoDB 连接优化**
452
+ ```bash
453
+ # 连接池和超时配置
454
+ export MONGODB_URI="mongodb://localhost:27017?maxPoolSize=10&serverSelectionTimeoutMS=5000"
455
+
456
+ # 副本集配置
457
+ export MONGODB_URI="mongodb://host1:27017,host2:27017,host3:27017/gcli2api?replicaSet=myReplicaSet"
458
+
459
+ # 读写分离配置
460
+ export MONGODB_URI="mongodb://localhost:27017/gcli2api?readPreference=secondaryPreferred"
461
+ ```
462
+
463
+ ## 🏗️ 技术架构
464
+
465
+ ### 核心模块说明
466
+
467
+ **认证和凭证管理** (`src/auth.py`, `src/credential_manager.py`)
468
+ - OAuth 2.0 认证流程管理
469
+ - 多凭证文件状态管理和轮换
470
+ - 自动故障检测和恢复
471
+ - JWT 令牌生成和验证
472
+
473
+ **API 路由和转换** (`src/openai_router.py`, `src/gemini_router.py`, `src/openai_transfer.py`)
474
+ - OpenAI 和 Gemini 格式双向转换
475
+ - 多模态输入处理(文本+图像)
476
+ - 思维链内容分离和处理
477
+ - 流式响应管理
478
+
479
+ **网络和代理** (`src/httpx_client.py`, `src/google_chat_api.py`)
480
+ - 统一 HTTP 客户端管理
481
+ - 代理配置和热更新支持
482
+ - 超时和重试策略
483
+ - 异步请求池管理
484
+
485
+ **状态管理** (`src/state_manager.py`, `src/usage_stats.py`)
486
+ - 原子化状态操作
487
+ - 使用统计和配额管理
488
+ - 文件锁和并发安全
489
+ - 数据持久化(TOML 格式)
490
+
491
+ **任务管理** (`src/task_manager.py`)
492
+ - 全局异步任务生命周期管理
493
+ - 资源清理和内存管理
494
+ - 优雅关闭和异常处理
495
+
496
+ **Web 控制台** (`src/web_routes.py`)
497
+ - RESTful API 端点
498
+ - WebSocket 实时通信
499
+ - 移动端适配检测
500
+ - 批量操作支持
501
+
502
+ ### 高级特性实现
503
+
504
+ **流式抗截断机制** (`src/anti_truncation.py`)
505
+ - 检测响应截断模式
506
+ - 自动重试和状态恢复
507
+ - 上下文连接管理
508
+
509
+ **格式检测和转换** (`src/format_detector.py`)
510
+ - 自动检测请求格式(OpenAI vs Gemini)
511
+ - 无缝格式转换
512
+ - 参数映射和验证
513
+
514
+ **用户代理模拟** (`src/utils.py`)
515
+ - GeminiCLI 格式用户代理生成
516
+ - 平台检测和客户端元数据
517
+ - API 兼容性保证
518
+
519
+ ### 环境变量配置
520
+
521
+ **基础配置**
522
+ - `PORT`: 服务端口(默认:7861)
523
+ - `HOST`: 服务器监听地址(默认:0.0.0.0)
524
+
525
+ **密码配置**
526
+ - `API_PASSWORD`: 聊天 API 访问密码(默认:继承 PASSWORD 或 pwd)
527
+ - `PANEL_PASSWORD`: 控制面板访问密码(默认:继承 PASSWORD 或 pwd)
528
+ - `PASSWORD`: 通用密码,设置后覆盖上述两个(默认:pwd)
529
+
530
+ **性能和稳定性配置**
531
+ - `CALLS_PER_ROTATION`: 每个凭证轮换前的调用次数(默认:10)
532
+ - `RETRY_429_ENABLED`: 启用 429 错误自动重试(默认:true)
533
+ - `RETRY_429_MAX_RETRIES`: 429 错误最大重试次数(默认:3)
534
+ - `RETRY_429_INTERVAL`: 429 错误重试间隔,秒(默认:1.0)
535
+ - `ANTI_TRUNCATION_MAX_ATTEMPTS`: 抗截断最大重试次数(默认:3)
536
+
537
+ **网络和代理配置**
538
+ - `PROXY`: HTTP/HTTPS 代理地址(格式:`http://host:port`)
539
+ - `OAUTH_PROXY_URL`: OAuth 认证代理端点
540
+ - `GOOGLEAPIS_PROXY_URL`: Google APIs 代理端点
541
+ - `METADATA_SERVICE_URL`: 元数据服务代理端点
542
+
543
+ **自动化配置**
544
+ - `AUTO_BAN`: 启用凭证自动封禁(默认:true)
545
+ - `AUTO_LOAD_ENV_CREDS`: 启动时自动加载环境变量凭证(默认:false)
546
+
547
+ **兼容性配置**
548
+ - `COMPATIBILITY_MODE`: 启用兼容性模式,将 system 消息转为 user 消息(默认:false)
549
+
550
+ **日志配置**
551
+ - `LOG_LEVEL`: 日志级别(DEBUG/INFO/WARNING/ERROR,默认:INFO)
552
+ - `LOG_FILE`: 日志文件路径(默认:gcli2api.log)
553
+
554
+ **存储配置(按优先级)**
555
+
556
+ **Redis 配置(最高优先级)**
557
+ - `REDIS_URI`: Redis 连接字符串(设置后启用 Redis 模式)
558
+ - 本地:`redis://localhost:6379`
559
+ - 带密码:`redis://:password@host:6379`
560
+ - SSL:`rediss://default:password@host:6380`
561
+ - `REDIS_DATABASE`: Redis 数据库索引(0-15,默认:0)
562
+
563
+ **MongoDB 配置(第二优先级)**
564
+ - `MONGODB_URI`: MongoDB 连接字符串(设置后启用 MongoDB 模式)
565
+ - `MONGODB_DATABASE`: MongoDB 数据库名称(默认:gcli2api)
566
+
567
+ **凭证配置**
568
+
569
+ 支持使用 `GCLI_CREDS_*` 环境变量导入多个凭证:
570
+
571
+ #### 凭证环境变量使用示例
572
+
573
+ **方式 1:编号格式**
574
+ ```bash
575
+ export GCLI_CREDS_1='{"client_id":"your-client-id","client_secret":"your-secret","refresh_token":"your-token","token_uri":"https://oauth2.googleapis.com/token","project_id":"your-project"}'
576
+ export GCLI_CREDS_2='{"client_id":"...","project_id":"..."}'
577
+ ```
578
+
579
+ **方式 2:项目名格式**
580
+ ```bash
581
+ export GCLI_CREDS_myproject='{"client_id":"...","project_id":"myproject",...}'
582
+ export GCLI_CREDS_project2='{"client_id":"...","project_id":"project2",...}'
583
+ ```
584
+
585
+ **启用自动加载**
586
+ ```bash
587
+ export AUTO_LOAD_ENV_CREDS=true # 程序启动时自动导入环境变量凭证
588
+ ```
589
+
590
+ **Docker 使用示例**
591
+ ```bash
592
+ # 使用通用密码
593
+ docker run -d --name gcli2api \
594
+ -e PASSWORD=mypassword \
595
+ -e PORT=8080 \
596
+ -e GOOGLE_CREDENTIALS="$(cat credential.json | base64 -w 0)" \
597
+ ghcr.io/su-kaka/gcli2api:latest
598
+
599
+ # 使用分离密码
600
+ docker run -d --name gcli2api \
601
+ -e API_PASSWORD=my_api_password \
602
+ -e PANEL_PASSWORD=my_panel_password \
603
+ -e PORT=8080 \
604
+ -e GOOGLE_CREDENTIALS="$(cat credential.json | base64 -w 0)" \
605
+ ghcr.io/su-kaka/gcli2api:latest
606
+ ```
607
+
608
+ 注意:当设置了凭证环境变量时,系统将优先使用环境变量中的凭证,忽略 `creds` 目录中的文件。
609
+
610
+ ### API 使用方式
611
+
612
+ 本服务支持两套完整的 API 端点:
613
+
614
+ #### 1. OpenAI 兼容端点
615
+
616
+ **端点:** `/v1/chat/completions`
617
+ **认证:** `Authorization: Bearer your_api_password`
618
+
619
+ 支持两种请求格式,会自动检测并处理:
620
+
621
+ **OpenAI 格式:**
622
+ ```json
623
+ {
624
+ "model": "gemini-2.5-pro",
625
+ "messages": [
626
+ {"role": "system", "content": "You are a helpful assistant"},
627
+ {"role": "user", "content": "Hello"}
628
+ ],
629
+ "temperature": 0.7,
630
+ "stream": true
631
+ }
632
+ ```
633
+
634
+ **Gemini 原生格式:**
635
+ ```json
636
+ {
637
+ "model": "gemini-2.5-pro",
638
+ "contents": [
639
+ {"role": "user", "parts": [{"text": "Hello"}]}
640
+ ],
641
+ "systemInstruction": {"parts": [{"text": "You are a helpful assistant"}]},
642
+ "generationConfig": {
643
+ "temperature": 0.7
644
+ }
645
+ }
646
+ ```
647
+
648
+ #### 2. Gemini 原生端点
649
+
650
+ **非流式端点:** `/v1/models/{model}:generateContent`
651
+ **流式端点:** `/v1/models/{model}:streamGenerateContent`
652
+ **模型列表:** `/v1/models`
653
+
654
+ **认证方式(任选一种):**
655
+ - `Authorization: Bearer your_api_password`
656
+ - `x-goog-api-key: your_api_password`
657
+ - URL 参数:`?key=your_api_password`
658
+
659
+ **请求示例:**
660
+ ```bash
661
+ # 使用 x-goog-api-key 头部
662
+ curl -X POST "http://127.0.0.1:7861/v1/models/gemini-2.5-pro:generateContent" \
663
+ -H "x-goog-api-key: your_api_password" \
664
+ -H "Content-Type: application/json" \
665
+ -d '{
666
+ "contents": [
667
+ {"role": "user", "parts": [{"text": "Hello"}]}
668
+ ]
669
+ }'
670
+
671
+ # 使用 URL 参数
672
+ curl -X POST "http://127.0.0.1:7861/v1/models/gemini-2.5-pro:streamGenerateContent?key=your_api_password" \
673
+ -H "Content-Type: application/json" \
674
+ -d '{
675
+ "contents": [
676
+ {"role": "user", "parts": [{"text": "Hello"}]}
677
+ ]
678
+ }'
679
+ ```
680
+
681
+ **说明:**
682
+ - OpenAI 端点返回 OpenAI 兼容格式
683
+ - Gemini 端点返��� Gemini 原生格式
684
+ - 两种端点使用相同的 API 密码
685
+
686
+ ## 📋 完整 API 参考
687
+
688
+ ### Web 控制台 API
689
+
690
+ **认证端点**
691
+ - `POST /auth/login` - 用户登录
692
+ - `POST /auth/start` - 开始 OAuth 认证
693
+ - `POST /auth/callback` - 处理 OAuth 回调
694
+ - `GET /auth/status/{project_id}` - 检查认证状态
695
+
696
+ **凭证管理端点**
697
+ - `GET /creds/status` - 获取所有凭证状态
698
+ - `POST /creds/action` - 单个凭证操作(启用/禁用/删除)
699
+ - `POST /creds/batch-action` - 批量凭证操作
700
+ - `POST /auth/upload` - 批量上传凭证文件(支持 ZIP)
701
+ - `GET /creds/download/{filename}` - 下载凭证文件
702
+ - `GET /creds/download-all` - 打包下载所有凭证
703
+ - `POST /creds/fetch-email/{filename}` - 获取用户邮箱
704
+ - `POST /creds/refresh-all-emails` - 批量刷新用户邮箱
705
+
706
+ **配置管理端点**
707
+ - `GET /config/get` - 获取当前配置
708
+ - `POST /config/save` - 保存配置
709
+
710
+ **环境变量凭证端点**
711
+ - `POST /auth/load-env-creds` - 加载环境变量凭证
712
+ - `DELETE /auth/env-creds` - 清除环境变量凭证
713
+ - `GET /auth/env-creds-status` - 获取环境变量凭证状态
714
+
715
+ **日志管理端点**
716
+ - `POST /auth/logs/clear` - 清空日志
717
+ - `GET /auth/logs/download` - 下载日志文件
718
+ - `WebSocket /auth/logs/stream` - 实时日志流
719
+
720
+ **使用统计端点**
721
+ - `GET /usage/stats` - 获取使用统计
722
+ - `GET /usage/aggregated` - 获取聚合统计
723
+ - `POST /usage/update-limits` - 更新使用限制
724
+ - `POST /usage/reset` - 重置使用统计
725
+
726
+ ### 聊天 API 功能特性
727
+
728
+ **多模态支持**
729
+ ```json
730
+ {
731
+ "model": "gemini-2.5-pro",
732
+ "messages": [
733
+ {
734
+ "role": "user",
735
+ "content": [
736
+ {"type": "text", "text": "描述这张图片"},
737
+ {
738
+ "type": "image_url",
739
+ "image_url": {
740
+ "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABA..."
741
+ }
742
+ }
743
+ ]
744
+ }
745
+ ]
746
+ }
747
+ ```
748
+
749
+ **思维模式支持**
750
+ ```json
751
+ {
752
+ "model": "gemini-2.5-pro-maxthinking",
753
+ "messages": [
754
+ {"role": "user", "content": "复杂数学问题"}
755
+ ]
756
+ }
757
+ ```
758
+
759
+ 响应将包含分离的思维内容:
760
+ ```json
761
+ {
762
+ "choices": [{
763
+ "message": {
764
+ "role": "assistant",
765
+ "content": "最终答案",
766
+ "reasoning_content": "详细的思考过程..."
767
+ }
768
+ }]
769
+ }
770
+ ```
771
+
772
+ **流式抗截断使用**
773
+ ```json
774
+ {
775
+ "model": "流式抗截断/gemini-2.5-pro",
776
+ "messages": [
777
+ {"role": "user", "content": "写一篇长文章"}
778
+ ],
779
+ "stream": true
780
+ }
781
+ ```
782
+
783
+ **兼容性模式**
784
+ ```bash
785
+ # 启用兼容性模式
786
+ export COMPATIBILITY_MODE=true
787
+ ```
788
+ 此模式下,所有 `system` 消息会转换为 `user` 消息,提高与某些客户端的兼容性。
789
+
790
+ ---
791
+
792
+ ## 支持项目
793
+
794
+ 如果这个项目对您有帮助,欢迎支持项目的持续发展!
795
+
796
+ 详细捐赠信息请查看:[📖 捐赠说明文档](docs/DONATE.md)
797
+
798
+ ---
799
+
800
+ ## 许可证与免责声明
801
+
802
+ 本项目仅供学习和研究用途。使用本项目表示您同意:
803
+ - 不将本项目用于任何商业用途
804
+ - 承担使用本项目的所有风险和责任
805
+ - 遵守相关的服务条款和法律法规
806
+
807
+ 项目作者对因使用本项目而产生的任何直接或间接损失不承担责任。
app.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Hugging Face Spaces Entry Point for GCLI2API Lightspeed
4
+ HF Spaces 部署入口文件
5
+
6
+ This file serves as the entry point for Hugging Face Spaces deployment.
7
+ It imports and runs the main application from web.py with appropriate configurations.
8
+ """
9
+
10
+ import os
11
+ import sys
12
+ import asyncio
13
+ import signal
14
+ import logging
15
+ from pathlib import Path
16
+
17
+ # 确保项目根目录在 Python 路径中
18
+ project_root = Path(__file__).parent
19
+ if str(project_root) not in sys.path:
20
+ sys.path.insert(0, str(project_root))
21
+
22
+ # 设置基础环境变量(HF Spaces 特定)
23
+ os.environ.setdefault('HOST', '0.0.0.0')
24
+ os.environ.setdefault('PORT', '7860') # HF Spaces 默认端口
25
+
26
+ # 如果没有设置密码,使用默认值(HF Spaces 用户需要在环境变量中设置)
27
+ if not os.environ.get('PASSWORD') and not os.environ.get('API_PASSWORD'):
28
+ print("⚠️ 警告: 未设置密码环境变量!")
29
+ print(" 请在 HF Spaces Settings 中设置 API_PASSWORD 和 PANEL_PASSWORD")
30
+ print(" 或者设置通用 PASSWORD 环境变量")
31
+
32
+ # 为了演示目的,设置默认密码
33
+ os.environ.setdefault('PASSWORD', 'hf_demo_password_please_change')
34
+
35
+ # 设置日志级别
36
+ os.environ.setdefault('LOG_LEVEL', 'INFO')
37
+
38
+ # HF Spaces 优化配置
39
+ os.environ.setdefault('CALLS_PER_ROTATION', '5') # 较少的轮换次数适合 HF Spaces
40
+ os.environ.setdefault('RETRY_429_ENABLED', 'true')
41
+ os.environ.setdefault('AUTO_BAN', 'true')
42
+ os.environ.setdefault('COMPATIBILITY_MODE', 'true') # 增强兼容性
43
+
44
+ # 确保 creds 目录存在
45
+ creds_dir = project_root / "creds"
46
+ creds_dir.mkdir(exist_ok=True)
47
+
48
+ def signal_handler(signum, frame):
49
+ """处理系统信号,优雅关闭"""
50
+ print(f"\n收到信号 {signum},正在优雅关闭服务...")
51
+ sys.exit(0)
52
+
53
+ def setup_logging():
54
+ """设置日志配置"""
55
+ logging.basicConfig(
56
+ level=getattr(logging, os.environ.get('LOG_LEVEL', 'INFO')),
57
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
58
+ datefmt='%Y-%m-%d %H:%M:%S'
59
+ )
60
+
61
+ def print_startup_info():
62
+ """打印启动信息"""
63
+ port = os.environ.get('PORT', '7860')
64
+ host = os.environ.get('HOST', '0.0.0.0')
65
+
66
+ print("=" * 80)
67
+ print("🚀 GCLI2API Lightspeed - Hugging Face Spaces Edition")
68
+ print("=" * 80)
69
+ print(f"🌐 服务地址: http://{host}:{port}")
70
+ print(f"🎛️ 控制面板: http://{host}:{port}/auth")
71
+ print(f"📡 OpenAI API: http://{host}:{port}/v1")
72
+ print(f"🔧 Gemini API: http://{host}:{port}")
73
+ print("=" * 80)
74
+
75
+ # 检查关键环境变量
76
+ if os.environ.get('API_PASSWORD') or os.environ.get('PASSWORD'):
77
+ print("✅ API 密码已配置")
78
+ else:
79
+ print("⚠️ API 密码未配置,将使用默认密码")
80
+
81
+ if os.environ.get('PANEL_PASSWORD') or os.environ.get('PASSWORD'):
82
+ print("✅ 控制面板密码已配置")
83
+ else:
84
+ print("⚠️ 控制面板密码未配置,将使用默认密码")
85
+
86
+ # 检查凭证配置
87
+ gcli_creds = [k for k in os.environ.keys() if k.startswith('GCLI_CREDS_')]
88
+ if gcli_creds:
89
+ print(f"✅ 检测到 {len(gcli_creds)} 个环境变量凭证")
90
+ elif os.environ.get('AUTO_LOAD_ENV_CREDS') == 'true':
91
+ print("⚠️ 启用了环境变量凭证加载,但未检测到 GCLI_CREDS_* 变量")
92
+ else:
93
+ print("📁 将使用 creds 目录中的凭证文件")
94
+
95
+ print("=" * 80)
96
+ print()
97
+
98
+ async def main():
99
+ """主函数"""
100
+ # 注册信号处理器
101
+ signal.signal(signal.SIGINT, signal_handler)
102
+ signal.signal(signal.SIGTERM, signal_handler)
103
+
104
+ # 设置日志
105
+ setup_logging()
106
+
107
+ # 打印启动信息
108
+ print_startup_info()
109
+
110
+ try:
111
+ # 导入主应用(延迟导入以确保环境变量已设置)
112
+ from web import main as web_main
113
+
114
+ print("🔄 正在启动 GCLI2API 服务...")
115
+ await web_main()
116
+
117
+ except KeyboardInterrupt:
118
+ print("\n👋 收到中断信号,正在关闭服务...")
119
+ except Exception as e:
120
+ print(f"❌ 启动失败: {e}")
121
+ logging.exception("启动过程中发生错误")
122
+ sys.exit(1)
123
+
124
+ def run_app():
125
+ """运行应用(同步入口)"""
126
+ try:
127
+ asyncio.run(main())
128
+ except KeyboardInterrupt:
129
+ print("\n👋 服务已停止")
130
+ except Exception as e:
131
+ print(f"❌ 运行错误: {e}")
132
+ sys.exit(1)
133
+
134
+ if __name__ == "__main__":
135
+ run_app()
136
+
137
+ # HF Spaces 兼容性:提供 Gradio 接口(可选)
138
+ try:
139
+ import gradio as gr
140
+
141
+ def create_gradio_interface():
142
+ """创建简单的 Gradio 界面用于 HF Spaces 展示"""
143
+
144
+ def show_info():
145
+ port = os.environ.get('PORT', '7860')
146
+ api_password = os.environ.get('API_PASSWORD') or os.environ.get('PASSWORD', 'hf_demo_password_please_change')
147
+
148
+ info = f"""
149
+ # 🚀 GCLI2API Lightspeed 已启动
150
+
151
+ ## 🔗 访问链接
152
+ - **控制面板**: [点击访问管理界面](/auth) (密码: `{api_password}`)
153
+ - **API 文档**: [OpenAI 兼容端点](/v1/models)
154
+
155
+ ## 📡 API 端点
156
+ - **OpenAI 格式**: `POST /v1/chat/completions`
157
+ - **Gemini 格式**: `POST /v1/models/{{model}}:generateContent`
158
+
159
+ ## 🔑 认证信息
160
+ - **API 密钥**: `{api_password}`
161
+ - **使用方法**: 在请求头中添加 `Authorization: Bearer {api_password}`
162
+
163
+ ## 💡 使用提示
164
+ 1. 首次使用请先访问控制面板上传 Google OAuth 凭证文件
165
+ 2. 支持多种模型变体,包括思维模型和搜索增强模型
166
+ 3. 完整文档请查看项目 README
167
+
168
+ ---
169
+ **注意**: 这是一个功能性的 API 服务,主要通过 HTTP 接口使用。
170
+ """
171
+ return info
172
+
173
+ with gr.Blocks(title="GCLI2API Lightspeed", theme=gr.themes.Soft()) as demo:
174
+ gr.Markdown(show_info())
175
+
176
+ gr.Markdown("## 🔧 快速测试")
177
+ with gr.Row():
178
+ test_input = gr.Textbox(
179
+ label="测试消息",
180
+ placeholder="输入测试消息...",
181
+ value="Hello, how are you?"
182
+ )
183
+ test_btn = gr.Button("发送测试", variant="primary")
184
+
185
+ test_output = gr.Textbox(
186
+ label="API 响应",
187
+ lines=10,
188
+ interactive=False
189
+ )
190
+
191
+ def test_api(message):
192
+ import requests
193
+ import json
194
+
195
+ try:
196
+ port = os.environ.get('PORT', '7860')
197
+ api_password = os.environ.get('API_PASSWORD') or os.environ.get('PASSWORD', 'hf_demo_password_please_change')
198
+
199
+ response = requests.post(
200
+ f"http://127.0.0.1:{port}/v1/chat/completions",
201
+ headers={
202
+ "Authorization": f"Bearer {api_password}",
203
+ "Content-Type": "application/json"
204
+ },
205
+ json={
206
+ "model": "gemini-2.5-pro",
207
+ "messages": [
208
+ {"role": "user", "content": message}
209
+ ],
210
+ "temperature": 0.7
211
+ },
212
+ timeout=30
213
+ )
214
+
215
+ if response.status_code == 200:
216
+ result = response.json()
217
+ return json.dumps(result, indent=2, ensure_ascii=False)
218
+ else:
219
+ return f"错误 {response.status_code}: {response.text}"
220
+
221
+ except Exception as e:
222
+ return f"请求失败: {str(e)}\n\n请确保:\n1. 已配置有效的 Google OAuth 凭证\n2. API 服务正在运行"
223
+
224
+ test_btn.click(
225
+ test_api,
226
+ inputs=[test_input],
227
+ outputs=[test_output]
228
+ )
229
+
230
+ return demo
231
+
232
+ # 如果在 HF Spaces 环境中,启动 Gradio 界面
233
+ if os.environ.get('SPACE_ID'):
234
+ demo = create_gradio_interface()
235
+
236
+ # 在后台启动主应用
237
+ import threading
238
+ app_thread = threading.Thread(target=run_app, daemon=True)
239
+ app_thread.start()
240
+
241
+ # 启动 Gradio 界面
242
+ if __name__ == "__main__":
243
+ demo.launch(
244
+ server_name="0.0.0.0",
245
+ server_port=7860,
246
+ show_error=True,
247
+ share=False
248
+ )
249
+
250
+ except ImportError:
251
+ # 如果没有 Gradio,直接运行主应用
252
+ if __name__ == "__main__":
253
+ run_app()
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ httpx[socks]
3
+ pydantic
4
+ python-dotenv
5
+ hypercorn
6
+ aiofiles
7
+ python-multipart
8
+ toml
9
+ PyJWT
10
+ oauthlib
11
+ motor
12
+ redis
13
+ asyncpg
14
+ gradio
15
+ requests