From 448a95134f1588abdbbb830936a101d56b84391d Mon Sep 17 00:00:00 2001 From: taylorxie Date: Tue, 10 Mar 2026 14:01:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- maubot_llmplus/thrid_platform.py | 60 +++++++++++++++----------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/maubot_llmplus/thrid_platform.py b/maubot_llmplus/thrid_platform.py index 68a0653..876080d 100644 --- a/maubot_llmplus/thrid_platform.py +++ b/maubot_llmplus/thrid_platform.py @@ -39,36 +39,6 @@ async def _read_openai_sse(response): pass -async def _read_gemini_sse(response): - """读取 Gemini SSE 流(?alt=sse 格式),yield 每个 text chunk。 - Gemini 无 [DONE] 哨兵,通过 finishReason 字段判断结束。""" - while True: - try: - line_bytes = await asyncio.wait_for(response.content.readline(), timeout=60.0) - except asyncio.TimeoutError: - break - if not line_bytes: - break - line = line_bytes.decode("utf-8").strip() - if not line.startswith("data: "): - continue - data_str = line[6:] - try: - data = json.loads(data_str) - candidates = data.get("candidates", []) - if candidates: - candidate = candidates[0] - parts = candidate.get("content", {}).get("parts", []) - for part in parts: - text = part.get("text", "") - if text: - yield text - # finishReason 存在表示流已结束,主动退出(等价于 [DONE]) - if candidate.get("finishReason"): - break - except json.JSONDecodeError: - pass - class Deepseek(Platform): @@ -416,8 +386,34 @@ class Gemini(Platform): async with self.http.post(endpoint, headers=headers, data=json.dumps(request_body)) as response: if response.status != 200: raise ValueError(f"Error: {await response.text()}") - async for chunk in _read_gemini_sse(response): - yield chunk + # 与 Anthropic 保持一致:内联 while 循环,避免双层异步生成器代理 + while True: + try: + line_bytes = await asyncio.wait_for(response.content.readline(), timeout=60.0) + except asyncio.TimeoutError: + break + if not line_bytes: + break + line = line_bytes.decode("utf-8").strip() + if not line.startswith("data: "): + continue + data_str = line[6:] + try: + data = json.loads(data_str) + candidates = data.get("candidates", []) + if not candidates: + continue + candidate = candidates[0] + # 先 yield 文本,再判断是否结束(对齐 OpenAI [DONE] 逻辑) + parts = candidate.get("content", {}).get("parts", []) + for part in parts: + text = part.get("text", "") + if text: + yield text + if candidate.get("finishReason"): + break + except json.JSONDecodeError: + pass async def list_models(self) -> List[str]: full_url = f"{self.url}/v1beta/models"