From ac1ae892acbbd122dd2f1c14924f4bdcf4736116 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Fri, 17 Jul 2026 00:44:59 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Coze=20v3=20API=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E8=BD=AE=E8=AF=A2=20-=20GET=E6=96=B9=E6=B3=95+query=E5=8F=82?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E7=AD=89=E5=BE=85answer=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/ci_code_review.py | 63 ++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/scripts/ci_code_review.py b/scripts/ci_code_review.py index 518510d57..006e8220b 100644 --- a/scripts/ci_code_review.py +++ b/scripts/ci_code_review.py @@ -300,7 +300,9 @@ def call_llm_coze( llm_model: str, coze_bot_id: str, ) -> Optional[str]: - """扣子(Coze)原生 Bot API 调用""" + """扣子(Coze)原生 Bot API 调用(支持异步轮询)""" + import time + base_url = llm_base_url.rstrip("/") + "/" api_url = f"{base_url}v3/chat" @@ -342,21 +344,68 @@ def call_llm_coze( continue data = resp.json() + chat_data = data.get("data", {}) + chat_id = chat_data.get("id", "") + conversation_id = chat_data.get("conversation_id", "") + status = chat_data.get("status", "") - # 解析 Coze 返回格式,兼容多种可能的返回结构 + # Coze v3 API 异步:先返回 in_progress,需要轮询 + if status == "in_progress" and conversation_id and chat_id: + logger.info(f"Coze 异步处理中,开始轮询... (chat_id={chat_id[:12]}...)") + # 轮询 message 列表接口(GET + query参数),最多等 LLM_TIMEOUT 秒 + poll_url = f"{base_url}v3/chat/message/list" + poll_start = time.time() + poll_interval = 3 # 每3秒轮询一次 + + while time.time() - poll_start < LLM_TIMEOUT: + time.sleep(poll_interval) + poll_params = { + "chat_id": chat_id, + "conversation_id": conversation_id, + } + poll_resp = requests.get( + poll_url, + headers=headers, + params=poll_params, + timeout=GITEA_TIMEOUT, + ) + if poll_resp.status_code != 200: + logger.debug(f"轮询返回 HTTP {poll_resp.status_code}: {poll_resp.text[:100]}") + continue + + poll_data = poll_resp.json() + if poll_data.get("code", 0) != 0: + logger.debug(f"轮询返回错误: {poll_data.get('msg', '')}") + continue + + messages = poll_data.get("data", []) or [] + + # 找assistant的answer消息 + content = None + for msg in messages: + if msg.get("role") == "assistant" and msg.get("type") == "answer": + content = msg.get("content", "") + break + + if content and content.strip(): + logger.info(f"Coze 审查完成,结果长度: {len(content)} 字符") + return content + + logger.warning(f"Coze 轮询超时 ({LLM_TIMEOUT}s),未拿到结果") + last_error = "poll timeout" + continue + + # 同步返回的情况(兼容) content = None - # 方式1: data.messages 数组 - messages = data.get("data", {}).get("messages", []) or data.get("messages", []) + messages = chat_data.get("messages", []) or data.get("messages", []) for msg in messages: if msg.get("role") == "assistant" and msg.get("type") == "answer": content = msg.get("content", "") break - # 方式2: 直接 content 字段 if not content: - content = data.get("data", {}).get("content") or data.get("content") + content = chat_data.get("content") or data.get("content") - # 方式3: choices 格式(兼容) if not content: choices = data.get("choices", []) if choices: