feat: add workflow action outcomes and tracing
This commit is contained in:
@@ -5,6 +5,7 @@ conversation_artifacts,并把对象存储地址关联到会话或消息。
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from copy import deepcopy
|
||||
from datetime import UTC, datetime
|
||||
from uuid import uuid4
|
||||
|
||||
@@ -13,6 +14,9 @@ from db.session import SessionLocal
|
||||
from loguru import logger
|
||||
|
||||
|
||||
MAX_WORKFLOW_TRACE_EVENTS = 1000
|
||||
|
||||
|
||||
def _parse_timestamp(value: object) -> datetime:
|
||||
if not isinstance(value, str) or not value:
|
||||
return datetime.now(UTC)
|
||||
@@ -29,6 +33,7 @@ class ConversationRecorder:
|
||||
def __init__(self, session_id: str):
|
||||
self.session_id = session_id
|
||||
self._sequence = 0
|
||||
self._trace_sequence = 0
|
||||
self._lock = asyncio.Lock()
|
||||
self._seen_events: set[str] = set()
|
||||
|
||||
@@ -41,6 +46,7 @@ class ConversationRecorder:
|
||||
channel: str,
|
||||
runtime_mode: str,
|
||||
session_id: str | None = None,
|
||||
extra: dict | None = None,
|
||||
) -> "ConversationRecorder | None":
|
||||
session_id = session_id or f"conv_{uuid4().hex[:20]}"
|
||||
try:
|
||||
@@ -54,7 +60,7 @@ class ConversationRecorder:
|
||||
runtime_mode=runtime_mode,
|
||||
status="active",
|
||||
message_count=0,
|
||||
extra={},
|
||||
extra=deepcopy(extra or {}),
|
||||
)
|
||||
)
|
||||
await db.commit()
|
||||
@@ -67,6 +73,9 @@ class ConversationRecorder:
|
||||
if not isinstance(message, dict):
|
||||
return
|
||||
event_type = message.get("type")
|
||||
if event_type == "workflow-event":
|
||||
await self._append_workflow_event(message)
|
||||
return
|
||||
role = ""
|
||||
content = ""
|
||||
extra: dict = {}
|
||||
@@ -98,6 +107,36 @@ class ConversationRecorder:
|
||||
self._seen_events.add(event_key)
|
||||
await self._append(role, content, timestamp, extra)
|
||||
|
||||
async def _append_workflow_event(self, message: dict) -> None:
|
||||
"""Persist bounded control-plane history without creating chat rows."""
|
||||
|
||||
event_id = str(message.get("eventId") or "")
|
||||
event_name = str(message.get("event") or "")
|
||||
event_key = f"workflow:{event_id}"
|
||||
if not event_id or not event_name or event_key in self._seen_events:
|
||||
return
|
||||
self._seen_events.add(event_key)
|
||||
|
||||
async with self._lock:
|
||||
next_sequence = self._trace_sequence + 1
|
||||
payload = deepcopy(message)
|
||||
payload["sessionId"] = self.session_id
|
||||
payload["sequence"] = next_sequence
|
||||
try:
|
||||
async with SessionLocal() as db:
|
||||
conversation = await db.get(ConversationSession, self.session_id)
|
||||
if not conversation:
|
||||
return
|
||||
extra = dict(conversation.extra or {})
|
||||
trace = list(extra.get("workflowTrace") or [])
|
||||
trace.append(payload)
|
||||
extra["workflowTrace"] = trace[-MAX_WORKFLOW_TRACE_EVENTS:]
|
||||
conversation.extra = extra
|
||||
await db.commit()
|
||||
self._trace_sequence = next_sequence
|
||||
except Exception as exc:
|
||||
logger.error(f"保存 Workflow 轨迹失败,不影响本次通话: {exc}")
|
||||
|
||||
async def _append(
|
||||
self,
|
||||
role: str,
|
||||
@@ -130,13 +169,14 @@ class ConversationRecorder:
|
||||
logger.error(f"保存对话文本失败,不影响本次通话: {exc}")
|
||||
|
||||
async def finish(self, *, status: str = "completed") -> None:
|
||||
try:
|
||||
async with SessionLocal() as db:
|
||||
conversation = await db.get(ConversationSession, self.session_id)
|
||||
if conversation:
|
||||
conversation.status = status
|
||||
conversation.ended_at = datetime.now(UTC)
|
||||
conversation.message_count = self._sequence
|
||||
await db.commit()
|
||||
except Exception as exc:
|
||||
logger.error(f"结束对话历史会话失败: {exc}")
|
||||
async with self._lock:
|
||||
try:
|
||||
async with SessionLocal() as db:
|
||||
conversation = await db.get(ConversationSession, self.session_id)
|
||||
if conversation:
|
||||
conversation.status = status
|
||||
conversation.ended_at = datetime.now(UTC)
|
||||
conversation.message_count = self._sequence
|
||||
await db.commit()
|
||||
except Exception as exc:
|
||||
logger.error(f"结束对话历史会话失败: {exc}")
|
||||
|
||||
Reference in New Issue
Block a user