xiaoyukkkk commited on
Commit
4f80088
·
verified ·
1 Parent(s): 1d73be2

Upload 11 files

Browse files
Files changed (5) hide show
  1. .env.example +21 -56
  2. .gitignore +1 -0
  3. README.md +1 -1
  4. main.py +202 -27
  5. requirements.txt +2 -1
.env.example CHANGED
@@ -2,69 +2,34 @@
2
  # Gemini Business2API 配置示例
3
  # ============================================
4
 
5
- # 代理设置(可选)
6
- # PROXY=http://127.0.0.1:7890
7
-
8
- # API 访问密钥(可选,用于保护 API 端点)
9
- # API_KEY=your-secret-api-key-here
10
 
11
- # 路径前缀(可选,用于隐藏端点路径)
12
  # 如果设置了,端点将为: /{PATH_PREFIX}/, /{PATH_PREFIX}/v1/
13
  # 如果未设置,端点将为: /, /v1/
14
  # PATH_PREFIX=your-random-path-prefix
15
 
16
- # 管理员密钥(必需,用于登录管理面板)
17
- ADMIN_KEY=your-admin-secret-key
18
-
19
- # Session加密密钥(可选,自动生成)
20
- # SESSION_SECRET_KEY=your-session-secret-key
21
-
22
- # Session过期时间(可选,单位:小时,默认24小时)
23
- # SESSION_EXPIRE_HOURS=24
24
-
25
- # 服务器完整 URL(可选,用于反代公开日志和图片 URL)
26
- # BASE_URL=https://your-domain.com
27
-
28
  # ============================================
29
- # 高级配置(可选,使用默认值)
 
 
30
  # ============================================
31
 
32
- # 新会话创建最多尝试账户数(默认:5)
33
- # MAX_NEW_SESSION_TRIES=5
34
-
35
- # 请求失败最多重试次数(默认:3)
36
- # MAX_REQUEST_RETRIES=3
37
-
38
- # 每次重试找账户的最大尝试次数(默认:5)
39
- # MAX_ACCOUNT_SWITCH_TRIES=5
40
-
41
- # 账户连续失败阈值(默认:3次)
42
- # ACCOUNT_FAILURE_THRESHOLD=3
43
-
44
- # 429限流错误冷却时间,单位秒(默认:600秒=10分钟)
45
- # RATE_LIMIT_COOLDOWN_SECONDS=600
46
-
47
- # 会话缓存过期时间,单位秒(默认:3600秒=1小时)
48
- # SESSION_CACHE_TTL_SECONDS=3600
49
-
50
- # ============================================
51
- # 公开展示配置(可选)
52
  # ============================================
53
- # Logo URL(公开,为空则不显示)
54
- # LOGO_URL=https://your-domain.com/logo.png
55
-
56
- # 开始对话链接(公开,为空则不显示)
57
- # CHAT_URL=https://your-chat-app.com
58
-
59
- # 模型名称(公开,默认:gemini-business)
60
- # MODEL_NAME=gemini-business
61
-
62
  # ============================================
63
- # 多账户配置(必需)
64
- # ============================================
65
- # 使用 JSON 数组格式配置多个 Gemini 账户
66
- # 必需字段:secure_c_ses, csesidx, config_id
67
- # 可选字段:id, host_c_oses, proxy, expires_at
68
- # 详细配置请参考 accounts_config.example.json
69
-
70
- ACCOUNTS_CONFIG=[{"secure_c_ses":"your-cookie-here","csesidx":"your-idx","config_id":"your-config","expires_at": "2026-01-08 21:04:39"}]
 
 
 
 
 
 
 
2
  # Gemini Business2API 配置示例
3
  # ============================================
4
 
5
+ # 管理员密钥(必需,用于登录管理面板)
6
+ ADMIN_KEY=your-admin-secret-key
 
 
 
7
 
8
+ # 路径前缀(可选,用于隐藏端点路径,提高安全性)
9
  # 如果设置了,端点将为: /{PATH_PREFIX}/, /{PATH_PREFIX}/v1/
10
  # 如果未设置,端点将为: /, /v1/
11
  # PATH_PREFIX=your-random-path-prefix
12
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  # ============================================
14
+ # 其他配置请在管理面板的"系统设置"中配置
15
+ # 包括:API密钥、代理、图片生成、重试策略等
16
+ # 配置保存在 data/settings.yaml
17
  # ============================================
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  # ============================================
20
+ # 账户配置
 
 
 
 
 
 
 
 
21
  # ============================================
22
+ # 账户配置保存在 accounts.json 文件中
23
+ # 首次启动时会自动创建空配置
24
+ # 请在管理面板中添加账户,或直接编辑 accounts.json
25
+ #
26
+ # 账户配置格式示例:
27
+ # [
28
+ # {
29
+ # "id": "account_1",
30
+ # "secure_c_ses": "your-cookie-here",
31
+ # "csesidx": "your-idx",
32
+ # "config_id": "your-config",
33
+ # "expires_at": "2025-12-31 23:59:59"
34
+ # }
35
+ # ]
.gitignore CHANGED
@@ -36,6 +36,7 @@ ENV/
36
  .env
37
  *.log
38
  data/stats.json
 
39
  accounts.json
40
 
41
  # Generated files
 
36
  .env
37
  *.log
38
  data/stats.json
39
+ data/settings.yaml
40
  accounts.json
41
 
42
  # Generated files
README.md CHANGED
@@ -624,7 +624,7 @@ Deno.serve(handler);
624
  ### 7. API_KEY 和 ADMIN_KEY 的区别?
625
 
626
  - **API_KEY**: 保护聊天接口 (`/v1/chat/completions`)
627
- - **ADMIN_KEY**: 保护管理面板 (`/admin`)
628
 
629
  可以设置相同的值,也可以分开
630
 
 
624
  ### 7. API_KEY 和 ADMIN_KEY 的区别?
625
 
626
  - **API_KEY**: 保护聊天接口 (`/v1/chat/completions`)
627
+ - **ADMIN_KEY**: 保护管理面板 (`/` 或 `/{PATH_PREFIX}`)
628
 
629
  可以设置相同的值,也可以分开
630
 
main.py CHANGED
@@ -1,6 +1,7 @@
1
- import json, time, os, asyncio, uuid, ssl, re
2
  from datetime import datetime, timezone, timedelta
3
  from typing import List, Optional, Union, Dict, Any
 
4
  import logging
5
  from dotenv import load_dotenv
6
 
@@ -118,20 +119,87 @@ memory_handler.setFormatter(logging.Formatter("%(asctime)s | %(levelname)s | %(m
118
  logger.addHandler(memory_handler)
119
 
120
  load_dotenv()
121
- # ---------- 配置 ----------
122
- PROXY = os.getenv("PROXY", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  TIMEOUT_SECONDS = 600
124
- API_KEY = os.getenv("API_KEY", "") # API 访问密钥(可选,用于保护API端点)
125
- PATH_PREFIX = os.getenv("PATH_PREFIX", "") # 路径前缀(可选,用于隐藏端点路径)
126
- ADMIN_KEY = os.getenv("ADMIN_KEY", "") # 管理员密钥(必需,用于登录)
127
- BASE_URL = os.getenv("BASE_URL", "") # 服务器完整URL(可选,用于图片URL生成)
128
- SESSION_SECRET_KEY = os.getenv("SESSION_SECRET_KEY", generate_session_secret()) # Session加密密钥(自动生成)
129
- SESSION_EXPIRE_HOURS = int(os.getenv("SESSION_EXPIRE_HOURS", "24")) # Session过期时间(默认24小时)
130
 
131
  # ---------- 公开展示配置 ----------
132
- LOGO_URL = os.getenv("LOGO_URL", "") # Logo URL(公开,为空则不显示)
133
- CHAT_URL = os.getenv("CHAT_URL", "") # 开始对话链接(公开,为空则不显示)
134
- MODEL_NAME = os.getenv("MODEL_NAME", "gemini-business") # 模型名称(公开)
 
 
 
135
 
136
  # ---------- 图片存储配置 ----------
137
  if os.path.exists("/data"):
@@ -139,13 +207,13 @@ if os.path.exists("/data"):
139
  else:
140
  IMAGE_DIR = "./data/images" # 本地持久化存储
141
 
142
- # ---------- 重试配置 ----------
143
- MAX_NEW_SESSION_TRIES = int(os.getenv("MAX_NEW_SESSION_TRIES", "5")) # 新会话创建最多尝试账户数(默认5)
144
- MAX_REQUEST_RETRIES = int(os.getenv("MAX_REQUEST_RETRIES", "3")) # 请求失败最多重试次数(默认3)
145
- MAX_ACCOUNT_SWITCH_TRIES = int(os.getenv("MAX_ACCOUNT_SWITCH_TRIES", "5")) # 每次重试找账户的最大尝试次数(默认5)
146
- ACCOUNT_FAILURE_THRESHOLD = int(os.getenv("ACCOUNT_FAILURE_THRESHOLD", "3")) # 账户连续失败阈值(默认3次)
147
- RATE_LIMIT_COOLDOWN_SECONDS = int(os.getenv("RATE_LIMIT_COOLDOWN_SECONDS", "600")) # 429错误冷却时间(默认600秒=10分钟)
148
- SESSION_CACHE_TTL_SECONDS = int(os.getenv("SESSION_CACHE_TTL_SECONDS", "3600")) # 会话缓存过期时间(默认3600秒=1小时)
149
 
150
  # ---------- 模型映射配置 ----------
151
  MODEL_MAPPING = {
@@ -552,7 +620,7 @@ async def home(request: Request):
552
  else:
553
  # 未设置PATH_PREFIX(公开模式),根据登录状态重定向
554
  if is_logged_in(request):
555
- return await generate_admin_html(request, multi_account_mgr)
556
  else:
557
  return RedirectResponse(url="/login", status_code=302)
558
 
@@ -736,6 +804,98 @@ async def admin_enable_account(request: Request, account_id: str):
736
  logger.error(f"[CONFIG] 启用账户失败: {str(e)}")
737
  raise HTTPException(500, f"启用失败: {str(e)}")
738
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
  @app.get("/admin/log")
740
  @require_login()
741
  async def admin_get_logs(
@@ -861,6 +1021,16 @@ if PATH_PREFIX:
861
  async def admin_logs_html_route_prefixed(request: Request):
862
  return await admin_logs_html_route(request=request)
863
 
 
 
 
 
 
 
 
 
 
 
864
  # ---------- API端点(API Key认证) ----------
865
 
866
  @app.get("/v1/models")
@@ -943,7 +1113,7 @@ async def chat_impl(
943
  request.state.model = req.model
944
 
945
  # 3. 生成会话指纹,获取Session锁(防止同一对话的并发请求冲突)
946
- conv_key = get_conversation_key([m.dict() for m in req.messages], client_ip)
947
  session_lock = await multi_account_mgr.acquire_session_lock(conv_key)
948
 
949
  # 4. 在锁的保护下检查缓存和处理Session(保证同一对话的请求串行化)
@@ -1286,6 +1456,16 @@ async def stream_chat_generator(session: str, text_content: str, file_ids: List[
1286
  jwt = await account_manager.get_jwt(request_id)
1287
  headers = get_common_headers(jwt, USER_AGENT)
1288
 
 
 
 
 
 
 
 
 
 
 
1289
  body = {
1290
  "configId": account_manager.config.config_id,
1291
  "additionalParams": {"token": "-"},
@@ -1295,12 +1475,7 @@ async def stream_chat_generator(session: str, text_content: str, file_ids: List[
1295
  "filter": "",
1296
  "fileIds": file_ids, # 注入文件 ID
1297
  "answerGenerationMode": "NORMAL",
1298
- "toolsSpec": {
1299
- "webGroundingSpec": {},
1300
- "toolRegistry": "default_tool_registry",
1301
- "imageGenerationSpec": {},
1302
- "videoGenerationSpec": {}
1303
- },
1304
  "languageCode": "zh-CN",
1305
  "userMetadata": {"timeZone": "Asia/Shanghai"},
1306
  "assistSkippingMode": "REQUEST_ASSIST"
 
1
+ import json, time, os, asyncio, uuid, ssl, re, yaml
2
  from datetime import datetime, timezone, timedelta
3
  from typing import List, Optional, Union, Dict, Any
4
+ from pathlib import Path
5
  import logging
6
  from dotenv import load_dotenv
7
 
 
119
  logger.addHandler(memory_handler)
120
 
121
  load_dotenv()
122
+
123
+ # ---------- YAML 配置系统 ----------
124
+ SETTINGS_FILE = "data/settings.yaml"
125
+
126
+ # 默认配置
127
+ DEFAULT_SETTINGS = {
128
+ "basic": {
129
+ "api_key": "",
130
+ "base_url": "",
131
+ "proxy": ""
132
+ },
133
+ "image_generation": {
134
+ "enabled": True,
135
+ "supported_models": ["gemini-3-pro-preview"],
136
+ "last_updated": None
137
+ },
138
+ "retry": {
139
+ "max_new_session_tries": 5,
140
+ "max_request_retries": 3,
141
+ "max_account_switch_tries": 5,
142
+ "account_failure_threshold": 3,
143
+ "rate_limit_cooldown_seconds": 600,
144
+ "session_cache_ttl_seconds": 3600
145
+ },
146
+ "public_display": {
147
+ "logo_url": "",
148
+ "chat_url": ""
149
+ },
150
+ "session": {
151
+ "expire_hours": 24
152
+ }
153
+ }
154
+
155
+ def load_settings() -> dict:
156
+ """加载 YAML 配置"""
157
+ Path("data").mkdir(exist_ok=True)
158
+ if Path(SETTINGS_FILE).exists():
159
+ try:
160
+ with open(SETTINGS_FILE, 'r', encoding='utf-8') as f:
161
+ settings = yaml.safe_load(f) or {}
162
+ # 合并默认配置(确保新增配置项有默认值)
163
+ for key, value in DEFAULT_SETTINGS.items():
164
+ if key not in settings:
165
+ settings[key] = value
166
+ elif isinstance(value, dict):
167
+ for k, v in value.items():
168
+ if k not in settings[key]:
169
+ settings[key][k] = v
170
+ return settings
171
+ except Exception as e:
172
+ print(f"[WARN] 加载配置文件失败: {e},使用默认配置")
173
+ # 创建默认配置文件
174
+ save_settings(DEFAULT_SETTINGS)
175
+ return DEFAULT_SETTINGS.copy()
176
+
177
+ def save_settings(settings: dict):
178
+ """保存 YAML 配置"""
179
+ Path("data").mkdir(exist_ok=True)
180
+ with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
181
+ yaml.dump(settings, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
182
+
183
+ # 加载配置
184
+ settings = load_settings()
185
+
186
+ # ---------- 配置(从 settings.yaml 读取)----------
187
+ PROXY = settings["basic"]["proxy"]
188
  TIMEOUT_SECONDS = 600
189
+ API_KEY = settings["basic"]["api_key"]
190
+ PATH_PREFIX = os.getenv("PATH_PREFIX", "") # 路径前缀保留环境变量(影响路由注册)
191
+ ADMIN_KEY = os.getenv("ADMIN_KEY", "") # 管理员密钥保留环境变量(安全相关)
192
+ BASE_URL = settings["basic"]["base_url"]
193
+ SESSION_SECRET_KEY = os.getenv("SESSION_SECRET_KEY", generate_session_secret()) # 自动生成
194
+ SESSION_EXPIRE_HOURS = settings["session"]["expire_hours"]
195
 
196
  # ---------- 公开展示配置 ----------
197
+ LOGO_URL = settings["public_display"]["logo_url"]
198
+ CHAT_URL = settings["public_display"]["chat_url"]
199
+
200
+ # ---------- 图片生成配置 ----------
201
+ IMAGE_GENERATION_ENABLED = settings["image_generation"]["enabled"]
202
+ IMAGE_GENERATION_MODELS = settings["image_generation"]["supported_models"]
203
 
204
  # ---------- 图片存储配置 ----------
205
  if os.path.exists("/data"):
 
207
  else:
208
  IMAGE_DIR = "./data/images" # 本地持久化存储
209
 
210
+ # ---------- 重试配置(从 settings.yaml 读取)----------
211
+ MAX_NEW_SESSION_TRIES = settings["retry"]["max_new_session_tries"]
212
+ MAX_REQUEST_RETRIES = settings["retry"]["max_request_retries"]
213
+ MAX_ACCOUNT_SWITCH_TRIES = settings["retry"]["max_account_switch_tries"]
214
+ ACCOUNT_FAILURE_THRESHOLD = settings["retry"]["account_failure_threshold"]
215
+ RATE_LIMIT_COOLDOWN_SECONDS = settings["retry"]["rate_limit_cooldown_seconds"]
216
+ SESSION_CACHE_TTL_SECONDS = settings["retry"]["session_cache_ttl_seconds"]
217
 
218
  # ---------- 模型映射配置 ----------
219
  MODEL_MAPPING = {
 
620
  else:
621
  # 未设置PATH_PREFIX(公开模式),根据登录状态重定向
622
  if is_logged_in(request):
623
+ return HTMLResponse(content=templates.generate_admin_html(request, multi_account_mgr))
624
  else:
625
  return RedirectResponse(url="/login", status_code=302)
626
 
 
804
  logger.error(f"[CONFIG] 启用账户失败: {str(e)}")
805
  raise HTTPException(500, f"启用失败: {str(e)}")
806
 
807
+ # ---------- 系统设置 API ----------
808
+ @app.get("/admin/settings")
809
+ @require_login()
810
+ async def admin_get_settings(request: Request):
811
+ """获取系统设置"""
812
+ return settings
813
+
814
+ @app.put("/admin/settings")
815
+ @require_login()
816
+ async def admin_update_settings(request: Request, new_settings: dict = Body(...)):
817
+ """更新系统设置"""
818
+ global settings, API_KEY, PROXY, BASE_URL, LOGO_URL, CHAT_URL
819
+ global IMAGE_GENERATION_ENABLED, IMAGE_GENERATION_MODELS
820
+ global MAX_NEW_SESSION_TRIES, MAX_REQUEST_RETRIES, MAX_ACCOUNT_SWITCH_TRIES
821
+ global ACCOUNT_FAILURE_THRESHOLD, RATE_LIMIT_COOLDOWN_SECONDS, SESSION_CACHE_TTL_SECONDS
822
+ global SESSION_EXPIRE_HOURS, multi_account_mgr, http_client
823
+
824
+ try:
825
+ # 合并设置(保留未修改的项)
826
+ for key, value in new_settings.items():
827
+ if key in settings and isinstance(value, dict):
828
+ settings[key].update(value)
829
+ else:
830
+ settings[key] = value
831
+
832
+ # 添加更新时间
833
+ if "image_generation" in settings:
834
+ settings["image_generation"]["last_updated"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
835
+
836
+ # 保存到文件
837
+ save_settings(settings)
838
+
839
+ # 保存旧配置用于对比
840
+ old_proxy = PROXY
841
+ old_retry_config = {
842
+ "account_failure_threshold": ACCOUNT_FAILURE_THRESHOLD,
843
+ "rate_limit_cooldown_seconds": RATE_LIMIT_COOLDOWN_SECONDS,
844
+ "session_cache_ttl_seconds": SESSION_CACHE_TTL_SECONDS
845
+ }
846
+
847
+ # 更新全局变量(实时生效)
848
+ API_KEY = settings["basic"]["api_key"]
849
+ PROXY = settings["basic"]["proxy"]
850
+ BASE_URL = settings["basic"]["base_url"]
851
+ LOGO_URL = settings["public_display"]["logo_url"]
852
+ CHAT_URL = settings["public_display"]["chat_url"]
853
+ IMAGE_GENERATION_ENABLED = settings["image_generation"]["enabled"]
854
+ IMAGE_GENERATION_MODELS = settings["image_generation"]["supported_models"]
855
+ MAX_NEW_SESSION_TRIES = settings["retry"]["max_new_session_tries"]
856
+ MAX_REQUEST_RETRIES = settings["retry"]["max_request_retries"]
857
+ MAX_ACCOUNT_SWITCH_TRIES = settings["retry"]["max_account_switch_tries"]
858
+ ACCOUNT_FAILURE_THRESHOLD = settings["retry"]["account_failure_threshold"]
859
+ RATE_LIMIT_COOLDOWN_SECONDS = settings["retry"]["rate_limit_cooldown_seconds"]
860
+ SESSION_CACHE_TTL_SECONDS = settings["retry"]["session_cache_ttl_seconds"]
861
+ SESSION_EXPIRE_HOURS = settings["session"]["expire_hours"]
862
+
863
+ # 检查是否需要重建 HTTP 客户端(代理变化)
864
+ if old_proxy != PROXY:
865
+ logger.info(f"[CONFIG] 代理配置已变化,重建 HTTP 客户端")
866
+ await http_client.aclose() # 关闭旧客户端
867
+ http_client = httpx.AsyncClient(
868
+ proxy=PROXY or None,
869
+ verify=False,
870
+ http2=False,
871
+ timeout=httpx.Timeout(TIMEOUT_SECONDS, connect=60.0),
872
+ limits=httpx.Limits(
873
+ max_keepalive_connections=100,
874
+ max_connections=200
875
+ )
876
+ )
877
+
878
+ # 检查是否需要更新账户管理器配置(重试策略变化)
879
+ retry_changed = (
880
+ old_retry_config["account_failure_threshold"] != ACCOUNT_FAILURE_THRESHOLD or
881
+ old_retry_config["rate_limit_cooldown_seconds"] != RATE_LIMIT_COOLDOWN_SECONDS or
882
+ old_retry_config["session_cache_ttl_seconds"] != SESSION_CACHE_TTL_SECONDS
883
+ )
884
+
885
+ if retry_changed:
886
+ logger.info(f"[CONFIG] 重试策略已变化,更新账户管理器配置")
887
+ # 更新所有账户管理器的配置
888
+ multi_account_mgr.session_cache_ttl_seconds = SESSION_CACHE_TTL_SECONDS
889
+ for account_id, account_mgr in multi_account_mgr.accounts.items():
890
+ account_mgr.account_failure_threshold = ACCOUNT_FAILURE_THRESHOLD
891
+ account_mgr.rate_limit_cooldown_seconds = RATE_LIMIT_COOLDOWN_SECONDS
892
+
893
+ logger.info(f"[CONFIG] 系统设置已更新并实时生效")
894
+ return {"status": "success", "message": "设置已保存并实时生效!"}
895
+ except Exception as e:
896
+ logger.error(f"[CONFIG] 更新设置失败: {str(e)}")
897
+ raise HTTPException(500, f"更新失败: {str(e)}")
898
+
899
  @app.get("/admin/log")
900
  @require_login()
901
  async def admin_get_logs(
 
1021
  async def admin_logs_html_route_prefixed(request: Request):
1022
  return await admin_logs_html_route(request=request)
1023
 
1024
+ @app.get(f"/{PATH_PREFIX}/settings")
1025
+ @require_login()
1026
+ async def admin_get_settings_prefixed(request: Request):
1027
+ return await admin_get_settings(request=request)
1028
+
1029
+ @app.put(f"/{PATH_PREFIX}/settings")
1030
+ @require_login()
1031
+ async def admin_update_settings_prefixed(request: Request, new_settings: dict = Body(...)):
1032
+ return await admin_update_settings(request=request, new_settings=new_settings)
1033
+
1034
  # ---------- API端点(API Key认证) ----------
1035
 
1036
  @app.get("/v1/models")
 
1113
  request.state.model = req.model
1114
 
1115
  # 3. 生成会话指纹,获取Session锁(防止同一对话的并发请求冲突)
1116
+ conv_key = get_conversation_key([m.model_dump() for m in req.messages], client_ip)
1117
  session_lock = await multi_account_mgr.acquire_session_lock(conv_key)
1118
 
1119
  # 4. 在锁的保护下检查缓存和处理Session(保证同一对话的请求串行化)
 
1456
  jwt = await account_manager.get_jwt(request_id)
1457
  headers = get_common_headers(jwt, USER_AGENT)
1458
 
1459
+ # 构建 toolsSpec(根据配置决定是否启用图片生成)
1460
+ tools_spec = {
1461
+ "webGroundingSpec": {},
1462
+ "toolRegistry": "default_tool_registry",
1463
+ }
1464
+ # 只在启用且模型支持时添加图片生成
1465
+ if IMAGE_GENERATION_ENABLED and model_name in IMAGE_GENERATION_MODELS:
1466
+ tools_spec["imageGenerationSpec"] = {}
1467
+ tools_spec["videoGenerationSpec"] = {}
1468
+
1469
  body = {
1470
  "configId": account_manager.config.config_id,
1471
  "additionalParams": {"token": "-"},
 
1475
  "filter": "",
1476
  "fileIds": file_ids, # 注入文件 ID
1477
  "answerGenerationMode": "NORMAL",
1478
+ "toolsSpec": tools_spec,
 
 
 
 
 
1479
  "languageCode": "zh-CN",
1480
  "userMetadata": {"timeZone": "Asia/Shanghai"},
1481
  "assistSkippingMode": "REQUEST_ASSIST"
requirements.txt CHANGED
@@ -5,4 +5,5 @@ pydantic==2.7.0
5
  aiofiles==24.1.0
6
  python-dotenv==1.0.1
7
  itsdangerous==2.1.2
8
- python-multipart==0.0.6
 
 
5
  aiofiles==24.1.0
6
  python-dotenv==1.0.1
7
  itsdangerous==2.1.2
8
+ python-multipart==0.0.6
9
+ pyyaml>=6.0