mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-20 21:57:14 +08:00
fix(web): prevent duplicate image previews
This commit is contained in:
@@ -659,8 +659,11 @@ class AgentStreamExecutor:
|
|||||||
tool_calls_buffer[index]["arguments"] += func["arguments"]
|
tool_calls_buffer[index]["arguments"] += func["arguments"]
|
||||||
|
|
||||||
# Preserve _gemini_raw_parts for Gemini thoughtSignature round-trip
|
# Preserve _gemini_raw_parts for Gemini thoughtSignature round-trip
|
||||||
|
# (direct Gemini: list of parts; LinkAI proxy: base64 string of JSON parts)
|
||||||
if "_gemini_raw_parts" in delta:
|
if "_gemini_raw_parts" in delta:
|
||||||
gemini_raw_parts = delta["_gemini_raw_parts"]
|
gemini_raw_parts = delta["_gemini_raw_parts"]
|
||||||
|
elif isinstance(choice, dict) and choice.get("_gemini_raw_parts"):
|
||||||
|
gemini_raw_parts = choice["_gemini_raw_parts"]
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_str = str(e)
|
error_str = str(e)
|
||||||
|
|||||||
@@ -297,8 +297,12 @@ class ChatChannel(Channel):
|
|||||||
logger.debug("[chat_channel] sending reply: {}, context: {}".format(reply, context))
|
logger.debug("[chat_channel] sending reply: {}, context: {}".format(reply, context))
|
||||||
|
|
||||||
# 如果是文本回复,尝试提取并发送图片
|
# 如果是文本回复,尝试提取并发送图片
|
||||||
if reply.type == ReplyType.TEXT:
|
# Web channel renders images/videos inline via renderMarkdown,
|
||||||
|
# so skip the extract-and-send step to avoid duplicate media.
|
||||||
|
if reply.type == ReplyType.TEXT and context.get("channel_type") != "web":
|
||||||
self._extract_and_send_images(reply, context)
|
self._extract_and_send_images(reply, context)
|
||||||
|
elif reply.type == ReplyType.TEXT:
|
||||||
|
self._send(reply, context)
|
||||||
# 如果是图片回复但带有文本内容,先发文本再发图片
|
# 如果是图片回复但带有文本内容,先发文本再发图片
|
||||||
elif reply.type == ReplyType.IMAGE_URL and hasattr(reply, 'text_content') and reply.text_content:
|
elif reply.type == ReplyType.IMAGE_URL and hasattr(reply, 'text_content') and reply.text_content:
|
||||||
# 先发送文本
|
# 先发送文本
|
||||||
|
|||||||
@@ -436,7 +436,6 @@ class GoogleGeminiBot(Bot):
|
|||||||
tool_result_data = {"result": tool_content}
|
tool_result_data = {"result": tool_content}
|
||||||
|
|
||||||
# Find the tool name from previous messages
|
# Find the tool name from previous messages
|
||||||
# Look for the corresponding tool_call in model's message
|
|
||||||
tool_name = None
|
tool_name = None
|
||||||
for prev_msg in reversed(messages):
|
for prev_msg in reversed(messages):
|
||||||
if prev_msg.get("role") == "assistant":
|
if prev_msg.get("role") == "assistant":
|
||||||
@@ -450,13 +449,14 @@ class GoogleGeminiBot(Bot):
|
|||||||
if tool_name:
|
if tool_name:
|
||||||
break
|
break
|
||||||
|
|
||||||
# Gemini functionResponse format
|
# Gemini functionResponse format (Gemini 3 requires `id`)
|
||||||
parts.append({
|
fn_response = {
|
||||||
"functionResponse": {
|
"name": tool_name or "unknown",
|
||||||
"name": tool_name or "unknown",
|
"response": tool_result_data
|
||||||
"response": tool_result_data
|
}
|
||||||
}
|
if tool_use_id:
|
||||||
})
|
fn_response["id"] = tool_use_id
|
||||||
|
parts.append({"functionResponse": fn_response})
|
||||||
|
|
||||||
elif "text" in block:
|
elif "text" in block:
|
||||||
# Generic text field
|
# Generic text field
|
||||||
@@ -624,10 +624,11 @@ class GoogleGeminiBot(Bot):
|
|||||||
# Check for functionCall (per REST API docs)
|
# Check for functionCall (per REST API docs)
|
||||||
if "functionCall" in part:
|
if "functionCall" in part:
|
||||||
fc = part["functionCall"]
|
fc = part["functionCall"]
|
||||||
logger.info(f"[Gemini] Function call detected: {fc.get('name')}")
|
fc_id = fc.get("id") or f"call_{int(time.time() * 1000000)}"
|
||||||
|
logger.info(f"[Gemini] Function call detected: {fc.get('name')} (id={fc_id})")
|
||||||
|
|
||||||
tool_calls.append({
|
tool_calls.append({
|
||||||
"id": f"call_{int(time.time() * 1000000)}",
|
"id": fc_id,
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": fc.get("name"),
|
"name": fc.get("name"),
|
||||||
@@ -747,10 +748,12 @@ class GoogleGeminiBot(Bot):
|
|||||||
# Collect function calls
|
# Collect function calls
|
||||||
if "functionCall" in part:
|
if "functionCall" in part:
|
||||||
fc = part["functionCall"]
|
fc = part["functionCall"]
|
||||||
logger.info(f"[Gemini] Function call: {fc.get('name')}")
|
logger.info(f"[Gemini] Function call: {fc.get('name')} (id={fc.get('id')})")
|
||||||
|
# Prefer Gemini's native id; fall back to generated one
|
||||||
|
fc_id = fc.get("id") or f"call_{int(time.time() * 1000000)}_{len(all_tool_calls)}"
|
||||||
all_tool_calls.append({
|
all_tool_calls.append({
|
||||||
"index": len(all_tool_calls), # Add index to differentiate multiple tool calls
|
"index": len(all_tool_calls),
|
||||||
"id": f"call_{int(time.time() * 1000000)}_{len(all_tool_calls)}",
|
"id": fc_id,
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": fc.get("name"),
|
"name": fc.get("name"),
|
||||||
|
|||||||
@@ -673,6 +673,9 @@ def _handle_linkai_stream_response(self, base_url, headers, body):
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Forward SSE JSON as-is so extensions (e.g. delta._gemini_raw_parts
|
||||||
|
# for Gemini via LinkAI) reach agent_stream and are stored on assistant
|
||||||
|
# messages for the next request. Standard OpenAI fields are unchanged.
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user