feat: restore risk assessment workspace and improve page scrolling
This commit is contained in:
+150
-1
@@ -3,6 +3,7 @@
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from fastapi import APIRouter
|
||||
@@ -80,7 +81,7 @@ async def event_generator(message: str, session_id: str) -> AsyncGenerator[dict,
|
||||
segments: list[dict] = [] # finalized segments
|
||||
current_segment: dict = {"content": "", "tool_calls": []}
|
||||
|
||||
async for event in agent_manager.astream(message, history):
|
||||
async for event in agent_manager.astream(message, history, session_id):
|
||||
event_type = event.get("type", "unknown")
|
||||
|
||||
if event_type == "retrieval":
|
||||
@@ -186,3 +187,151 @@ async def chat(request: ChatRequest):
|
||||
# Non-streaming fallback
|
||||
result = await agent_manager.ainvoke(request.message, request.session_id)
|
||||
return {"reply": result}
|
||||
|
||||
|
||||
@router.get("/chat/raw-traces")
|
||||
async def get_raw_traces(session_id: str = "default", limit: int = 20):
|
||||
"""获取指定 session 的原始 LLM 调用报文
|
||||
|
||||
Args:
|
||||
session_id: 会话 ID
|
||||
limit: 返回最近 N 条记录 (默认 20)
|
||||
|
||||
Returns:
|
||||
{"traces": [...]} 原始报文列表,按时间倒序
|
||||
"""
|
||||
try:
|
||||
from graph.raw_trace import load_raw_traces
|
||||
|
||||
# 从 agent_manager 获取 raw_logs_dir
|
||||
raw_logs_dir = agent_manager._raw_logs_dir
|
||||
if not raw_logs_dir:
|
||||
return {"traces": []}
|
||||
|
||||
traces = load_raw_traces(session_id, raw_logs_dir, limit=limit)
|
||||
return {"traces": traces}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return {"traces": [], "error": str(e)}
|
||||
|
||||
|
||||
@router.get("/chat/trajectory")
|
||||
async def get_trajectory(session_id: str = "default"):
|
||||
"""获取会话完整交互轨迹 - 包含完整的 human/assistant/tool 消息流
|
||||
|
||||
Args:
|
||||
session_id: 会话 ID
|
||||
|
||||
Returns:
|
||||
{
|
||||
"session_id": "...",
|
||||
"messages": [
|
||||
{"role": "human", "content": "...", "timestamp": ...},
|
||||
{"role": "assistant", "content": "...", "timestamp": ..., "tool_calls": [...]},
|
||||
{"role": "tool", "name": "...", "content": "...", "timestamp": ...},
|
||||
{"role": "assistant", "content": "...", "timestamp": ...}
|
||||
],
|
||||
"steps": [...] // 保留旧格式用于向后兼容
|
||||
}
|
||||
"""
|
||||
try:
|
||||
import json
|
||||
from graph.raw_trace import load_raw_traces
|
||||
|
||||
raw_logs_dir = agent_manager._raw_logs_dir
|
||||
if not raw_logs_dir:
|
||||
# 无 raw_logs_dir,返回空
|
||||
return {"session_id": session_id, "messages": [], "steps": []}
|
||||
|
||||
# 优先读取完整消息流日志文件
|
||||
msgs_log_file = raw_logs_dir / f"{session_id}.msgs.jsonl"
|
||||
messages = []
|
||||
|
||||
if msgs_log_file.exists():
|
||||
# 从 msgs.jsonl 读取完整消息流(包含 tool 消息)
|
||||
with open(msgs_log_file, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line:
|
||||
try:
|
||||
msg = json.loads(line)
|
||||
messages.append(msg)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
else:
|
||||
# 回退到旧逻辑:从 session 文件读取(无 tool 消息)
|
||||
session_messages = session_manager.load_session(session_id)
|
||||
for msg in session_messages:
|
||||
messages.append({
|
||||
"role": msg["role"],
|
||||
"content": msg["content"],
|
||||
"timestamp": None,
|
||||
})
|
||||
|
||||
# 构建旧格式 steps(向后兼容)
|
||||
steps = []
|
||||
traces = load_raw_traces(session_id, raw_logs_dir, limit=1000)
|
||||
traces.sort(key=lambda x: x.get("timestamp_start", 0))
|
||||
|
||||
# 按 user/assistant 消息构建 steps(忽略 tool 消息)
|
||||
session_messages = session_manager.load_session(session_id)
|
||||
assistant_count = sum(1 for m in session_messages if m["role"] == "assistant")
|
||||
|
||||
for msg_idx, msg in enumerate(session_messages):
|
||||
step = {
|
||||
"role": msg["role"],
|
||||
"content": msg["content"],
|
||||
"timestamp": None,
|
||||
}
|
||||
|
||||
if msg["role"] == "assistant":
|
||||
llm_calls = []
|
||||
assistant_seq = sum(1 for m in session_messages[:msg_idx+1] if m["role"] == "assistant")
|
||||
traces_per_assistant = len(traces) / max(assistant_count, 1)
|
||||
start_trace = int((assistant_seq - 1) * traces_per_assistant)
|
||||
end_trace = int(assistant_seq * traces_per_assistant)
|
||||
|
||||
for i in range(start_trace, min(end_trace, len(traces))):
|
||||
trace = traces[i]
|
||||
llm_call = {
|
||||
"model": trace.get("request", {}).get("model", "unknown"),
|
||||
"finish_reason": trace.get("finish_reason", "unknown"),
|
||||
"request_messages": trace.get("request", {}).get("messages", []),
|
||||
"response_text": "",
|
||||
"tool_calls_list": [],
|
||||
"usage": trace.get("usage", {}),
|
||||
"duration_ms": trace.get("duration_ms", 0),
|
||||
}
|
||||
|
||||
response_gens = trace.get("response", {}).get("generations", [])
|
||||
if response_gens:
|
||||
llm_call["response_text"] = response_gens[0].get("text", "")
|
||||
msg_data = response_gens[0].get("message", {})
|
||||
if isinstance(msg_data, dict) and "tool_calls" in msg_data:
|
||||
tool_calls = msg_data["tool_calls"]
|
||||
if tool_calls:
|
||||
llm_call["tool_calls_list"] = [
|
||||
{
|
||||
"name": tc.get("name", "") if isinstance(tc, dict) else getattr(tc, "name", ""),
|
||||
"args": tc.get("args", {}) if isinstance(tc, dict) else getattr(tc, "args", {}),
|
||||
}
|
||||
for tc in tool_calls
|
||||
]
|
||||
|
||||
llm_calls.append(llm_call)
|
||||
if step["timestamp"] is None:
|
||||
step["timestamp"] = trace.get("timestamp_start")
|
||||
|
||||
step["llm_calls"] = llm_calls
|
||||
|
||||
steps.append(step)
|
||||
|
||||
return {
|
||||
"session_id": session_id,
|
||||
"messages": messages, # 新格式:完整消息流(含 tool)
|
||||
"steps": steps, # 旧格式:向后兼容
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return {"session_id": session_id, "messages": [], "steps": [], "error": str(e)}
|
||||
|
||||
Reference in New Issue
Block a user