diff --git a/bilibili_page.html b/bilibili_page.html
new file mode 100644
index 0000000..d3cd089
--- /dev/null
+++ b/bilibili_page.html
@@ -0,0 +1,344 @@
+
+
diff --git a/src/web/server.py b/src/web/server.py
index 840c7b9..465886f 100644
--- a/src/web/server.py
+++ b/src/web/server.py
@@ -9,7 +9,6 @@
from __future__ import annotations
import base64
-import json
import mimetypes
import os
import shutil
@@ -325,11 +324,13 @@ def api_clear_session(session_id: str = Form(...)) -> dict[str, Any]:
{"sessionId": str, "sessions": list[str], "messages": []}
系统定位:
- 前端“清空对话”按钮调用。
+ 前端"清空对话"按钮调用。
"""
if not session_id:
raise HTTPException(status_code=400, detail="缺少 session_id")
messages, _ = ui.clear_ui_and_manifest(session_id)
+ # 清理 tool_calling 目录
+ _clear_tool_calling(session_id)
ids = ui.list_session_ids_ordered()
if session_id not in ids:
ids = [session_id, *ids]
@@ -352,6 +353,7 @@ def api_delete_session(session_id: str = Form(...)) -> dict[str, Any]:
if not session_id:
raise HTTPException(status_code=400, detail="缺少 session_id")
ui.delete_session(session_id)
+ _clear_tool_calling(session_id)
sid, ids, hist = _bootstrap()
return {"sessionId": sid, "sessions": ids, "messages": _serialize_messages(hist)}
@@ -433,8 +435,8 @@ def _chat_start_core(session_id: str, query: dict[str, Any]) -> dict[str, Any]:
"""
hist = ui.load_ui_messages_for_session(session_id)
before = len(hist or [])
- new_hist, cleared = ui.add_message(hist or [], query)
- if cleared is None:
+ new_hist = ui.add_message(hist or [], query)
+ if new_hist is None:
raise HTTPException(status_code=400, detail="请输入文字或上传图片/音频")
if len(new_hist) == before:
raise HTTPException(status_code=400, detail="请输入文字或上传图片/音频")
@@ -547,43 +549,7 @@ def api_chat_complete(
return _chat_complete_core(session_id, output_type=output_type, agent_mode=agent_mode)
-@app.post("/api/chat")
-async def api_chat(
- session_id: str = Form(...),
- text: str = Form(""),
- images: list[UploadFile] | None = File(None),
- files: list[UploadFile] | None = File(None),
- audio: UploadFile | None = File(None),
- output_type: str = Form("text"),
- agent_mode: str = Form("agent"),
-) -> dict[str, Any]:
- """
- 兼容旧客户端:等价于依次 ``/api/chat/start`` + ``/api/chat/complete``(仍阻塞至推理结束)。
- 输入:
- session_id: 表单字段,会话 ID。
- text: 用户输入的文本。
- images: 上传的图片文件列表。
- files: 上传的普通文件列表。
- audio: 上传的音频文件。
- output_type: text | audio | both。
- agent_mode: agent | plan | team。
-
- 输出:
- {"sessionId": str, "sessions": list[str], "messages": list[dict]}
-
- 系统定位:
- 向后兼容的一次性对话接口。
- """
- if not session_id:
- raise HTTPException(status_code=400, detail="缺少 session_id")
-
- query, tmp_root = _materialize_uploads_to_temp(images, files, audio, text)
- try:
- _chat_start_core(session_id, query)
- return _chat_complete_core(session_id, output_type=output_type, agent_mode=agent_mode)
- finally:
- shutil.rmtree(tmp_root, ignore_errors=True)
@app.post("/api/human-action")
@@ -635,15 +601,16 @@ def api_get_agent_configs() -> dict[str, Any]:
输出: {"agents": list[dict], "config_path": str}
"""
+ from agent.core.config_manager import load_models
configs = load_agent_configs()
+ models = {m["id"]: m for m in load_models()}
return {
"agents": [{"name": c.name, "system_prompt": c.system_prompt, "max_iterations": c.max_iterations,
- "llm": {"model": c.llm.model, "api_key": c.llm.api_key, "base_url": c.llm.base_url,
- "timeout": c.llm.timeout, "max_retries": c.llm.max_retries},
"llm_model_id": c.llm_model_id,
"tools": c.tools,
"enabled": c.enabled,
"role": c.role} for c in configs],
+ "models": list(models.values()),
"config_path": str(AGENT_CONFIG_PATH),
}
@@ -795,70 +762,10 @@ def api_reload_configs() -> dict[str, Any]:
return {"success": False, "agent": {}, "detail": str(e)}
-# ---------- 多 Agent 图面板 API ----------
-@app.get("/api/graph/snapshot")
-def api_get_graph_snapshot(session_id: str = "") -> dict[str, Any]:
- """获取当前多 Agent 运行时的图结构快照,供前端 graph 面板渲染。"""
- try:
- from agent.agents.multi_agent_runtime import get_agent_graph_snapshot, build_multi_agent_runtime
- from agent.agents.main_agent import main_agent_runtime
- from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn
- runtime = build_multi_agent_runtime(main_runtime=main_agent_runtime)
- result = get_agent_graph_snapshot(runtime)
- if session_id:
- recorded = get_tool_calls_for_latest_turn(session_id)
- if recorded:
- existing = result.get("tool_calls", {}) or {}
- main_calls = existing.get("main", [])
- for r in recorded:
- main_calls.append({
- "tool_name": r.get("name", ""),
- "tool_args": {},
- "result_preview": r.get("result_text", "")[:80],
- "status": r.get("status", "done"),
- })
- result["tool_calls"] = {**existing, "main": main_calls}
- return result
- except Exception as e:
- return {
- "agents": [
- {"name": "main", "role": "main", "icon": "🧠", "status": "idle", "error_message": ""}
- ],
- "edges": [],
- "events": [],
- "tool_calls": {},
- "error": str(e),
- }
-
-
-@app.get("/api/graph/config")
-def api_get_graph_config() -> dict[str, Any]:
- """获取 graph 面板的初始配置(从 agent_config.json 读取各 agent 的 role/icon 等)。
-
- 输出:
- {"agents": [{"name": str, "role": str, "icon": str}, ...]}
- """
- from agent.core.config_manager import load_agent_configs
- configs = load_agent_configs()
- role_icons = {"main": "🧠", "code": "💻", "research": "🔍", "doc": "📄", "gui": "🖥"}
- agents_list = []
- for cfg in configs:
- if cfg.enabled:
- agents_list.append({
- "name": cfg.name,
- "role": cfg.role,
- "icon": role_icons.get(cfg.name, "🔧"),
- "tools": cfg.tools,
- })
- if not agents_list:
- agents_list = [{"name": "main", "role": "main", "icon": "🧠", "tools": []}]
- return {"agents": agents_list}
-
-
# ---------- 工具调用历史 API ----------
@app.get("/api/tool-calls/{session_id}/live")
def api_get_live_tool_calls(session_id: str) -> dict[str, Any]:
- """获取当前进行中的工具调用(图表执行期间实时轮询)。"""
+ """获取当前进行中的工具调用(前端执行期间实时轮询)。"""
try:
from agent.history.tool_call_recorder import get_live_tool_calls
return {"tool_calls": get_live_tool_calls(session_id)}
@@ -866,61 +773,35 @@ def api_get_live_tool_calls(session_id: str) -> dict[str, Any]:
return {"tool_calls": [], "error": str(e)}
-@app.get("/api/tool-calls/{session_id}/recent")
-def api_get_recent_tool_calls(session_id: str) -> dict[str, Any]:
- """获取最新 turn 的工具调用缩略(仅名称,无返回值)。"""
+# ---------- Todo List 实时推送 ----------
+# 数据直接从 agent.tools.tool 的共享 store 读取,无需额外存储
+
+@app.get("/api/todo-list/{session_id}")
+def api_get_todo_list(session_id: str) -> dict[str, Any]:
+ """获取当前会话最新的 todo list 数据(前端轮询)。"""
try:
- from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn
- calls = get_tool_calls_for_latest_turn(session_id)
- result = []
- for c in calls:
- result.append({
- "name": c.get("name", ""),
- "args_summary": json.dumps(c.get("args", {}), ensure_ascii=False)[:40],
- "status": "done",
- "turn_id": c.get("turn_id", ""),
- })
- return {"tool_calls": result}
- except Exception as e:
- return {"tool_calls": [], "error": str(e)}
+ from agent.tools.tool import get_todo_store_data
+ data = get_todo_store_data(session_id)
+ except Exception:
+ data = None
+ if data:
+ return {"has_todo": True, "data": data}
+ return {"has_todo": False, "data": None}
-@app.get("/api/tool-calls/{session_id}/detail")
-def api_get_tool_call_detail(session_id: str) -> dict[str, Any]:
- """获取最新 turn 的工具调用详情(含返回值和图片 base64)。"""
+def _clear_tool_calling(session_id: str) -> None:
+ """清理会话关联的 tool_calling 目录与 todo_list 缓存。"""
try:
- from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn
- details = get_tool_calls_for_latest_turn(session_id)
- # 图片文件转 data URL
- import base64
- for d in details:
- imgs = []
- for fp in d.get("result_files_full", []):
- if os.path.isfile(fp):
- with open(fp, "rb") as f:
- b64 = base64.standard_b64encode(f.read()).decode("ascii")
- ext = os.path.splitext(fp)[1].lower()
- mime_map = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif"}
- mime = mime_map.get(ext, "image/png")
- imgs.append(f"data:{mime};base64,{b64}")
- d["result_images"] = imgs
- return {"details": details}
- except Exception as e:
- return {"details": [], "error": str(e)}
-
-
-@app.get("/api/tool-calls/{session_id}/has-pending")
-def api_has_pending_tool_calls(session_id: str) -> dict[str, Any]:
- """检查是否有待处理的工具调用(thinking 状态时使用)。"""
+ from agent.tools.tool import clear_todo_store_session
+ clear_todo_store_session(session_id)
+ except Exception:
+ pass
try:
- from agent.history.tool_call_recorder import list_turns
- turns = list_turns(session_id)
- has_tools = bool(turns)
- # 判断最新 turn 是否已完成(有 assistant_response)
- latest_complete = turns[0].get("assistant_response", "") if turns else ""
- return {"has_recent": has_tools, "is_complete": bool(latest_complete)}
- except Exception as e:
- return {"has_recent": False, "is_complete": True, "error": str(e)}
+ import shutil as _shutil
+ from agent.history.tool_call_recorder import session_tool_dir
+ _shutil.rmtree(str(session_tool_dir(session_id)), ignore_errors=True)
+ except Exception:
+ pass
@app.get("/favicon.ico")
diff --git a/src/web/ui_session.py b/src/web/ui_session.py
index 099618f..bc17a67 100644
--- a/src/web/ui_session.py
+++ b/src/web/ui_session.py
@@ -142,6 +142,12 @@ def delete_session(session_id: str) -> None:
conn.close()
_memory_session_ids.discard(session_id)
shutil.rmtree(session_dir(session_id), ignore_errors=True)
+ # 清理 tool_calling 目录
+ try:
+ from agent.history.tool_call_recorder import session_tool_dir
+ shutil.rmtree(str(session_tool_dir(session_id)), ignore_errors=True)
+ except Exception:
+ pass
def _lc_content_for_ui_messages(content) -> str | list:
@@ -701,8 +707,7 @@ def add_message(chat_history, query):
query: {"text": str, "files": list[str], "file_names": list[str]}
输出:
- (new_chat_history, cleared_query) 元组;
- cleared_query 在旧 API 兼容中保留,新调用可忽略。
+ 成功返回 new_chat_history,无有效内容返回 None。
系统定位:
API 层构造用户消息的核心函数。
@@ -710,9 +715,9 @@ def add_message(chat_history, query):
chat_history = chat_history or []
user_content = _build_user_chat_content(query)
if user_content is None:
- return chat_history, None
+ return None
chat_history.append({"role": "user", "content": user_content})
- return chat_history, {"text": "", "files": []}
+ return chat_history
def clear_ui_and_manifest(session_id: str):