@@ -46,6 +46,9 @@ def __init__(self):
4646 settings .THINKING_MODEL : "0727-360B-API" , # GLM-4.5-Thinking
4747 settings .SEARCH_MODEL : "0727-360B-API" , # GLM-4.5-Search
4848 settings .AIR_MODEL : "0727-106B-API" , # GLM-4.5-Air
49+ settings .GLM46_MODEL : "GLM-4-6-API-V1" , # GLM-4.6
50+ settings .GLM46_THINKING_MODEL : "GLM-4-6-API-V1" , # GLM-4.6-Thinking
51+ settings .GLM46_SEARCH_MODEL : "GLM-4-6-API-V1" , # GLM-4.6-Search
4952 }
5053
5154 def get_supported_models (self ) -> List [str ]:
@@ -54,7 +57,10 @@ def get_supported_models(self) -> List[str]:
5457 settings .PRIMARY_MODEL ,
5558 settings .THINKING_MODEL ,
5659 settings .SEARCH_MODEL ,
57- settings .AIR_MODEL
60+ settings .AIR_MODEL ,
61+ settings .GLM46_MODEL ,
62+ settings .GLM46_THINKING_MODEL ,
63+ settings .GLM46_SEARCH_MODEL ,
5864 ]
5965
6066 async def get_token (self ) -> str :
@@ -131,16 +137,16 @@ async def transform_request(self, request: OpenAIRequest) -> Dict[str, Any]:
131137
132138 # 确定请求的模型特性
133139 requested_model = request .model
134- is_thinking = requested_model == settings . THINKING_MODEL
135- is_search = requested_model == settings . SEARCH_MODEL
136- is_air = requested_model == settings . AIR_MODEL
140+ is_thinking = "-thinking" in requested_model . casefold ()
141+ is_search = "-search" in requested_model . casefold ()
142+ is_air = "-air" in requested_model . casefold ()
137143
138144 # 获取上游模型ID
139145 upstream_model_id = self .model_mapping .get (requested_model , "0727-360B-API" )
140146
141147 # 构建MCP服务器列表
142148 mcp_servers = []
143- if is_search :
149+ if is_search and "-4.5" in requested_model :
144150 mcp_servers .append ("deep-web-search" )
145151 self .logger .info ("🔍 检测到搜索模型,添加 deep-web-search MCP 服务器" )
146152
@@ -158,7 +164,38 @@ async def transform_request(self, request: OpenAIRequest) -> Dict[str, Any]:
158164 "auto_web_search" : is_search ,
159165 "preview_mode" : False ,
160166 "flags" : [],
161- "features" : [],
167+ "features" : [
168+ {
169+ "type" : "mcp" ,
170+ "server" : "vibe-coding" ,
171+ "status" : "hidden"
172+ },
173+ {
174+ "type" : "mcp" ,
175+ "server" : "ppt-maker" ,
176+ "status" : "hidden"
177+ },
178+ {
179+ "type" : "mcp" ,
180+ "server" : "image-search" ,
181+ "status" : "hidden"
182+ },
183+ {
184+ "type" : "mcp" ,
185+ "server" : "deep-research" ,
186+ "status" : "hidden"
187+ },
188+ {
189+ "type" : "tool_selector" ,
190+ "server" : "tool_selector" ,
191+ "status" : "hidden"
192+ },
193+ {
194+ "type" : "mcp" ,
195+ "server" : "advanced-search" ,
196+ "status" : "hidden"
197+ }
198+ ],
162199 "enable_thinking" : is_thinking ,
163200 },
164201 "background_tasks" : {
@@ -614,7 +651,114 @@ async def _handle_non_stream_response(
614651 chat_id : str ,
615652 model : str
616653 ) -> Dict [str , Any ]:
617- """处理非流式响应"""
618- # 简化的非流式响应处理
619- content = "非流式响应处理中..."
620- return self .create_openai_response (chat_id , model , content )
654+ """处理非流式响应
655+
656+ 说明:上游始终以 SSE 形式返回(transform_request 固定 stream=True),
657+ 因此这里需要聚合 aiter_lines() 的 data: 块,提取 usage、思考内容与答案内容,
658+ 并最终产出一次性 OpenAI 格式响应。
659+ """
660+ final_content = ""
661+ reasoning_content = ""
662+ usage_info : Dict [str , int ] = {
663+ "prompt_tokens" : 0 ,
664+ "completion_tokens" : 0 ,
665+ "total_tokens" : 0 ,
666+ }
667+
668+ try :
669+ async for line in response .aiter_lines ():
670+ if not line :
671+ continue
672+
673+ line = line .strip ()
674+
675+ # 仅处理以 data: 开头的 SSE 行,其余行尝试作为错误/JSON 忽略
676+ if not line .startswith ("data:" ):
677+ # 尝试解析为错误 JSON
678+ try :
679+ maybe_err = json .loads (line )
680+ if isinstance (maybe_err , dict ) and (
681+ "error" in maybe_err or "code" in maybe_err or "message" in maybe_err
682+ ):
683+ # 统一错误处理
684+ msg = (
685+ (maybe_err .get ("error" ) or {}).get ("message" )
686+ if isinstance (maybe_err .get ("error" ), dict )
687+ else maybe_err .get ("message" )
688+ ) or "上游返回错误"
689+ return self .handle_error (Exception (msg ), "API响应" )
690+ except Exception :
691+ pass
692+ continue
693+
694+ data_str = line [5 :].strip ()
695+ if not data_str or data_str in ("[DONE]" , "DONE" , "done" ):
696+ continue
697+
698+ # 解析 SSE 数据块
699+ try :
700+ chunk = json .loads (data_str )
701+ except json .JSONDecodeError :
702+ continue
703+
704+ if chunk .get ("type" ) != "chat:completion" :
705+ continue
706+
707+ data = chunk .get ("data" , {})
708+ phase = data .get ("phase" )
709+ delta_content = data .get ("delta_content" , "" )
710+ edit_content = data .get ("edit_content" , "" )
711+
712+ # 记录用量(通常在最后块中出现,但这里每次覆盖保持最新)
713+ if data .get ("usage" ):
714+ try :
715+ usage_info = data ["usage" ]
716+ except Exception :
717+ pass
718+
719+ # 思考阶段聚合(去除 <details><summary>... 包裹头)
720+ if phase == "thinking" :
721+ if delta_content :
722+ if delta_content .startswith ("<details" ):
723+ cleaned = (
724+ delta_content .split ("</summary>\n >" )[- 1 ].strip ()
725+ if "</summary>\n >" in delta_content
726+ else delta_content
727+ )
728+ else :
729+ cleaned = delta_content
730+ reasoning_content += cleaned
731+
732+ # 答案阶段聚合
733+ elif phase == "answer" :
734+ # 当 edit_content 同时包含思考结束标记与答案时,提取答案部分
735+ if edit_content and "</details>\n " in edit_content :
736+ content_after = edit_content .split ("</details>\n " )[- 1 ]
737+ if content_after :
738+ final_content += content_after
739+ elif delta_content :
740+ final_content += delta_content
741+
742+ except Exception as e :
743+ self .logger .error (f"❌ 非流式响应处理错误: { e } " )
744+ import traceback
745+ self .logger .error (traceback .format_exc ())
746+ # 返回统一错误响应
747+ return self .handle_error (e , "非流式聚合" )
748+
749+ # 清理并返回
750+ final_content = (final_content or "" ).strip ()
751+ reasoning_content = (reasoning_content or "" ).strip ()
752+
753+ # 若没有聚合到答案,但有思考内容,则保底返回思考内容
754+ if not final_content and reasoning_content :
755+ final_content = reasoning_content
756+
757+ # 返回包含推理内容的标准响应(若无推理则不会携带)
758+ return self .create_openai_response_with_reasoning (
759+ chat_id ,
760+ model ,
761+ final_content ,
762+ reasoning_content ,
763+ usage_info ,
764+ )
0 commit comments