feat: persist photos and enrich conversation history

This commit is contained in:
Xin Wang
2026-08-05 16:14:03 +08:00
parent 545536bdf3
commit 317a0600bc
19 changed files with 1146 additions and 119 deletions
+54
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio
from collections.abc import Awaitable
from time import monotonic
from typing import Any
from uuid import uuid4
@@ -503,12 +504,45 @@ class PromptBrain(BaseBrain):
await params.result_callback(result)
async def call_tool(params: FunctionCallParams) -> None:
invocation_id = f"tool_{uuid4().hex[:20]}"
started_at = monotonic()
await self._emit_trace(
"tool_started",
invocationId=invocation_id,
toolId=tool.id,
toolName=tool.name,
functionName=tool.function_name,
toolType=tool.type,
)
try:
result = await self._tools.execute(tool, dict(params.arguments or {}))
if result["updated_variables"]:
self._refresh_prompt()
await self._emit_trace(
"tool_completed" if result.get("status") == "ok" else "tool_failed",
invocationId=invocation_id,
toolId=tool.id,
toolName=tool.name,
functionName=tool.function_name,
toolType=tool.type,
status=str(result.get("status") or "unknown"),
durationMs=max(0, round((monotonic() - started_at) * 1000)),
updatedVariables=list(result.get("updated_variables") or []),
resultKeys=sorted(str(name) for name in result if name != "data"),
)
await return_result(params, result)
except (ToolExecutionError, ValueError) as exc:
await self._emit_trace(
"tool_failed",
invocationId=invocation_id,
toolId=tool.id,
toolName=tool.name,
functionName=tool.function_name,
toolType=tool.type,
status="error",
durationMs=max(0, round((monotonic() - started_at) * 1000)),
error=str(exc)[:2048],
)
await return_result(
params,
{"status": "error", "message": f"工具调用失败: {exc}"},
@@ -522,6 +556,26 @@ class PromptBrain(BaseBrain):
)
return schema, call_tool
async def _emit_trace(self, event: str, **details: Any) -> None:
runtime = self._runtime
if runtime is None:
return
try:
await runtime.queue_frame(
OutputTransportMessageUrgentFrame(
message={
"type": "workflow-event",
"eventId": f"wfe_{uuid4().hex[:20]}",
"event": event,
"timestamp": time_now_iso8601(),
"sessionId": runtime.session_id,
**details,
}
)
)
except Exception as exc: # noqa: BLE001 - trace must not alter execution
logger.warning(f"发送 Prompt 工具轨迹失败,不影响当前调用: {exc}")
def _make_end_call_tool(self, tool, runtime: BrainRuntime):
config = (tool.definition or {}).get("config") or {}
message_type = str(config.get("message_type") or "none")
+38
View File
@@ -6,7 +6,9 @@ import asyncio
from collections.abc import Awaitable
from copy import deepcopy
from dataclasses import dataclass, replace
from time import monotonic
from typing import Any
from uuid import uuid4
from loguru import logger
from models import AssistantConfig, RuntimeTool
@@ -764,10 +766,46 @@ class WorkflowBrain(BaseBrain):
async def handler(args, _flow_manager):
transition_id = self._state.transition_id
invocation_id = f"tool_{uuid4().hex[:20]}"
started_at = monotonic()
await self._emit_trace(
"tool_started",
nodeId=node_id,
invocationId=invocation_id,
toolId=tool.id,
toolName=tool.name,
functionName=tool.function_name,
toolType=tool.type,
)
try:
result = await self._tools.execute(tool, dict(args or {}))
except ToolExecutionError as exc:
await self._emit_trace(
"tool_failed",
nodeId=node_id,
invocationId=invocation_id,
toolId=tool.id,
toolName=tool.name,
functionName=tool.function_name,
toolType=tool.type,
status="error",
durationMs=max(0, round((monotonic() - started_at) * 1000)),
error=str(exc)[:2048],
)
return {"status": "error", "message": str(exc)}
await self._emit_trace(
"tool_completed" if result.get("status") == "ok" else "tool_failed",
nodeId=node_id,
invocationId=invocation_id,
toolId=tool.id,
toolName=tool.name,
functionName=tool.function_name,
toolType=tool.type,
status=str(result.get("status") or "unknown"),
durationMs=max(0, round((monotonic() - started_at) * 1000)),
updatedVariables=list(result.get("updated_variables") or []),
resultKeys=sorted(str(name) for name in result if name != "data"),
)
if (
self._state.current_node_id != node_id
or self._state.transition_id != transition_id