Support multimodal images in local chat agent

This commit is contained in:
showtan001
2026-08-29 21:17:20 +08:00
parent a315c62f3a
commit 71fd182f50
3 changed files with 56 additions and 4 deletions
@@ -38,7 +38,7 @@ class ConversationAgent:
self.max_rounds = max_rounds
self._client: Any | None = None
def chat(self, messages: list[dict[str, str]]) -> ChatResult:
def chat(self, messages: list[dict[str, Any]]) -> ChatResult:
if self.config.provider not in {"openai", "openai_compatible"}:
raise RuntimeError(
"conversation chat currently requires an OpenAI-compatible provider"
@@ -54,7 +54,7 @@ class ConversationAgent:
"completed unless the tool result confirms it."
),
},
*messages,
*[_normalize_message(message) for message in messages],
]
calls = 0
for _ in range(self.max_rounds):
@@ -142,3 +142,36 @@ def _message_dict(message: Any) -> dict[str, Any]:
for call in calls
]
return result
def _normalize_message(message: dict[str, Any]) -> dict[str, Any]:
"""Accept OpenAI text/image content blocks and a compact image_base64 form."""
role = message.get("role")
content = message.get("content")
if isinstance(content, str):
return {"role": role, "content": content}
if isinstance(content, list):
blocks: list[dict[str, Any]] = []
for block in content:
if not isinstance(block, dict):
continue
if block.get("type") == "text" and isinstance(block.get("text"), str):
blocks.append({"type": "text", "text": block["text"]})
elif block.get("type") == "image_url":
image_url = block.get("image_url")
if isinstance(image_url, str):
image_url = {"url": image_url}
if isinstance(image_url, dict) and isinstance(image_url.get("url"), str):
blocks.append({"type": "image_url", "image_url": {"url": image_url["url"]}})
if blocks:
return {"role": role, "content": blocks}
image = message.get("image_base64")
if isinstance(image, str) and image:
mime = str(message.get("mime_type") or "image/png")
text = message.get("text")
blocks = []
if isinstance(text, str) and text:
blocks.append({"type": "text", "text": text})
blocks.append({"type": "image_url", "image_url": {"url": f"data:{mime};base64,{image}"}})
return {"role": role, "content": blocks}
raise ValueError("message content must be text, image blocks, or image_base64")
+2 -2
View File
@@ -429,11 +429,11 @@ def create_console_app(
if not isinstance(raw_messages, list) or not raw_messages:
raise HTTPException(status_code=400, detail="messages must be a non-empty list")
messages = [
{"role": str(item["role"]), "content": str(item["content"])}
{key: value for key, value in item.items() if key in {"role", "content", "image_base64", "mime_type", "text"}}
for item in raw_messages
if isinstance(item, dict)
and item.get("role") in {"user", "assistant"}
and isinstance(item.get("content"), str)
and (isinstance(item.get("content"), (str, list)) or isinstance(item.get("image_base64"), str))
]
if not messages:
raise HTTPException(status_code=400, detail="messages are invalid")