openclaw-agent-dashboard 1.0.35 → 1.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/api/agents.py +3 -29
- package/dashboard/api/timeline.py +18 -1
- package/dashboard/data/config_reader.py +4 -1
- package/dashboard/data/timeline_reader.py +360 -169
- package/frontend-dist/assets/index-DoK53sgy.css +1 -0
- package/frontend-dist/assets/index-Drb7GQ4m.js +24 -0
- package/frontend-dist/index.html +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/frontend-dist/assets/index-BkM6T7k-.js +0 -24
- package/frontend-dist/assets/index-CFl9xHR7.css +0 -1
package/dashboard/api/agents.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Agent API 路由
|
|
3
3
|
"""
|
|
4
|
-
from fastapi import APIRouter
|
|
4
|
+
from fastapi import APIRouter
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
from typing import List, Optional
|
|
7
7
|
import sys
|
|
@@ -51,37 +51,10 @@ async def get_agent(agent_id: str):
|
|
|
51
51
|
agent['lastActiveFormatted'] = format_last_active(agent['lastActiveAt'])
|
|
52
52
|
return agent
|
|
53
53
|
|
|
54
|
+
from fastapi import HTTPException
|
|
54
55
|
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
class TimelineContextResponse(BaseModel):
|
|
58
|
-
"""供「实时执行时序」与 runs.json 对齐:使用最新 run 的 childSessionKey 解析独立会话。"""
|
|
59
|
-
childSessionKey: Optional[str] = None
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@router.get("/agents/{agent_id}/timeline-context", response_model=TimelineContextResponse)
|
|
63
|
-
async def get_agent_timeline_context(agent_id: str):
|
|
64
|
-
"""
|
|
65
|
-
返回该 Agent 在 runs.json 中最近一条 run 的 childSessionKey(若有)。
|
|
66
|
-
前端传给 GET /api/timeline/{agent_id}?session_key= 以命中 sessions.json 指定 jsonl,
|
|
67
|
-
避免仅靠 mtime 选文件或误扫主会话合并路径。
|
|
68
|
-
"""
|
|
69
|
-
from data.config_reader import get_agents_list
|
|
70
|
-
from data.subagent_reader import get_agent_runs
|
|
71
|
-
|
|
72
|
-
agents = get_agents_list()
|
|
73
|
-
if not any(a.get("id") == agent_id for a in agents):
|
|
74
|
-
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
75
|
-
|
|
76
|
-
runs = get_agent_runs(agent_id, limit=1)
|
|
77
|
-
key = None
|
|
78
|
-
if runs:
|
|
79
|
-
key = runs[0].get("childSessionKey") or None
|
|
80
|
-
if isinstance(key, str) and not key.strip():
|
|
81
|
-
key = None
|
|
82
|
-
return TimelineContextResponse(childSessionKey=key)
|
|
83
|
-
|
|
84
|
-
|
|
85
58
|
@router.get("/agents/{agent_id}/output")
|
|
86
59
|
async def get_agent_output(agent_id: str, limit: int = 50):
|
|
87
60
|
"""
|
|
@@ -93,6 +66,7 @@ async def get_agent_output(agent_id: str, limit: int = 50):
|
|
|
93
66
|
|
|
94
67
|
agents = get_agents_list()
|
|
95
68
|
if not any(a.get('id') == agent_id for a in agents):
|
|
69
|
+
from fastapi import HTTPException
|
|
96
70
|
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
97
71
|
|
|
98
72
|
turns = get_session_turns(agent_id, limit=limit)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Timeline API 路由 - 实时执行时序图
|
|
3
3
|
"""
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
4
6
|
from fastapi import APIRouter, Query, HTTPException
|
|
5
7
|
from pydantic import BaseModel
|
|
6
8
|
from typing import Optional, List, Dict, Any
|
|
7
9
|
import sys
|
|
8
10
|
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
LOG = logging.getLogger(__name__)
|
|
9
13
|
sys.path.append(str(Path(__file__).parent.parent))
|
|
10
14
|
|
|
11
15
|
from data.timeline_reader import get_timeline_steps, StepType, StepStatus
|
|
@@ -38,13 +42,17 @@ class TimelineResponse(BaseModel):
|
|
|
38
42
|
agentName: Optional[str] = None
|
|
39
43
|
model: Optional[str] = None
|
|
40
44
|
startedAt: Optional[int] = None
|
|
45
|
+
runStartedAt: Optional[int] = None
|
|
41
46
|
status: str
|
|
42
47
|
steps: List[Dict[str, Any]]
|
|
43
48
|
stats: TimelineStats
|
|
44
49
|
message: Optional[str] = None
|
|
50
|
+
# 主 Agent 无会话文件时由后端置 True,避免前端误用「子代理」空态文案
|
|
51
|
+
isMainAgent: Optional[bool] = None
|
|
45
52
|
# LLM 轮次分组
|
|
46
53
|
rounds: Optional[List[LLMRound]] = None
|
|
47
54
|
roundMode: Optional[bool] = None
|
|
55
|
+
dataSource: Optional[str] = None
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
@router.get("/timeline/{agent_id}", response_model=TimelineResponse)
|
|
@@ -73,8 +81,17 @@ async def get_timeline(
|
|
|
73
81
|
if not agent_info:
|
|
74
82
|
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
t0 = time.perf_counter()
|
|
77
85
|
result = get_timeline_steps(agent_id, session_key, limit)
|
|
86
|
+
elapsed_ms = (time.perf_counter() - t0) * 1000
|
|
87
|
+
if elapsed_ms >= 200.0:
|
|
88
|
+
LOG.info(
|
|
89
|
+
"timeline agent=%s limit=%d steps=%d ms=%.1f",
|
|
90
|
+
agent_id,
|
|
91
|
+
limit,
|
|
92
|
+
len(result.get("steps", [])),
|
|
93
|
+
elapsed_ms,
|
|
94
|
+
)
|
|
78
95
|
|
|
79
96
|
# 补充 Agent 信息
|
|
80
97
|
result['agentName'] = agent_info.get('name', agent_id)
|
|
@@ -52,8 +52,11 @@ def get_agents_list() -> List[Dict[str, Any]]:
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
def get_main_agent_id() -> str:
|
|
55
|
-
"""获取主 Agent ID
|
|
55
|
+
"""获取主 Agent ID:优先 default:true,其次 id 为 main,否则列表第一项。"""
|
|
56
56
|
agents = get_agents_list()
|
|
57
|
+
for a in agents:
|
|
58
|
+
if a.get('default') is True:
|
|
59
|
+
return a.get('id', 'main')
|
|
57
60
|
for a in agents:
|
|
58
61
|
if a.get('id') == 'main':
|
|
59
62
|
return 'main'
|