@humanclaw/humanclaw 1.2.8 → 2.0.1
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/README.md +79 -49
- package/README_EN.md +81 -51
- package/dist/index.js +974 -63
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -72,6 +72,37 @@ function initSchema(db2) {
|
|
|
72
72
|
key TEXT PRIMARY KEY,
|
|
73
73
|
value TEXT NOT NULL
|
|
74
74
|
);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS teams (
|
|
77
|
+
team_id TEXT PRIMARY KEY,
|
|
78
|
+
name TEXT NOT NULL,
|
|
79
|
+
description TEXT NOT NULL DEFAULT '',
|
|
80
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
CREATE TABLE IF NOT EXISTS team_members (
|
|
84
|
+
team_id TEXT NOT NULL REFERENCES teams(team_id) ON DELETE CASCADE,
|
|
85
|
+
agent_id TEXT NOT NULL REFERENCES agents(agent_id) ON DELETE CASCADE,
|
|
86
|
+
relationship TEXT NOT NULL DEFAULT '',
|
|
87
|
+
PRIMARY KEY (team_id, agent_id)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_team_members_agent ON team_members(agent_id);
|
|
91
|
+
|
|
92
|
+
CREATE TABLE IF NOT EXISTS evaluations (
|
|
93
|
+
eval_id TEXT PRIMARY KEY,
|
|
94
|
+
job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,
|
|
95
|
+
agent_id TEXT NOT NULL REFERENCES agents(agent_id) ON DELETE CASCADE,
|
|
96
|
+
trace_id TEXT NOT NULL REFERENCES tasks(trace_id) ON DELETE CASCADE,
|
|
97
|
+
rating_system TEXT NOT NULL CHECK (rating_system IN ('ali', 'letter', 'em')),
|
|
98
|
+
rating TEXT NOT NULL,
|
|
99
|
+
weight REAL NOT NULL DEFAULT 1.0,
|
|
100
|
+
comment TEXT NOT NULL DEFAULT '',
|
|
101
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
CREATE INDEX IF NOT EXISTS idx_evaluations_job ON evaluations(job_id);
|
|
105
|
+
CREATE INDEX IF NOT EXISTS idx_evaluations_agent ON evaluations(agent_id);
|
|
75
106
|
`);
|
|
76
107
|
const cols = db2.prepare("PRAGMA table_info(agents)").all();
|
|
77
108
|
if (!cols.some((c) => c.name === "relationship")) {
|
|
@@ -125,6 +156,19 @@ function deleteAgent(agentId, db2) {
|
|
|
125
156
|
const result = conn.prepare("DELETE FROM agents WHERE agent_id = ?").run(agentId);
|
|
126
157
|
return result.changes > 0;
|
|
127
158
|
}
|
|
159
|
+
function getAgentWithTeams(agentId, db2) {
|
|
160
|
+
const conn = db2 ?? getDb();
|
|
161
|
+
const agent = getAgent(agentId, conn);
|
|
162
|
+
if (!agent) return void 0;
|
|
163
|
+
const teams = conn.prepare(
|
|
164
|
+
`SELECT t.team_id, t.name AS team_name, tm.relationship
|
|
165
|
+
FROM team_members tm
|
|
166
|
+
JOIN teams t ON tm.team_id = t.team_id
|
|
167
|
+
WHERE tm.agent_id = ?
|
|
168
|
+
ORDER BY t.created_at`
|
|
169
|
+
).all(agentId);
|
|
170
|
+
return { ...agent, teams };
|
|
171
|
+
}
|
|
128
172
|
function listAgentsWithMetrics(db2) {
|
|
129
173
|
const conn = db2 ?? getDb();
|
|
130
174
|
const rows = conn.prepare(
|
|
@@ -203,6 +247,14 @@ router.patch("/:agent_id/status", (req, res) => {
|
|
|
203
247
|
}
|
|
204
248
|
res.json({ agent_id, status });
|
|
205
249
|
});
|
|
250
|
+
router.get("/:agent_id", (req, res) => {
|
|
251
|
+
const agent = getAgentWithTeams(req.params.agent_id);
|
|
252
|
+
if (!agent) {
|
|
253
|
+
res.status(404).json({ error: `Agent not found: ${req.params.agent_id}` });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
res.json(agent);
|
|
257
|
+
});
|
|
206
258
|
router.delete("/:agent_id", (req, res) => {
|
|
207
259
|
const { agent_id } = req.params;
|
|
208
260
|
const deleted = deleteAgent(agent_id);
|
|
@@ -378,6 +430,70 @@ function dispatchJob(request, db2) {
|
|
|
378
430
|
return { ...job, tasks };
|
|
379
431
|
}
|
|
380
432
|
|
|
433
|
+
// src/models/team.ts
|
|
434
|
+
function createTeam(team, db2) {
|
|
435
|
+
const conn = db2 ?? getDb();
|
|
436
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
437
|
+
conn.prepare(
|
|
438
|
+
`INSERT INTO teams (team_id, name, description, created_at)
|
|
439
|
+
VALUES (?, ?, ?, ?)`
|
|
440
|
+
).run(team.team_id, team.name, team.description || "", now);
|
|
441
|
+
return { ...team, description: team.description || "", created_at: now };
|
|
442
|
+
}
|
|
443
|
+
function getTeam(teamId, db2) {
|
|
444
|
+
const conn = db2 ?? getDb();
|
|
445
|
+
const row = conn.prepare("SELECT * FROM teams WHERE team_id = ?").get(teamId);
|
|
446
|
+
return row || void 0;
|
|
447
|
+
}
|
|
448
|
+
function listTeams(db2) {
|
|
449
|
+
const conn = db2 ?? getDb();
|
|
450
|
+
return conn.prepare("SELECT * FROM teams ORDER BY created_at").all();
|
|
451
|
+
}
|
|
452
|
+
function deleteTeam(teamId, db2) {
|
|
453
|
+
const conn = db2 ?? getDb();
|
|
454
|
+
const result = conn.prepare("DELETE FROM teams WHERE team_id = ?").run(teamId);
|
|
455
|
+
return result.changes > 0;
|
|
456
|
+
}
|
|
457
|
+
function addTeamMember(teamId, agentId, relationship, db2) {
|
|
458
|
+
const conn = db2 ?? getDb();
|
|
459
|
+
conn.prepare(
|
|
460
|
+
`INSERT OR REPLACE INTO team_members (team_id, agent_id, relationship)
|
|
461
|
+
VALUES (?, ?, ?)`
|
|
462
|
+
).run(teamId, agentId, relationship || "");
|
|
463
|
+
}
|
|
464
|
+
function removeTeamMember(teamId, agentId, db2) {
|
|
465
|
+
const conn = db2 ?? getDb();
|
|
466
|
+
const result = conn.prepare("DELETE FROM team_members WHERE team_id = ? AND agent_id = ?").run(teamId, agentId);
|
|
467
|
+
return result.changes > 0;
|
|
468
|
+
}
|
|
469
|
+
function updateTeamMemberRelationship(teamId, agentId, relationship, db2) {
|
|
470
|
+
const conn = db2 ?? getDb();
|
|
471
|
+
const result = conn.prepare(
|
|
472
|
+
"UPDATE team_members SET relationship = ? WHERE team_id = ? AND agent_id = ?"
|
|
473
|
+
).run(relationship, teamId, agentId);
|
|
474
|
+
return result.changes > 0;
|
|
475
|
+
}
|
|
476
|
+
function getTeamWithMembers(teamId, db2) {
|
|
477
|
+
const conn = db2 ?? getDb();
|
|
478
|
+
const team = getTeam(teamId, conn);
|
|
479
|
+
if (!team) return void 0;
|
|
480
|
+
const members = conn.prepare(
|
|
481
|
+
`SELECT tm.team_id, tm.agent_id, tm.relationship, a.name AS agent_name, a.status AS agent_status
|
|
482
|
+
FROM team_members tm
|
|
483
|
+
JOIN agents a ON tm.agent_id = a.agent_id
|
|
484
|
+
WHERE tm.team_id = ?
|
|
485
|
+
ORDER BY a.created_at`
|
|
486
|
+
).all(teamId);
|
|
487
|
+
return { ...team, members };
|
|
488
|
+
}
|
|
489
|
+
function getTeamMemberRelationship(teamId, agentId, db2) {
|
|
490
|
+
const conn = db2 ?? getDb();
|
|
491
|
+
const row = conn.prepare(
|
|
492
|
+
"SELECT relationship FROM team_members WHERE team_id = ? AND agent_id = ?"
|
|
493
|
+
).get(teamId, agentId);
|
|
494
|
+
return row?.relationship;
|
|
495
|
+
}
|
|
496
|
+
|
|
381
497
|
// src/llm/claude.ts
|
|
382
498
|
var DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
383
499
|
var ClaudeProvider = class {
|
|
@@ -466,6 +582,62 @@ var OpenAIProvider = class {
|
|
|
466
582
|
}
|
|
467
583
|
};
|
|
468
584
|
|
|
585
|
+
// src/llm/responses.ts
|
|
586
|
+
var DEFAULT_BASE_URL3 = "https://api.openai.com";
|
|
587
|
+
var ResponsesProvider = class {
|
|
588
|
+
apiKey;
|
|
589
|
+
model;
|
|
590
|
+
baseUrl;
|
|
591
|
+
constructor(apiKey, model, baseUrl) {
|
|
592
|
+
this.apiKey = apiKey;
|
|
593
|
+
this.model = model || "gpt-4o";
|
|
594
|
+
this.baseUrl = (baseUrl || DEFAULT_BASE_URL3).replace(/\/+$/, "");
|
|
595
|
+
}
|
|
596
|
+
async complete(request) {
|
|
597
|
+
const systemMessages = request.messages.filter((m) => m.role === "system");
|
|
598
|
+
const nonSystemMessages = request.messages.filter((m) => m.role !== "system");
|
|
599
|
+
const input = nonSystemMessages.map((m) => ({
|
|
600
|
+
role: m.role,
|
|
601
|
+
content: m.content
|
|
602
|
+
}));
|
|
603
|
+
const body = {
|
|
604
|
+
model: this.model,
|
|
605
|
+
input
|
|
606
|
+
};
|
|
607
|
+
if (systemMessages.length > 0) {
|
|
608
|
+
body.instructions = systemMessages.map((m) => m.content).join("\n\n");
|
|
609
|
+
}
|
|
610
|
+
if (request.temperature !== void 0) {
|
|
611
|
+
body.temperature = request.temperature;
|
|
612
|
+
}
|
|
613
|
+
if (request.max_tokens) {
|
|
614
|
+
body.max_output_tokens = request.max_tokens;
|
|
615
|
+
}
|
|
616
|
+
const response = await fetch(`${this.baseUrl}/v1/responses`, {
|
|
617
|
+
method: "POST",
|
|
618
|
+
headers: {
|
|
619
|
+
"Content-Type": "application/json",
|
|
620
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
621
|
+
},
|
|
622
|
+
body: JSON.stringify(body)
|
|
623
|
+
});
|
|
624
|
+
if (!response.ok) {
|
|
625
|
+
const errorText = await response.text();
|
|
626
|
+
throw new Error(`Responses API error (${response.status}): ${errorText}`);
|
|
627
|
+
}
|
|
628
|
+
const data = await response.json();
|
|
629
|
+
for (const item of data.output) {
|
|
630
|
+
if (item.type === "message" && item.content) {
|
|
631
|
+
const textBlock = item.content.find((c) => c.type === "output_text");
|
|
632
|
+
if (textBlock) {
|
|
633
|
+
return { content: textBlock.text };
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
throw new Error("Responses API returned no text content");
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
469
641
|
// src/models/config.ts
|
|
470
642
|
function getConfig(key, db2) {
|
|
471
643
|
const conn = db2 ?? getDb();
|
|
@@ -482,12 +654,17 @@ function deleteConfig(key, db2) {
|
|
|
482
654
|
}
|
|
483
655
|
|
|
484
656
|
// src/llm/index.ts
|
|
657
|
+
function normalizeProvider(raw) {
|
|
658
|
+
if (raw === "claude") return "anthropic";
|
|
659
|
+
return raw;
|
|
660
|
+
}
|
|
485
661
|
function getLlmConfig() {
|
|
486
662
|
const dbProvider = getConfig("llm_provider");
|
|
487
663
|
const dbApiKey = getConfig("llm_api_key");
|
|
488
664
|
const dbModel = getConfig("llm_model");
|
|
489
665
|
const dbBaseUrl = getConfig("llm_base_url");
|
|
490
|
-
const
|
|
666
|
+
const rawProvider = dbProvider || process.env.HUMANCLAW_LLM_PROVIDER || "anthropic";
|
|
667
|
+
const provider = normalizeProvider(rawProvider);
|
|
491
668
|
const apiKey = dbApiKey || process.env.HUMANCLAW_LLM_API_KEY || "";
|
|
492
669
|
const model = dbModel || process.env.HUMANCLAW_LLM_MODEL;
|
|
493
670
|
const baseUrl = dbBaseUrl || process.env.HUMANCLAW_LLM_BASE_URL;
|
|
@@ -496,18 +673,21 @@ function getLlmConfig() {
|
|
|
496
673
|
"\u672A\u914D\u7F6E LLM API Key\u3002\u8BF7\u5728 Dashboard \u8BBE\u7F6E\u4E2D\u914D\u7F6E\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF HUMANCLAW_LLM_API_KEY\u3002"
|
|
497
674
|
);
|
|
498
675
|
}
|
|
499
|
-
|
|
500
|
-
|
|
676
|
+
const validFormats = ["openai", "anthropic", "responses"];
|
|
677
|
+
if (!validFormats.includes(provider)) {
|
|
678
|
+
throw new Error(`\u4E0D\u652F\u6301\u7684 API \u683C\u5F0F: ${provider}\u3002\u652F\u6301: openai, anthropic, responses`);
|
|
501
679
|
}
|
|
502
680
|
return { provider, apiKey, model: model || void 0, baseUrl: baseUrl || void 0 };
|
|
503
681
|
}
|
|
504
682
|
function createLlmProvider(config) {
|
|
505
683
|
const cfg = config || getLlmConfig();
|
|
506
684
|
switch (cfg.provider) {
|
|
507
|
-
case "
|
|
685
|
+
case "anthropic":
|
|
508
686
|
return new ClaudeProvider(cfg.apiKey, cfg.model, cfg.baseUrl);
|
|
509
687
|
case "openai":
|
|
510
688
|
return new OpenAIProvider(cfg.apiKey, cfg.model, cfg.baseUrl);
|
|
689
|
+
case "responses":
|
|
690
|
+
return new ResponsesProvider(cfg.apiKey, cfg.model, cfg.baseUrl);
|
|
511
691
|
}
|
|
512
692
|
}
|
|
513
693
|
|
|
@@ -536,16 +716,23 @@ function buildSystemPrompt() {
|
|
|
536
716
|
]
|
|
537
717
|
\`\`\``;
|
|
538
718
|
}
|
|
539
|
-
function buildUserPrompt(prompt, agents) {
|
|
719
|
+
function buildUserPrompt(prompt, agents, teamContext) {
|
|
540
720
|
const now = /* @__PURE__ */ new Date();
|
|
541
721
|
const agentList = agents.map((a) => {
|
|
542
722
|
const load = a.active_task_count > 0 ? `\u5F53\u524D\u6709 ${a.active_task_count} \u4E2A\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1` : "\u5F53\u524D\u7A7A\u95F2";
|
|
543
723
|
const speed = a.avg_delivery_hours !== null ? `\u5E73\u5747\u4EA4\u4ED8\u65F6\u95F4 ${a.avg_delivery_hours}h` : "\u6682\u65E0\u5386\u53F2\u6570\u636E";
|
|
544
|
-
const
|
|
724
|
+
const relText = teamContext?.relationships.get(a.agent_id) || a.relationship;
|
|
725
|
+
const rel = relText ? ` \u5173\u7CFB: ${relText}` : "";
|
|
545
726
|
return `- ${a.name} (ID: ${a.agent_id}) \u6280\u80FD: [${a.capabilities.join(", ")}]${rel} ${load} ${speed}`;
|
|
546
727
|
}).join("\n");
|
|
728
|
+
let teamInfo = "";
|
|
729
|
+
if (teamContext) {
|
|
730
|
+
teamInfo = `
|
|
731
|
+
\u56E2\u961F: ${teamContext.name}${teamContext.description ? ` \u2014 ${teamContext.description}` : ""}
|
|
732
|
+
`;
|
|
733
|
+
}
|
|
547
734
|
return `\u5F53\u524D\u65F6\u95F4: ${now.toISOString()}
|
|
548
|
-
|
|
735
|
+
${teamInfo}
|
|
549
736
|
\u53EF\u7528\u7684\u78B3\u57FA\u8282\u70B9\uFF1A
|
|
550
737
|
${agentList}
|
|
551
738
|
|
|
@@ -605,7 +792,25 @@ async function planJob(request, provider, db2) {
|
|
|
605
792
|
const conn = db2 ?? getDb();
|
|
606
793
|
const allAgents = listAgentsWithMetrics(conn);
|
|
607
794
|
let agents;
|
|
608
|
-
|
|
795
|
+
let teamContext;
|
|
796
|
+
if (request.team_id) {
|
|
797
|
+
const team = getTeamWithMembers(request.team_id, conn);
|
|
798
|
+
if (!team) {
|
|
799
|
+
throw new Error(`\u56E2\u961F\u4E0D\u5B58\u5728: ${request.team_id}`);
|
|
800
|
+
}
|
|
801
|
+
if (team.members.length === 0) {
|
|
802
|
+
throw new Error(`\u56E2\u961F "${team.name}" \u4E2D\u6CA1\u6709\u6210\u5458`);
|
|
803
|
+
}
|
|
804
|
+
const memberIds = new Set(team.members.map((m) => m.agent_id));
|
|
805
|
+
agents = allAgents.filter((a) => memberIds.has(a.agent_id));
|
|
806
|
+
const relationships = /* @__PURE__ */ new Map();
|
|
807
|
+
for (const m of team.members) {
|
|
808
|
+
if (m.relationship) {
|
|
809
|
+
relationships.set(m.agent_id, m.relationship);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
teamContext = { name: team.name, description: team.description, relationships };
|
|
813
|
+
} else if (request.agent_ids && request.agent_ids.length > 0) {
|
|
609
814
|
agents = allAgents.filter((a) => request.agent_ids.includes(a.agent_id));
|
|
610
815
|
if (agents.length === 0) {
|
|
611
816
|
throw new Error("\u6307\u5B9A\u7684 Agent \u5747\u4E0D\u5B58\u5728");
|
|
@@ -621,7 +826,7 @@ async function planJob(request, provider, db2) {
|
|
|
621
826
|
}
|
|
622
827
|
const llm = provider ?? createLlmProvider();
|
|
623
828
|
const systemPrompt = buildSystemPrompt();
|
|
624
|
-
const userPrompt = buildUserPrompt(request.prompt, agents);
|
|
829
|
+
const userPrompt = buildUserPrompt(request.prompt, agents, teamContext);
|
|
625
830
|
const response = await llm.complete({
|
|
626
831
|
messages: [
|
|
627
832
|
{ role: "system", content: systemPrompt },
|
|
@@ -762,7 +967,7 @@ ${taskDescription}
|
|
|
762
967
|
4. \u5B57\u6570 200-400 \u5B57
|
|
763
968
|
5. \u76F4\u63A5\u8F93\u51FA\u6C47\u62A5\u5185\u5BB9\uFF0C\u4E0D\u8981\u52A0\u4EFB\u4F55\u683C\u5F0F\u524D\u7F00\u6216\u8BF4\u660E`;
|
|
764
969
|
}
|
|
765
|
-
async function simulateDelivery(traceId, provider, db2) {
|
|
970
|
+
async function simulateDelivery(traceId, provider, db2, teamId) {
|
|
766
971
|
const conn = db2 ?? getDb();
|
|
767
972
|
const task = getTask(traceId, conn);
|
|
768
973
|
if (!task) {
|
|
@@ -772,6 +977,11 @@ async function simulateDelivery(traceId, provider, db2) {
|
|
|
772
977
|
if (!agent) {
|
|
773
978
|
throw new Error(`Agent not found: ${task.assignee_id}`);
|
|
774
979
|
}
|
|
980
|
+
let relationship = agent.relationship;
|
|
981
|
+
if (teamId) {
|
|
982
|
+
const teamRel = getTeamMemberRelationship(teamId, agent.agent_id, conn);
|
|
983
|
+
if (teamRel) relationship = teamRel;
|
|
984
|
+
}
|
|
775
985
|
const llm = provider ?? createLlmProvider();
|
|
776
986
|
const response = await llm.complete({
|
|
777
987
|
messages: [
|
|
@@ -783,7 +993,7 @@ async function simulateDelivery(traceId, provider, db2) {
|
|
|
783
993
|
role: "user",
|
|
784
994
|
content: buildSimulatePrompt(
|
|
785
995
|
agent.name,
|
|
786
|
-
|
|
996
|
+
relationship,
|
|
787
997
|
agent.capabilities,
|
|
788
998
|
task.todo_description,
|
|
789
999
|
task.deadline
|
|
@@ -838,13 +1048,13 @@ router3.post("/reject", (req, res) => {
|
|
|
838
1048
|
}
|
|
839
1049
|
});
|
|
840
1050
|
router3.post("/simulate", async (req, res) => {
|
|
841
|
-
const { trace_id } = req.body;
|
|
1051
|
+
const { trace_id, team_id } = req.body;
|
|
842
1052
|
if (!trace_id) {
|
|
843
1053
|
res.status(400).json({ error: "trace_id is required" });
|
|
844
1054
|
return;
|
|
845
1055
|
}
|
|
846
1056
|
try {
|
|
847
|
-
const result = await simulateDelivery(trace_id);
|
|
1057
|
+
const result = await simulateDelivery(trace_id, void 0, void 0, team_id);
|
|
848
1058
|
res.json(result);
|
|
849
1059
|
} catch (error) {
|
|
850
1060
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -857,9 +1067,72 @@ var tasks_default = router3;
|
|
|
857
1067
|
// src/routes/sync.ts
|
|
858
1068
|
import { Router as Router4 } from "express";
|
|
859
1069
|
|
|
1070
|
+
// src/models/evaluation.ts
|
|
1071
|
+
var RATING_SYSTEMS = {
|
|
1072
|
+
ali: ["3.25", "3.5", "3.5+", "3.75", "4.0"],
|
|
1073
|
+
letter: ["S", "A", "B", "C", "D"],
|
|
1074
|
+
em: ["E", "M+", "M", "M-", "L"]
|
|
1075
|
+
};
|
|
1076
|
+
function validateRating(system, rating) {
|
|
1077
|
+
const values = RATING_SYSTEMS[system];
|
|
1078
|
+
return values ? values.includes(rating) : false;
|
|
1079
|
+
}
|
|
1080
|
+
function createEvaluation(evaluation, db2) {
|
|
1081
|
+
const conn = db2 ?? getDb();
|
|
1082
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1083
|
+
conn.prepare(
|
|
1084
|
+
`INSERT INTO evaluations (eval_id, job_id, agent_id, trace_id, rating_system, rating, weight, comment, created_at)
|
|
1085
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
1086
|
+
).run(
|
|
1087
|
+
evaluation.eval_id,
|
|
1088
|
+
evaluation.job_id,
|
|
1089
|
+
evaluation.agent_id,
|
|
1090
|
+
evaluation.trace_id,
|
|
1091
|
+
evaluation.rating_system,
|
|
1092
|
+
evaluation.rating,
|
|
1093
|
+
evaluation.weight,
|
|
1094
|
+
evaluation.comment || "",
|
|
1095
|
+
now
|
|
1096
|
+
);
|
|
1097
|
+
return { ...evaluation, comment: evaluation.comment || "", created_at: now };
|
|
1098
|
+
}
|
|
1099
|
+
function rowToEvaluation(row) {
|
|
1100
|
+
return {
|
|
1101
|
+
...row,
|
|
1102
|
+
rating_system: row.rating_system
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
function listEvaluationsByJob(jobId, db2) {
|
|
1106
|
+
const conn = db2 ?? getDb();
|
|
1107
|
+
const rows = conn.prepare(
|
|
1108
|
+
`SELECT e.*
|
|
1109
|
+
FROM evaluations e
|
|
1110
|
+
WHERE e.job_id = ?
|
|
1111
|
+
ORDER BY e.created_at`
|
|
1112
|
+
).all(jobId);
|
|
1113
|
+
return rows.map(rowToEvaluation);
|
|
1114
|
+
}
|
|
1115
|
+
function listEvaluationsByAgent(agentId, db2) {
|
|
1116
|
+
const conn = db2 ?? getDb();
|
|
1117
|
+
const rows = conn.prepare(
|
|
1118
|
+
`SELECT e.*, j.original_prompt, t.todo_description
|
|
1119
|
+
FROM evaluations e
|
|
1120
|
+
LEFT JOIN jobs j ON e.job_id = j.job_id
|
|
1121
|
+
LEFT JOIN tasks t ON e.trace_id = t.trace_id
|
|
1122
|
+
WHERE e.agent_id = ?
|
|
1123
|
+
ORDER BY e.created_at DESC`
|
|
1124
|
+
).all(agentId);
|
|
1125
|
+
return rows.map((r) => ({ ...rowToEvaluation(r), original_prompt: r.original_prompt, todo_description: r.todo_description }));
|
|
1126
|
+
}
|
|
1127
|
+
function deleteEvaluationsByJob(jobId, db2) {
|
|
1128
|
+
const conn = db2 ?? getDb();
|
|
1129
|
+
const result = conn.prepare("DELETE FROM evaluations WHERE job_id = ?").run(jobId);
|
|
1130
|
+
return result.changes;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
860
1133
|
// src/services/reviewer.ts
|
|
861
|
-
function buildReviewSystemPrompt() {
|
|
862
|
-
|
|
1134
|
+
function buildReviewSystemPrompt(ratingSystem) {
|
|
1135
|
+
let base = `\u4F60\u662F HumanClaw \u4EA4\u4ED8\u5BA1\u67E5\u5458\u3002\u4F60\u7684\u5DE5\u4F5C\u662F\u5BA1\u67E5\u78B3\u57FA\u8282\u70B9\u63D0\u4EA4\u7684\u6240\u6709\u4EFB\u52A1\u4EA4\u4ED8\u7269\uFF0C\u751F\u6210\u6574\u4F53\u5BA1\u67E5\u62A5\u544A\u3002
|
|
863
1136
|
|
|
864
1137
|
\u5BA1\u67E5\u8981\u70B9\uFF1A
|
|
865
1138
|
1. \u68C0\u67E5\u6BCF\u4E2A\u4EA4\u4ED8\u7269\u662F\u5426\u6EE1\u8DB3\u539F\u59CB\u4EFB\u52A1\u8981\u6C42
|
|
@@ -870,6 +1143,30 @@ function buildReviewSystemPrompt() {
|
|
|
870
1143
|
\u8F93\u51FA\u683C\u5F0F\uFF1A
|
|
871
1144
|
- \u4F7F\u7528 Markdown \u683C\u5F0F
|
|
872
1145
|
- \u5148\u7ED9\u603B\u7ED3\u8BC4\u5206\uFF0C\u518D\u9010\u4E2A\u5206\u6790\u6BCF\u4E2A\u5B50\u4EFB\u52A1\u7684\u4EA4\u4ED8\u8D28\u91CF`;
|
|
1146
|
+
if (ratingSystem) {
|
|
1147
|
+
const ratings = RATING_SYSTEMS[ratingSystem];
|
|
1148
|
+
const systemLabels = {
|
|
1149
|
+
ali: "\u963F\u91CC\u7EE9\u6548\u4F53\u7CFB",
|
|
1150
|
+
letter: "SABCD \u7B49\u7EA7",
|
|
1151
|
+
em: "EM \u7EE9\u6548\u4F53\u7CFB"
|
|
1152
|
+
};
|
|
1153
|
+
base += `
|
|
1154
|
+
|
|
1155
|
+
\u540C\u65F6\uFF0C\u8BF7\u4E3A\u6BCF\u4E2A\u53C2\u4E0E\u7684\u78B3\u57FA\u8282\u70B9\u751F\u6210\u4E2A\u4EBA\u7EE9\u6548\u8BC4\u4EF7\u3002
|
|
1156
|
+
\u4F7F\u7528\u300C${systemLabels[ratingSystem]}\u300D\uFF0C\u53EF\u9009\u8BC4\u5206\u4E3A: ${ratings.join(" / ")}
|
|
1157
|
+
\u5728\u5BA1\u67E5\u62A5\u544A\u672B\u5C3E\uFF0C\u989D\u5916\u8F93\u51FA\u4E00\u4E2A JSON \u5757\uFF08\u7528 \`\`\`json \u5305\u88F9\uFF09\uFF0C\u683C\u5F0F\u5982\u4E0B\uFF1A
|
|
1158
|
+
\`\`\`json
|
|
1159
|
+
[
|
|
1160
|
+
{
|
|
1161
|
+
"agent_id": "\u6267\u884C\u8005ID",
|
|
1162
|
+
"trace_id": "\u4EFB\u52A1\u8FFD\u8E2A\u7801",
|
|
1163
|
+
"rating": "\u8BC4\u5206",
|
|
1164
|
+
"comment": "\u4E00\u53E5\u8BDD\u8BC4\u8BED"
|
|
1165
|
+
}
|
|
1166
|
+
]
|
|
1167
|
+
\`\`\``;
|
|
1168
|
+
}
|
|
1169
|
+
return base;
|
|
873
1170
|
}
|
|
874
1171
|
function buildReviewUserPrompt(originalPrompt, tasks) {
|
|
875
1172
|
let prompt = `\u539F\u59CB\u9700\u6C42: ${originalPrompt}
|
|
@@ -890,10 +1187,16 @@ function buildReviewUserPrompt(originalPrompt, tasks) {
|
|
|
890
1187
|
}
|
|
891
1188
|
prompt += `--- \u5B50\u4EFB\u52A1 ${i + 1} ---
|
|
892
1189
|
`;
|
|
893
|
-
prompt += `\u6267\u884C\u8005: ${t.assignee_id}
|
|
1190
|
+
prompt += `\u6267\u884C\u8005: ${t.assignee_name} (${t.assignee_id})
|
|
1191
|
+
`;
|
|
1192
|
+
prompt += `\u8FFD\u8E2A\u7801: ${t.trace_id}
|
|
894
1193
|
`;
|
|
895
1194
|
prompt += `\u4EFB\u52A1: ${t.todo_description}
|
|
896
1195
|
`;
|
|
1196
|
+
if (t.weight !== void 0 && t.weight !== 1) {
|
|
1197
|
+
prompt += `\u4EFB\u52A1\u6743\u91CD: ${t.weight}
|
|
1198
|
+
`;
|
|
1199
|
+
}
|
|
897
1200
|
prompt += `\u4EA4\u4ED8\u7269:
|
|
898
1201
|
${resultText}
|
|
899
1202
|
|
|
@@ -902,7 +1205,22 @@ ${resultText}
|
|
|
902
1205
|
prompt += "\u8BF7\u5BA1\u67E5\u4EE5\u4E0A\u6240\u6709\u4EA4\u4ED8\u7269\uFF0C\u751F\u6210\u5BA1\u67E5\u62A5\u544A\u3002";
|
|
903
1206
|
return prompt;
|
|
904
1207
|
}
|
|
905
|
-
|
|
1208
|
+
function extractEvaluationJson(raw) {
|
|
1209
|
+
const blocks = raw.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/g);
|
|
1210
|
+
if (!blocks) return void 0;
|
|
1211
|
+
const lastBlock = blocks[blocks.length - 1];
|
|
1212
|
+
const jsonMatch = lastBlock.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
1213
|
+
if (!jsonMatch) return void 0;
|
|
1214
|
+
try {
|
|
1215
|
+
const parsed = JSON.parse(jsonMatch[1].trim());
|
|
1216
|
+
if (Array.isArray(parsed)) {
|
|
1217
|
+
return parsed;
|
|
1218
|
+
}
|
|
1219
|
+
} catch {
|
|
1220
|
+
}
|
|
1221
|
+
return void 0;
|
|
1222
|
+
}
|
|
1223
|
+
async function reviewJob(jobId, provider, db2, ratingSystem, taskWeights) {
|
|
906
1224
|
const conn = db2 ?? getDb();
|
|
907
1225
|
const job = getJobWithTasks(jobId, conn);
|
|
908
1226
|
if (!job) {
|
|
@@ -915,38 +1233,58 @@ async function reviewJob(jobId, provider, db2) {
|
|
|
915
1233
|
);
|
|
916
1234
|
}
|
|
917
1235
|
const llm = provider ?? createLlmProvider();
|
|
1236
|
+
const tasksWithNames = job.tasks.map((t) => {
|
|
1237
|
+
const agent = getAgent(t.assignee_id, conn);
|
|
1238
|
+
return {
|
|
1239
|
+
trace_id: t.trace_id,
|
|
1240
|
+
assignee_id: t.assignee_id,
|
|
1241
|
+
assignee_name: agent?.name || t.assignee_id,
|
|
1242
|
+
todo_description: t.todo_description,
|
|
1243
|
+
result_data: t.result_data,
|
|
1244
|
+
weight: taskWeights?.[t.trace_id]
|
|
1245
|
+
};
|
|
1246
|
+
});
|
|
918
1247
|
const response = await llm.complete({
|
|
919
1248
|
messages: [
|
|
920
|
-
{ role: "system", content: buildReviewSystemPrompt() },
|
|
1249
|
+
{ role: "system", content: buildReviewSystemPrompt(ratingSystem) },
|
|
921
1250
|
{
|
|
922
1251
|
role: "user",
|
|
923
|
-
content: buildReviewUserPrompt(
|
|
924
|
-
job.original_prompt,
|
|
925
|
-
job.tasks.map((t) => ({
|
|
926
|
-
assignee_id: t.assignee_id,
|
|
927
|
-
todo_description: t.todo_description,
|
|
928
|
-
result_data: t.result_data
|
|
929
|
-
}))
|
|
930
|
-
)
|
|
1252
|
+
content: buildReviewUserPrompt(job.original_prompt, tasksWithNames)
|
|
931
1253
|
}
|
|
932
1254
|
],
|
|
933
1255
|
temperature: 0.3,
|
|
934
1256
|
max_tokens: 4096
|
|
935
1257
|
});
|
|
936
|
-
|
|
1258
|
+
const result = {
|
|
937
1259
|
job_id: jobId,
|
|
938
1260
|
original_prompt: job.original_prompt,
|
|
939
1261
|
review: response.content,
|
|
940
1262
|
reviewed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
941
1263
|
};
|
|
1264
|
+
if (ratingSystem) {
|
|
1265
|
+
const evalData = extractEvaluationJson(response.content);
|
|
1266
|
+
if (evalData) {
|
|
1267
|
+
result.evaluations = evalData.map((e) => {
|
|
1268
|
+
const agent = getAgent(e.agent_id, conn);
|
|
1269
|
+
return {
|
|
1270
|
+
...e,
|
|
1271
|
+
agent_name: agent?.name || e.agent_id
|
|
1272
|
+
};
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return result;
|
|
942
1277
|
}
|
|
943
1278
|
|
|
944
1279
|
// src/routes/sync.ts
|
|
945
1280
|
var router4 = Router4();
|
|
946
1281
|
router4.post("/:job_id/review", async (req, res) => {
|
|
947
1282
|
const { job_id } = req.params;
|
|
1283
|
+
const { rating_system, task_weights } = req.body;
|
|
1284
|
+
const validSystems = ["ali", "letter", "em"];
|
|
1285
|
+
const ratingSystem = rating_system && validSystems.includes(rating_system) ? rating_system : void 0;
|
|
948
1286
|
try {
|
|
949
|
-
const result = await reviewJob(job_id);
|
|
1287
|
+
const result = await reviewJob(job_id, void 0, void 0, ratingSystem, task_weights);
|
|
950
1288
|
res.json(result);
|
|
951
1289
|
} catch (error) {
|
|
952
1290
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -960,7 +1298,8 @@ var sync_default = router4;
|
|
|
960
1298
|
import { Router as Router5 } from "express";
|
|
961
1299
|
var router5 = Router5();
|
|
962
1300
|
router5.get("/", (_req, res) => {
|
|
963
|
-
const
|
|
1301
|
+
const rawProvider = getConfig("llm_provider") || process.env.HUMANCLAW_LLM_PROVIDER || "anthropic";
|
|
1302
|
+
const provider = rawProvider === "claude" ? "anthropic" : rawProvider;
|
|
964
1303
|
const apiKey = getConfig("llm_api_key") || process.env.HUMANCLAW_LLM_API_KEY || "";
|
|
965
1304
|
const model = getConfig("llm_model") || process.env.HUMANCLAW_LLM_MODEL || "";
|
|
966
1305
|
const baseUrl = getConfig("llm_base_url") || process.env.HUMANCLAW_LLM_BASE_URL || "";
|
|
@@ -977,11 +1316,13 @@ router5.get("/", (_req, res) => {
|
|
|
977
1316
|
router5.put("/", (req, res) => {
|
|
978
1317
|
const { provider, api_key, model, base_url } = req.body;
|
|
979
1318
|
if (provider !== void 0) {
|
|
980
|
-
|
|
981
|
-
|
|
1319
|
+
const normalized = provider === "claude" ? "anthropic" : provider;
|
|
1320
|
+
const validFormats = ["openai", "anthropic", "responses"];
|
|
1321
|
+
if (!validFormats.includes(normalized)) {
|
|
1322
|
+
res.status(400).json({ error: `\u4E0D\u652F\u6301\u7684 API \u683C\u5F0F\u3002\u652F\u6301: ${validFormats.join(", ")}` });
|
|
982
1323
|
return;
|
|
983
1324
|
}
|
|
984
|
-
setConfig("llm_provider",
|
|
1325
|
+
setConfig("llm_provider", normalized);
|
|
985
1326
|
}
|
|
986
1327
|
if (api_key !== void 0) {
|
|
987
1328
|
if (api_key === "") {
|
|
@@ -1006,8 +1347,309 @@ router5.put("/", (req, res) => {
|
|
|
1006
1347
|
}
|
|
1007
1348
|
res.json({ ok: true });
|
|
1008
1349
|
});
|
|
1350
|
+
router5.post("/reset", (_req, res) => {
|
|
1351
|
+
const db2 = getDb();
|
|
1352
|
+
db2.exec(`
|
|
1353
|
+
DELETE FROM evaluations;
|
|
1354
|
+
DELETE FROM team_members;
|
|
1355
|
+
DELETE FROM tasks;
|
|
1356
|
+
DELETE FROM jobs;
|
|
1357
|
+
DELETE FROM teams;
|
|
1358
|
+
DELETE FROM agents;
|
|
1359
|
+
`);
|
|
1360
|
+
res.json({ ok: true });
|
|
1361
|
+
});
|
|
1009
1362
|
var config_default = router5;
|
|
1010
1363
|
|
|
1364
|
+
// src/routes/teams.ts
|
|
1365
|
+
import { Router as Router6 } from "express";
|
|
1366
|
+
var router6 = Router6();
|
|
1367
|
+
router6.get("/", (_req, res) => {
|
|
1368
|
+
const teams = listTeams();
|
|
1369
|
+
const teamsWithMembers = teams.map((t) => {
|
|
1370
|
+
const full = getTeamWithMembers(t.team_id);
|
|
1371
|
+
return full || { ...t, members: [] };
|
|
1372
|
+
});
|
|
1373
|
+
res.json({ teams: teamsWithMembers });
|
|
1374
|
+
});
|
|
1375
|
+
router6.get("/:id", (req, res) => {
|
|
1376
|
+
const team = getTeamWithMembers(req.params.id);
|
|
1377
|
+
if (!team) {
|
|
1378
|
+
res.status(404).json({ error: `\u56E2\u961F\u4E0D\u5B58\u5728: ${req.params.id}` });
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
res.json(team);
|
|
1382
|
+
});
|
|
1383
|
+
router6.post("/", (req, res) => {
|
|
1384
|
+
const { name, description } = req.body;
|
|
1385
|
+
if (!name || typeof name !== "string") {
|
|
1386
|
+
res.status(400).json({ error: "name is required" });
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
const team = createTeam({
|
|
1390
|
+
team_id: generateId("team"),
|
|
1391
|
+
name,
|
|
1392
|
+
description: description || ""
|
|
1393
|
+
});
|
|
1394
|
+
res.status(201).json(team);
|
|
1395
|
+
});
|
|
1396
|
+
router6.delete("/:id", (req, res) => {
|
|
1397
|
+
const deleted = deleteTeam(req.params.id);
|
|
1398
|
+
if (!deleted) {
|
|
1399
|
+
res.status(404).json({ error: `\u56E2\u961F\u4E0D\u5B58\u5728: ${req.params.id}` });
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
res.json({ deleted: req.params.id });
|
|
1403
|
+
});
|
|
1404
|
+
router6.post("/:id/members", (req, res) => {
|
|
1405
|
+
const { agent_id, relationship } = req.body;
|
|
1406
|
+
if (!agent_id || typeof agent_id !== "string") {
|
|
1407
|
+
res.status(400).json({ error: "agent_id is required" });
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
const agent = getAgent(agent_id);
|
|
1411
|
+
if (!agent) {
|
|
1412
|
+
res.status(404).json({ error: `Agent \u4E0D\u5B58\u5728: ${agent_id}` });
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
try {
|
|
1416
|
+
addTeamMember(req.params.id, agent_id, relationship || "");
|
|
1417
|
+
res.status(201).json({ team_id: req.params.id, agent_id, relationship: relationship || "" });
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1420
|
+
res.status(400).json({ error: message });
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
router6.delete("/:id/members/:agent_id", (req, res) => {
|
|
1424
|
+
const removed = removeTeamMember(req.params.id, req.params.agent_id);
|
|
1425
|
+
if (!removed) {
|
|
1426
|
+
res.status(404).json({ error: "\u6210\u5458\u4E0D\u5728\u8BE5\u56E2\u961F\u4E2D" });
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
res.json({ removed: req.params.agent_id, team_id: req.params.id });
|
|
1430
|
+
});
|
|
1431
|
+
router6.put("/:id/members/:agent_id", (req, res) => {
|
|
1432
|
+
const { relationship } = req.body;
|
|
1433
|
+
if (relationship === void 0 || typeof relationship !== "string") {
|
|
1434
|
+
res.status(400).json({ error: "relationship is required" });
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
const updated = updateTeamMemberRelationship(req.params.id, req.params.agent_id, relationship);
|
|
1438
|
+
if (!updated) {
|
|
1439
|
+
res.status(404).json({ error: "\u6210\u5458\u4E0D\u5728\u8BE5\u56E2\u961F\u4E2D" });
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
res.json({ team_id: req.params.id, agent_id: req.params.agent_id, relationship });
|
|
1443
|
+
});
|
|
1444
|
+
var teams_default = router6;
|
|
1445
|
+
|
|
1446
|
+
// src/routes/evaluations.ts
|
|
1447
|
+
import { Router as Router7 } from "express";
|
|
1448
|
+
|
|
1449
|
+
// src/services/evaluator.ts
|
|
1450
|
+
function generateEvalId() {
|
|
1451
|
+
return "eval_" + Math.random().toString(36).substring(2, 10);
|
|
1452
|
+
}
|
|
1453
|
+
function buildEvalPrompt(originalPrompt, ratingSystem, tasks) {
|
|
1454
|
+
const ratings = RATING_SYSTEMS[ratingSystem];
|
|
1455
|
+
const systemLabels = {
|
|
1456
|
+
ali: "\u963F\u91CC\u7EE9\u6548\u4F53\u7CFB\uFF083.25=\u4E0D\u5408\u683C, 3.5=\u57FA\u672C\u8FBE\u6807, 3.5+=\u8FBE\u6807, 3.75=\u4F18\u79C0, 4.0=\u5353\u8D8A\uFF09",
|
|
1457
|
+
letter: "SABCD \u7B49\u7EA7\uFF08S=\u5353\u8D8A, A=\u4F18\u79C0, B=\u8FBE\u6807, C=\u5F85\u6539\u8FDB, D=\u4E0D\u5408\u683C\uFF09",
|
|
1458
|
+
em: "EM \u4F53\u7CFB\uFF08E=\u8FDC\u8D85\u9884\u671F, M+=\u8D85\u51FA\u9884\u671F, M=\u8FBE\u6807, M-=\u57FA\u672C\u8FBE\u6807, L=\u4E0D\u8FBE\u6807\uFF09"
|
|
1459
|
+
};
|
|
1460
|
+
let prompt = `\u539F\u59CB\u9700\u6C42: ${originalPrompt}
|
|
1461
|
+
|
|
1462
|
+
`;
|
|
1463
|
+
prompt += `\u8BC4\u5206\u4F53\u7CFB: ${systemLabels[ratingSystem]}
|
|
1464
|
+
`;
|
|
1465
|
+
prompt += `\u53EF\u9009\u8BC4\u5206: ${ratings.join(" / ")}
|
|
1466
|
+
|
|
1467
|
+
`;
|
|
1468
|
+
for (const t of tasks) {
|
|
1469
|
+
let resultText = "";
|
|
1470
|
+
if (t.result_data) {
|
|
1471
|
+
if (typeof t.result_data === "string") {
|
|
1472
|
+
resultText = t.result_data;
|
|
1473
|
+
} else if (typeof t.result_data === "object") {
|
|
1474
|
+
const rd = t.result_data;
|
|
1475
|
+
resultText = rd.text || JSON.stringify(rd, null, 2);
|
|
1476
|
+
} else {
|
|
1477
|
+
resultText = String(t.result_data);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
prompt += `--- \u4EFB\u52A1 ${t.trace_id} (\u6743\u91CD: ${t.weight}) ---
|
|
1481
|
+
`;
|
|
1482
|
+
prompt += `\u6267\u884C\u8005: ${t.assignee_name} (${t.assignee_id})
|
|
1483
|
+
`;
|
|
1484
|
+
prompt += `\u4EFB\u52A1: ${t.todo_description}
|
|
1485
|
+
`;
|
|
1486
|
+
prompt += `\u4EA4\u4ED8\u7269:
|
|
1487
|
+
${resultText}
|
|
1488
|
+
|
|
1489
|
+
`;
|
|
1490
|
+
}
|
|
1491
|
+
prompt += `\u8BF7\u4E3A\u6BCF\u4E2A\u6267\u884C\u8005\u7684\u6BCF\u4E2A\u4EFB\u52A1\u5355\u72EC\u8BC4\u5206\u3002\u4E25\u683C\u8F93\u51FA JSON \u6570\u7EC4\uFF08\u4E0D\u8981\u8F93\u51FA\u5176\u4ED6\u5185\u5BB9\uFF09\uFF1A
|
|
1492
|
+
\`\`\`json
|
|
1493
|
+
[
|
|
1494
|
+
{
|
|
1495
|
+
"agent_id": "\u6267\u884C\u8005ID",
|
|
1496
|
+
"trace_id": "\u4EFB\u52A1\u8FFD\u8E2A\u7801",
|
|
1497
|
+
"rating": "\u4ECE ${ratings.join("/")} \u4E2D\u9009\u4E00\u4E2A",
|
|
1498
|
+
"comment": "\u4E00\u53E5\u8BDD\u8BC4\u8BED\uFF0C\u8BF4\u660E\u7ED9\u6B64\u8BC4\u5206\u7684\u539F\u56E0"
|
|
1499
|
+
}
|
|
1500
|
+
]
|
|
1501
|
+
\`\`\``;
|
|
1502
|
+
return prompt;
|
|
1503
|
+
}
|
|
1504
|
+
function extractJson2(raw) {
|
|
1505
|
+
const codeBlockMatch = raw.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
1506
|
+
if (codeBlockMatch) {
|
|
1507
|
+
return codeBlockMatch[1].trim();
|
|
1508
|
+
}
|
|
1509
|
+
const arrayMatch = raw.match(/\[[\s\S]*\]/);
|
|
1510
|
+
if (arrayMatch) {
|
|
1511
|
+
return arrayMatch[0];
|
|
1512
|
+
}
|
|
1513
|
+
return raw.trim();
|
|
1514
|
+
}
|
|
1515
|
+
async function generateEvaluations(request, provider, db2) {
|
|
1516
|
+
const conn = db2 ?? getDb();
|
|
1517
|
+
const job = getJobWithTasks(request.job_id, conn);
|
|
1518
|
+
if (!job) {
|
|
1519
|
+
throw new Error(`Job not found: ${request.job_id}`);
|
|
1520
|
+
}
|
|
1521
|
+
if (!isJobComplete(request.job_id, conn)) {
|
|
1522
|
+
const resolved = job.tasks.filter((t) => t.status === "RESOLVED").length;
|
|
1523
|
+
throw new Error(
|
|
1524
|
+
`Job \u5C1A\u672A\u5168\u90E8\u5B8C\u6210: ${resolved}/${job.tasks.length} \u4E2A\u4EFB\u52A1\u5DF2\u4EA4\u4ED8`
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
deleteEvaluationsByJob(request.job_id, conn);
|
|
1528
|
+
const llm = provider ?? createLlmProvider();
|
|
1529
|
+
const tasksWithInfo = job.tasks.map((t) => {
|
|
1530
|
+
const agent = getAgent(t.assignee_id, conn);
|
|
1531
|
+
return {
|
|
1532
|
+
trace_id: t.trace_id,
|
|
1533
|
+
assignee_id: t.assignee_id,
|
|
1534
|
+
assignee_name: agent?.name || t.assignee_id,
|
|
1535
|
+
todo_description: t.todo_description,
|
|
1536
|
+
result_data: t.result_data,
|
|
1537
|
+
weight: request.task_weights?.[t.trace_id] ?? 1
|
|
1538
|
+
};
|
|
1539
|
+
});
|
|
1540
|
+
const response = await llm.complete({
|
|
1541
|
+
messages: [
|
|
1542
|
+
{
|
|
1543
|
+
role: "system",
|
|
1544
|
+
content: "\u4F60\u662F\u4E00\u4E2A\u5BA2\u89C2\u516C\u6B63\u7684\u7EE9\u6548\u8BC4\u4F30\u4E13\u5BB6\u3002\u6839\u636E\u4EFB\u52A1\u5B8C\u6210\u8D28\u91CF\u548C\u4EA4\u4ED8\u7269\u5185\u5BB9\u8FDB\u884C\u8BC4\u5206\u3002"
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
role: "user",
|
|
1548
|
+
content: buildEvalPrompt(job.original_prompt, request.rating_system, tasksWithInfo)
|
|
1549
|
+
}
|
|
1550
|
+
],
|
|
1551
|
+
temperature: 0.3,
|
|
1552
|
+
max_tokens: 4096
|
|
1553
|
+
});
|
|
1554
|
+
const jsonStr = extractJson2(response.content);
|
|
1555
|
+
let parsed;
|
|
1556
|
+
try {
|
|
1557
|
+
parsed = JSON.parse(jsonStr);
|
|
1558
|
+
} catch {
|
|
1559
|
+
throw new Error("AI \u8FD4\u56DE\u7684\u7EE9\u6548\u8BC4\u4EF7\u65E0\u6CD5\u89E3\u6790\u4E3A JSON\uFF0C\u8BF7\u91CD\u8BD5");
|
|
1560
|
+
}
|
|
1561
|
+
if (!Array.isArray(parsed)) {
|
|
1562
|
+
throw new Error("AI \u8FD4\u56DE\u7684\u4E0D\u662F\u6709\u6548\u7684\u8BC4\u4EF7\u6570\u7EC4");
|
|
1563
|
+
}
|
|
1564
|
+
const evaluations = [];
|
|
1565
|
+
for (const item of parsed) {
|
|
1566
|
+
const agentId = String(item.agent_id || "");
|
|
1567
|
+
const traceId = String(item.trace_id || "");
|
|
1568
|
+
let rating = String(item.rating || "");
|
|
1569
|
+
const comment = String(item.comment || "");
|
|
1570
|
+
if (!validateRating(request.rating_system, rating)) {
|
|
1571
|
+
const ratings = RATING_SYSTEMS[request.rating_system];
|
|
1572
|
+
rating = ratings[Math.floor(ratings.length / 2)];
|
|
1573
|
+
}
|
|
1574
|
+
const weight = request.task_weights?.[traceId] ?? 1;
|
|
1575
|
+
const evaluation = createEvaluation({
|
|
1576
|
+
eval_id: generateEvalId(),
|
|
1577
|
+
job_id: request.job_id,
|
|
1578
|
+
agent_id: agentId,
|
|
1579
|
+
trace_id: traceId,
|
|
1580
|
+
rating_system: request.rating_system,
|
|
1581
|
+
rating,
|
|
1582
|
+
weight,
|
|
1583
|
+
comment
|
|
1584
|
+
}, conn);
|
|
1585
|
+
evaluations.push(evaluation);
|
|
1586
|
+
}
|
|
1587
|
+
return {
|
|
1588
|
+
job_id: request.job_id,
|
|
1589
|
+
evaluations,
|
|
1590
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
function getPerformanceDashboard(agentId, db2) {
|
|
1594
|
+
const conn = db2 ?? getDb();
|
|
1595
|
+
if (agentId) {
|
|
1596
|
+
return { evaluations: listEvaluationsByAgent(agentId, conn) };
|
|
1597
|
+
}
|
|
1598
|
+
const rows = conn.prepare(
|
|
1599
|
+
`SELECT e.*, j.original_prompt, t.todo_description, a.name AS agent_name
|
|
1600
|
+
FROM evaluations e
|
|
1601
|
+
LEFT JOIN jobs j ON e.job_id = j.job_id
|
|
1602
|
+
LEFT JOIN tasks t ON e.trace_id = t.trace_id
|
|
1603
|
+
LEFT JOIN agents a ON e.agent_id = a.agent_id
|
|
1604
|
+
ORDER BY e.created_at DESC
|
|
1605
|
+
LIMIT 50`
|
|
1606
|
+
).all();
|
|
1607
|
+
return { evaluations: rows };
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// src/routes/evaluations.ts
|
|
1611
|
+
var router7 = Router7();
|
|
1612
|
+
router7.post("/generate", async (req, res) => {
|
|
1613
|
+
const { job_id, rating_system, task_weights } = req.body;
|
|
1614
|
+
if (!job_id || typeof job_id !== "string") {
|
|
1615
|
+
res.status(400).json({ error: "job_id is required" });
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
const validSystems = ["ali", "letter", "em"];
|
|
1619
|
+
if (!rating_system || !validSystems.includes(rating_system)) {
|
|
1620
|
+
res.status(400).json({
|
|
1621
|
+
error: `rating_system is required. Must be one of: ${validSystems.join(", ")}`
|
|
1622
|
+
});
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
try {
|
|
1626
|
+
const result = await generateEvaluations({
|
|
1627
|
+
job_id,
|
|
1628
|
+
rating_system,
|
|
1629
|
+
task_weights
|
|
1630
|
+
});
|
|
1631
|
+
res.json(result);
|
|
1632
|
+
} catch (error) {
|
|
1633
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1634
|
+
const status = message.includes("API Key") || message.includes("API key") ? 503 : 400;
|
|
1635
|
+
res.status(status).json({ error: message });
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
router7.get("/job/:job_id", (req, res) => {
|
|
1639
|
+
const evaluations = listEvaluationsByJob(req.params.job_id);
|
|
1640
|
+
res.json({ evaluations });
|
|
1641
|
+
});
|
|
1642
|
+
router7.get("/agent/:agent_id", (req, res) => {
|
|
1643
|
+
const evaluations = listEvaluationsByAgent(req.params.agent_id);
|
|
1644
|
+
res.json({ evaluations });
|
|
1645
|
+
});
|
|
1646
|
+
router7.get("/dashboard", (req, res) => {
|
|
1647
|
+
const agentId = req.query.agent_id;
|
|
1648
|
+
const dashboard = getPerformanceDashboard(agentId);
|
|
1649
|
+
res.json(dashboard);
|
|
1650
|
+
});
|
|
1651
|
+
var evaluations_default = router7;
|
|
1652
|
+
|
|
1011
1653
|
// src/dashboard.ts
|
|
1012
1654
|
function getDashboardHtml() {
|
|
1013
1655
|
return `<!DOCTYPE html>
|
|
@@ -1164,6 +1806,26 @@ main{padding:24px 32px;max-width:1200px}
|
|
|
1164
1806
|
.task-row .remove-task{position:absolute;top:8px;right:10px;background:none;border:none;color:var(--red);cursor:pointer;font-size:16px;line-height:1}
|
|
1165
1807
|
.task-row .fg{margin-bottom:10px}
|
|
1166
1808
|
.task-row .fg:last-child{margin-bottom:0}
|
|
1809
|
+
/* Markdown Editor */
|
|
1810
|
+
.md-editor{position:relative}
|
|
1811
|
+
.md-editor textarea{min-height:120px;resize:vertical;font-family:var(--font-mono);font-size:12px;width:100%;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text);outline:none;transition:border-color .15s}
|
|
1812
|
+
.md-editor textarea:focus{border-color:var(--accent)}
|
|
1813
|
+
.md-expand-btn{position:absolute;top:6px;right:6px;background:var(--surface-hover);border:1px solid var(--border);color:var(--text-dim);border-radius:4px;width:24px;height:24px;font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:2;transition:all .15s;line-height:1}
|
|
1814
|
+
.md-expand-btn:hover{color:var(--accent);border-color:var(--accent)}
|
|
1815
|
+
.md-editor.fullscreen{position:fixed;inset:20px;z-index:200;background:var(--surface);border-radius:var(--radius);padding:16px;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,.7)}
|
|
1816
|
+
.md-editor.fullscreen textarea{flex:1;min-height:unset;height:100%;resize:none;font-size:14px}
|
|
1817
|
+
.md-editor.fullscreen .md-expand-btn{top:22px;right:22px}
|
|
1818
|
+
.md-fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:199}
|
|
1819
|
+
/* Team badge */
|
|
1820
|
+
.team-badge{display:inline-block;background:rgba(168,85,247,.12);border:1px solid rgba(168,85,247,.25);border-radius:4px;padding:1px 8px;font-size:10px;color:var(--purple);margin-right:4px;margin-bottom:2px}
|
|
1821
|
+
.team-group{margin-bottom:28px}
|
|
1822
|
+
.team-group-header{display:flex;align-items:center;gap:6px;padding:10px 0 8px;border-bottom:1px solid var(--border);margin-bottom:12px;cursor:pointer}
|
|
1823
|
+
.team-group-header:hover .agent-name{color:var(--accent)}
|
|
1824
|
+
/* Evaluation */
|
|
1825
|
+
.eval-badge{display:inline-block;padding:3px 10px;border-radius:12px;font-weight:700;font-size:13px;font-family:var(--font-mono)}
|
|
1826
|
+
.eval-badge.top{background:rgba(34,197,94,.15);color:var(--green)}
|
|
1827
|
+
.eval-badge.mid{background:rgba(234,179,8,.15);color:var(--yellow)}
|
|
1828
|
+
.eval-badge.low{background:rgba(239,68,68,.15);color:var(--red)}
|
|
1167
1829
|
</style>
|
|
1168
1830
|
</head>
|
|
1169
1831
|
<body>
|
|
@@ -1193,8 +1855,25 @@ function toast(msg,ok){
|
|
|
1193
1855
|
setTimeout(()=>t.remove(),3500);
|
|
1194
1856
|
}
|
|
1195
1857
|
let cachedAgents=[];
|
|
1858
|
+
let cachedTeams=[];
|
|
1196
1859
|
let currentPlan=null;
|
|
1197
1860
|
let selectedAgentIds=new Set();
|
|
1861
|
+
let selectedTeamId='';
|
|
1862
|
+
|
|
1863
|
+
window.toggleFullscreen=function(btn){
|
|
1864
|
+
const editor=btn.closest('.md-editor');
|
|
1865
|
+
if(editor.classList.contains('fullscreen')){
|
|
1866
|
+
editor.classList.remove('fullscreen');
|
|
1867
|
+
const bd=document.querySelector('.md-fullscreen-backdrop');
|
|
1868
|
+
if(bd)bd.remove();
|
|
1869
|
+
}else{
|
|
1870
|
+
const bd=document.createElement('div');bd.className='md-fullscreen-backdrop';
|
|
1871
|
+
bd.onclick=()=>{editor.classList.remove('fullscreen');bd.remove()};
|
|
1872
|
+
document.body.appendChild(bd);
|
|
1873
|
+
editor.classList.add('fullscreen');
|
|
1874
|
+
const ta=editor.querySelector('textarea');if(ta)ta.focus();
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1198
1877
|
|
|
1199
1878
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1200
1879
|
// DEMO SCENARIOS
|
|
@@ -1204,6 +1883,9 @@ const DEMOS={
|
|
|
1204
1883
|
emoji:'🐉',title:'\u4E09\u56FD\u8700\u6C49',role:'\u4F60\u662F\u5218\u5907',
|
|
1205
1884
|
desc:'\u6843\u56ED\u7ED3\u4E49\uFF0C\u4E09\u987E\u8305\u5E90\u3002\u4F5C\u4E3A\u8700\u6C49\u4E4B\u4E3B\uFF0C\u7EDF\u9886\u4E94\u864E\u4E0A\u5C06\u548C\u5367\u9F99\u51E4\u96CF\uFF0C\u9010\u9E7F\u4E2D\u539F\u3002',
|
|
1206
1885
|
prompt:'\u5317\u4F10\u4E2D\u539F\uFF0C\u5175\u5206\u4E09\u8DEF\uFF0C\u9700\u8981\u653B\u57CE\u3001\u65AD\u7CAE\u548C\u5916\u4EA4\u4E09\u7BA1\u9F50\u4E0B',
|
|
1886
|
+
teams:[
|
|
1887
|
+
{name:'\u4E09\u56FD\u8700\u6C49',description:'\u5218\u5907\u9EBE\u4E0B\u5168\u4F53\u6587\u81E3\u6B66\u5C06',members:['\u5173\u7FBD','\u5F20\u98DE','\u8D75\u4E91','\u8BF8\u845B\u4EAE','\u5E9E\u7EDF','\u9EC4\u5FE0','\u9A6C\u8D85'],relationships:{'\u5173\u7FBD':'\u4E49\u5F1F\u517C\u519B\u56E2\u7EDF\u5E05','\u5F20\u98DE':'\u4E49\u5F1F\u517C\u5148\u950B\u5927\u5C06','\u8D75\u4E91':'\u5FC3\u8179\u7231\u5C06','\u8BF8\u845B\u4EAE':'\u9996\u5E2D\u519B\u5E08\uFF0C\u5982\u9C7C\u5F97\u6C34','\u5E9E\u7EDF':'\u526F\u519B\u5E08\uFF0C\u5947\u8C0B\u767E\u51FA','\u9EC4\u5FE0':'\u8001\u5F53\u76CA\u58EE\u7684\u731B\u5C06','\u9A6C\u8D85':'\u5F52\u964D\u7684\u897F\u51C9\u864E\u5C06'}}
|
|
1888
|
+
],
|
|
1207
1889
|
agents:[
|
|
1208
1890
|
{name:'\u5173\u7FBD',capabilities:['\u6B66\u827A','\u7EDF\u5175','\u9547\u5B88\u8981\u5730','\u6C34\u519B\u6307\u6325'],relationship:'\u4E49\u5F1F\uFF0C\u6843\u56ED\u7ED3\u4E49\u4E8C\u5F1F\uFF0C\u6700\u4FE1\u4EFB\u7684\u5144\u5F1F\u548C\u5927\u5C06'},
|
|
1209
1891
|
{name:'\u5F20\u98DE',capabilities:['\u6B66\u827A','\u5148\u950B\u7A81\u51FB','\u9A91\u5175\u6307\u6325','\u5A01\u6151\u654C\u519B'],relationship:'\u4E49\u5F1F\uFF0C\u6843\u56ED\u7ED3\u4E49\u4E09\u5F1F\uFF0C\u6027\u5982\u70C8\u706B\u4F46\u5FE0\u5FC3\u803F\u803F'},
|
|
@@ -1218,6 +1900,9 @@ const DEMOS={
|
|
|
1218
1900
|
emoji:'💻',title:'\u4E92\u8054\u7F51\u5927\u5382',role:'\u4F60\u662F\u6280\u672F\u603B\u76D1',
|
|
1219
1901
|
desc:'\u5E26\u9886\u4E00\u652F\u5168\u6808\u56E2\u961F\uFF0C\u4ECE\u524D\u7AEF\u5230\u8FD0\u7EF4\u4E00\u5E94\u4FF1\u5168\u3002\u5E94\u5BF9\u9AD8\u5E76\u53D1\u3001\u641E AI\u3001\u4E0A\u7EBF\u65B0\u7CFB\u7EDF\u3002',
|
|
1220
1902
|
prompt:'\u4E0A\u7EBF\u4E00\u4E2A AI \u667A\u80FD\u5BA2\u670D\u7CFB\u7EDF\uFF0C\u5305\u62EC\u524D\u7AEF\u754C\u9762\u3001\u540E\u7AEF API\u3001\u63A8\u8350\u7B97\u6CD5\u3001\u538B\u529B\u6D4B\u8BD5\u548C\u7070\u5EA6\u53D1\u5E03\u65B9\u6848',
|
|
1903
|
+
teams:[
|
|
1904
|
+
{name:'\u4E92\u8054\u7F51\u5927\u5382',description:'\u6280\u672F\u603B\u76D1\u76F4\u7BA1\u7684\u5168\u6808\u56E2\u961F',members:['\u524D\u7AEF\u8001\u674E','\u540E\u7AEF\u5927\u738B','\u7B97\u6CD5\u5C0F\u9648','\u4EA7\u54C1\u7ECF\u7406 Amy','\u8BBE\u8BA1\u5E08\u5C0F\u6797','\u6D4B\u8BD5\u8D1F\u8D23\u4EBA\u8001\u8D75','\u8FD0\u7EF4 DevOps \u963F\u6770'],relationships:{'\u524D\u7AEF\u8001\u674E':'\u524D\u7AEFTL\uFF0C\u6838\u5FC3\u9AA8\u5E72','\u540E\u7AEF\u5927\u738B':'\u67B6\u6784\u5E08\uFF0C\u6280\u672F\u51B3\u7B56\u8005','\u7B97\u6CD5\u5C0F\u9648':'\u7B97\u6CD5\u5DE5\u7A0B\u5E08\uFF0C\u9700\u8981\u6307\u5BFC','\u4EA7\u54C1\u7ECF\u7406 Amy':'\u4EA7\u54C1\u8D1F\u8D23\u4EBA','\u8BBE\u8BA1\u5E08\u5C0F\u6797':'\u8D44\u6DF1\u8BBE\u8BA1\u5E08','\u6D4B\u8BD5\u8D1F\u8D23\u4EBA\u8001\u8D75':'\u8D28\u91CF\u628A\u5173\u4EBA','\u8FD0\u7EF4 DevOps \u963F\u6770':'SRE\u8D1F\u8D23\u4EBA'}}
|
|
1905
|
+
],
|
|
1221
1906
|
agents:[
|
|
1222
1907
|
{name:'\u524D\u7AEF\u8001\u674E',capabilities:['React','TypeScript','Next.js','\u79FB\u52A8\u7AEF\u9002\u914D','\u6027\u80FD\u4F18\u5316'],relationship:'P7 \u524D\u7AEF TL\uFF0C\u8DDF\u4E86\u4F60\u4E09\u5E74\uFF0C\u6280\u672F\u8FC7\u786C\u4F46\u6700\u8FD1\u6709\u70B9\u5026\u6020'},
|
|
1223
1908
|
{name:'\u540E\u7AEF\u5927\u738B',capabilities:['Java','Go','\u5FAE\u670D\u52A1','\u6570\u636E\u5E93\u8BBE\u8BA1','\u9AD8\u5E76\u53D1\u67B6\u6784'],relationship:'P8 \u540E\u7AEF\u67B6\u6784\u5E08\uFF0C\u6280\u672F\u5927\u62FF\uFF0C\u8BF4\u8BDD\u76F4\u6765\u76F4\u53BB'},
|
|
@@ -1232,6 +1917,9 @@ const DEMOS={
|
|
|
1232
1917
|
emoji:'🇺🇸',title:'\u7F8E\u56FD\u653F\u5E9C',role:'\u4F60\u662F\u7279\u6717\u666E (POTUS)',
|
|
1233
1918
|
desc:'Make the executive branch great again! \u7BA1\u7406\u4F60\u7684\u6838\u5FC3\u5185\u9601\u6210\u5458\uFF0C\u63A8\u884C\u653F\u7B56\u8BAE\u7A0B\u3002',
|
|
1234
1919
|
prompt:'\u5236\u5B9A\u4E00\u4E2A\u8BA9\u7F8E\u56FD\u5236\u9020\u4E1A\u56DE\u6D41\u7684\u7EFC\u5408\u8BA1\u5212\uFF0C\u9700\u8981\u5173\u7A0E\u653F\u7B56\u3001\u51CF\u7A0E\u65B9\u6848\u3001\u80FD\u6E90\u4FDD\u969C\u3001\u8FB9\u5883\u5B89\u5168\u914D\u5408\u548C\u653F\u5E9C\u6548\u7387\u4F18\u5316',
|
|
1920
|
+
teams:[
|
|
1921
|
+
{name:'\u7F8E\u56FD\u653F\u5E9C',description:'\u603B\u7EDF\u76F4\u5C5E\u6838\u5FC3\u5185\u9601',members:['Elon Musk','Marco Rubio','Pete Hegseth','Scott Bessent','Kristi Noem','Tulsi Gabbard','Robert F. Kennedy Jr.'],relationships:{'Elon Musk':'DOGE \u8D1F\u8D23\u4EBA\uFF0C\u6548\u7387\u6539\u9769\u63A8\u52A8\u8005','Marco Rubio':'\u56FD\u52A1\u537F\uFF0C\u5916\u4EA4\u603B\u7BA1','Pete Hegseth':'\u56FD\u9632\u90E8\u957F\uFF0C\u519B\u4E8B\u4E8B\u52A1','Scott Bessent':'\u8D22\u653F\u90E8\u957F\uFF0C\u9996\u5E2D\u7ECF\u6D4E\u987E\u95EE','Kristi Noem':'\u56FD\u571F\u5B89\u5168\u90E8\u957F\uFF0C\u8FB9\u5883\u5F3A\u786C\u6D3E','Tulsi Gabbard':'\u60C5\u62A5\u603B\u76D1\uFF0C\u5B89\u5168\u8BC4\u4F30','Robert F. Kennedy Jr.':'\u536B\u751F\u90E8\u957F\uFF0C\u533B\u7597\u6539\u9769'}}
|
|
1922
|
+
],
|
|
1235
1923
|
agents:[
|
|
1236
1924
|
{name:'Elon Musk',capabilities:['\u653F\u5E9C\u6548\u7387','\u6210\u672C\u524A\u51CF','\u79D1\u6280\u521B\u65B0','SpaceX','Tesla','\u793E\u4EA4\u5A92\u4F53'],relationship:'DOGE \u8D1F\u8D23\u4EBA\uFF0C\u4E16\u754C\u9996\u5BCC\uFF0CTwitter/X \u8001\u677F\uFF0C\u6700\u5177\u5F71\u54CD\u529B\u7684\u76DF\u53CB'},
|
|
1237
1925
|
{name:'Marco Rubio',capabilities:['\u5916\u4EA4\u653F\u7B56','\u62C9\u7F8E\u4E8B\u52A1','\u56FD\u9645\u8C08\u5224','\u5236\u88C1\u653F\u7B56','\u56FD\u5BB6\u5B89\u5168'],relationship:'\u56FD\u52A1\u537F\uFF0C\u4F5B\u7F57\u91CC\u8FBE\u53C2\u8BAE\u5458\uFF0C\u66FE\u7ECF\u7684\u7ADE\u9009\u5BF9\u624B\u53D8\u5FE0\u5B9E\u652F\u6301\u8005'},
|
|
@@ -1274,12 +1962,31 @@ window.loadDemo=async function(key){
|
|
|
1274
1962
|
}
|
|
1275
1963
|
let ok=0;
|
|
1276
1964
|
const demoAgentIds=[];
|
|
1965
|
+
const agentNameToId={};
|
|
1277
1966
|
for(const a of demo.agents){
|
|
1278
1967
|
try{
|
|
1279
1968
|
const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:a.name,capabilities:a.capabilities,relationship:a.relationship})});
|
|
1280
|
-
if(r.ok){ok++;const d=await r.json();demoAgentIds.push(d.agent_id);}
|
|
1969
|
+
if(r.ok){ok++;const d=await r.json();demoAgentIds.push(d.agent_id);agentNameToId[a.name]=d.agent_id;}
|
|
1281
1970
|
}catch{}
|
|
1282
1971
|
}
|
|
1972
|
+
// Create demo teams
|
|
1973
|
+
if(demo.teams){
|
|
1974
|
+
for(const tm of demo.teams){
|
|
1975
|
+
try{
|
|
1976
|
+
const tr=await fetch(API+'/teams',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:tm.name,description:tm.description||''})});
|
|
1977
|
+
if(tr.ok){
|
|
1978
|
+
const td=await tr.json();
|
|
1979
|
+
for(const mName of tm.members){
|
|
1980
|
+
const agentId=agentNameToId[mName];
|
|
1981
|
+
if(agentId){
|
|
1982
|
+
const rel=tm.relationships?tm.relationships[mName]||'':'';
|
|
1983
|
+
await fetch(API+'/teams/'+td.team_id+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({agent_id:agentId,relationship:rel})});
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}catch{}
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1283
1990
|
toast(demo.title+' \u573A\u666F\u5DF2\u52A0\u8F7D\uFF01'+ok+'/'+demo.agents.length+' \u4E2A\u8282\u70B9\u6CE8\u518C\u6210\u529F',true);
|
|
1284
1991
|
// Switch to pipeline and open AI planning with suggested prompt
|
|
1285
1992
|
load('fleet');
|
|
@@ -1324,6 +2031,8 @@ async function loadFleet(el){
|
|
|
1324
2031
|
const r=await fetch(API+'/nodes/status');
|
|
1325
2032
|
const d=await r.json();
|
|
1326
2033
|
cachedAgents=d.agents||[];
|
|
2034
|
+
// Also load teams
|
|
2035
|
+
try{const tr=await fetch(API+'/teams');const td=await tr.json();cachedTeams=td.teams||[]}catch{cachedTeams=[]}
|
|
1327
2036
|
let h='<div class="section-hd"><h2>\u78B3\u57FA\u7B97\u529B\u6C60</h2><div style="display:flex;gap:6px"><button class="btn btn-ghost btn-sm" onclick="showDemoSelector()">🎮 Demo</button><button class="btn btn-primary btn-sm" onclick="showAddAgent()">+ \u6DFB\u52A0\u8282\u70B9</button></div></div>';
|
|
1328
2037
|
h+='<div class="stats">';
|
|
1329
2038
|
h+='<div class="stat"><div class="stat-val">'+d.total+'</div><div class="stat-lbl">\u603B\u8BA1</div></div>';
|
|
@@ -1337,21 +2046,64 @@ async function loadFleet(el){
|
|
|
1337
2046
|
h+=renderDemoCards();
|
|
1338
2047
|
el.innerHTML=h;return;
|
|
1339
2048
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
if(a.relationship)
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
if(a.avg_delivery_hours!==null)
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
for(const s of ['IDLE','BUSY','OFFLINE','OOM'])
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
2049
|
+
// Build agent card HTML helper
|
|
2050
|
+
function agentCard(a){
|
|
2051
|
+
let c='<div class="card"><div class="agent-header"><span class="dot '+a.status+'"></span><span class="agent-name">'+esc(a.name)+'</span></div>';
|
|
2052
|
+
c+='<div class="agent-id">'+a.agent_id+'</div>';
|
|
2053
|
+
if(a.relationship)c+='<div style="font-size:11px;color:var(--purple);margin-bottom:4px">👥 '+esc(a.relationship)+'</div>';
|
|
2054
|
+
c+='<div class="caps">';for(const cap of a.capabilities)c+='<span class="cap">'+esc(cap)+'</span>';c+='</div>';
|
|
2055
|
+
c+='<div class="agent-meta"><span>\u4EFB\u52A1: '+a.active_task_count+'</span>';
|
|
2056
|
+
if(a.avg_delivery_hours!==null)c+='<span>\u5E73\u5747\u4EA4\u4ED8: '+a.avg_delivery_hours+'h</span>';
|
|
2057
|
+
c+='</div>';
|
|
2058
|
+
c+='<div class="agent-actions">';
|
|
2059
|
+
c+='<select onchange="changeStatus(\\''+a.agent_id+'\\',this.value)">';
|
|
2060
|
+
for(const s of ['IDLE','BUSY','OFFLINE','OOM'])c+='<option'+(s===a.status?' selected':'')+'>'+s+'</option>';
|
|
2061
|
+
c+='</select>';
|
|
2062
|
+
c+='<button onclick="deleteAgent(\\''+a.agent_id+'\\')">\u5220\u9664</button>';
|
|
2063
|
+
c+='</div></div>';
|
|
2064
|
+
return c;
|
|
2065
|
+
}
|
|
2066
|
+
// Group agents by team
|
|
2067
|
+
const assignedIds=new Set();
|
|
2068
|
+
if(cachedTeams.length){
|
|
2069
|
+
for(const tm of cachedTeams){
|
|
2070
|
+
if(!tm.members||!tm.members.length)continue;
|
|
2071
|
+
const teamAgents=d.agents.filter(a=>tm.members.some(m=>m.agent_id===a.agent_id));
|
|
2072
|
+
if(!teamAgents.length)continue;
|
|
2073
|
+
for(const a of teamAgents)assignedIds.add(a.agent_id);
|
|
2074
|
+
h+='<div class="team-group">';
|
|
2075
|
+
h+='<div class="team-group-header" onclick="showTeamDetail(\\''+tm.team_id+'\\')">';
|
|
2076
|
+
h+='<span style="font-size:18px">👥</span> <span class="agent-name">'+esc(tm.name)+'</span>';
|
|
2077
|
+
if(tm.description)h+=' <span style="font-size:12px;color:var(--text-dim);margin-left:8px">'+esc(tm.description)+'</span>';
|
|
2078
|
+
h+=' <span style="font-size:11px;color:var(--accent);margin-left:8px">'+teamAgents.length+' \u4EBA</span>';
|
|
2079
|
+
h+='</div>';
|
|
2080
|
+
h+='<div class="grid">';
|
|
2081
|
+
for(const a of teamAgents)h+=agentCard(a);
|
|
2082
|
+
h+='</div></div>';
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
// Ungrouped agents
|
|
2086
|
+
const ungrouped=d.agents.filter(a=>!assignedIds.has(a.agent_id));
|
|
2087
|
+
if(ungrouped.length){
|
|
2088
|
+
if(assignedIds.size>0){
|
|
2089
|
+
h+='<div class="team-group">';
|
|
2090
|
+
h+='<div class="team-group-header"><span style="font-size:18px">👤</span> <span class="agent-name">\u672A\u5206\u7EC4</span> <span style="font-size:11px;color:var(--accent);margin-left:8px">'+ungrouped.length+' \u4EBA</span></div>';
|
|
2091
|
+
h+='<div class="grid">';
|
|
2092
|
+
for(const a of ungrouped)h+=agentCard(a);
|
|
2093
|
+
h+='</div></div>';
|
|
2094
|
+
} else {
|
|
2095
|
+
h+='<div class="grid">';
|
|
2096
|
+
for(const a of ungrouped)h+=agentCard(a);
|
|
2097
|
+
h+='</div>';
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
// Team management buttons
|
|
2101
|
+
h+='<div style="margin-top:24px;display:flex;gap:8px;align-items:center">';
|
|
2102
|
+
h+='<button class="btn btn-primary btn-sm" onclick="showCreateTeam()">+ \u521B\u5EFA\u56E2\u961F</button>';
|
|
2103
|
+
if(cachedTeams.length){
|
|
2104
|
+
for(const tm of cachedTeams){
|
|
2105
|
+
h+='<span class="team-badge" style="cursor:pointer" onclick="showTeamDetail(\\''+tm.team_id+'\\')">'+esc(tm.name)+'</span>';
|
|
2106
|
+
}
|
|
1355
2107
|
}
|
|
1356
2108
|
h+='</div>';
|
|
1357
2109
|
el.innerHTML=h;
|
|
@@ -1399,6 +2151,95 @@ window.deleteAgent=async function(id){
|
|
|
1399
2151
|
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
1400
2152
|
};
|
|
1401
2153
|
|
|
2154
|
+
// \u2500\u2500\u2500 Team Management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2155
|
+
window.showCreateTeam=function(){
|
|
2156
|
+
const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';
|
|
2157
|
+
ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});
|
|
2158
|
+
let membersHtml='<div id="team-members-list">';
|
|
2159
|
+
for(const a of cachedAgents){
|
|
2160
|
+
membersHtml+='<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">';
|
|
2161
|
+
membersHtml+='<label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px"><input type="checkbox" class="tm-check" value="'+a.agent_id+'"/><span class="dot '+a.status+'" style="width:8px;height:8px"></span>'+esc(a.name)+'</label>';
|
|
2162
|
+
membersHtml+='<input class="tm-rel" data-agent="'+a.agent_id+'" placeholder="\u56E2\u961F\u4E2D\u7684\u5173\u7CFB" style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:11px;color:var(--text);outline:none"/>';
|
|
2163
|
+
membersHtml+='</div>';
|
|
2164
|
+
}
|
|
2165
|
+
membersHtml+='</div>';
|
|
2166
|
+
ov.innerHTML='<div class="form-card"><h3>+ \u521B\u5EFA\u56E2\u961F</h3>'
|
|
2167
|
+
+'<div class="fg"><label>\u56E2\u961F\u540D\u79F0</label><input id="tm-name" placeholder="\u4F8B: \u524D\u7AEF\u7EC4"/></div>'
|
|
2168
|
+
+'<div class="fg"><label>\u56E2\u961F\u63CF\u8FF0 <span style="color:var(--text-dim);font-weight:400">(\u53EF\u9009)</span></label><input id="tm-desc" placeholder="\u4F8B: \u8D1F\u8D23\u6240\u6709\u524D\u7AEF\u9875\u9762\u5F00\u53D1"/></div>'
|
|
2169
|
+
+'<div class="fg"><label>\u9009\u62E9\u6210\u5458\u53CA\u56E2\u961F\u5173\u7CFB</label>'+membersHtml+'</div>'
|
|
2170
|
+
+'<div class="btn-group"><button class="btn btn-primary" onclick="submitTeam()">\u521B\u5EFA\u56E2\u961F</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button></div></div>';
|
|
2171
|
+
document.body.appendChild(ov);
|
|
2172
|
+
document.getElementById('tm-name').focus();
|
|
2173
|
+
};
|
|
2174
|
+
window.submitTeam=async function(){
|
|
2175
|
+
const name=document.getElementById('tm-name').value.trim();
|
|
2176
|
+
const desc=document.getElementById('tm-desc').value.trim();
|
|
2177
|
+
if(!name){toast('\u8BF7\u8F93\u5165\u56E2\u961F\u540D\u79F0',false);return}
|
|
2178
|
+
try{
|
|
2179
|
+
const r=await fetch(API+'/teams',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,description:desc})});
|
|
2180
|
+
const d=await r.json();
|
|
2181
|
+
if(!r.ok){toast(d.error||'\u521B\u5EFA\u5931\u8D25',false);return}
|
|
2182
|
+
const checks=document.querySelectorAll('.tm-check:checked');
|
|
2183
|
+
for(const chk of checks){
|
|
2184
|
+
const agentId=chk.value;
|
|
2185
|
+
const relInput=document.querySelector('.tm-rel[data-agent="'+agentId+'"]');
|
|
2186
|
+
const rel=relInput?relInput.value.trim():'';
|
|
2187
|
+
await fetch(API+'/teams/'+d.team_id+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({agent_id:agentId,relationship:rel})});
|
|
2188
|
+
}
|
|
2189
|
+
toast('\u56E2\u961F\u300C'+name+'\u300D\u521B\u5EFA\u6210\u529F\uFF01',true);
|
|
2190
|
+
document.getElementById('overlay').remove();
|
|
2191
|
+
load('fleet');
|
|
2192
|
+
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
2193
|
+
};
|
|
2194
|
+
window.showTeamDetail=async function(teamId){
|
|
2195
|
+
const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';
|
|
2196
|
+
ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});
|
|
2197
|
+
ov.innerHTML='<div class="form-card"><h3>\u56E2\u961F\u8BE6\u60C5</h3><div class="spinner-wrap"><div class="spinner"></div></div></div>';
|
|
2198
|
+
document.body.appendChild(ov);
|
|
2199
|
+
try{
|
|
2200
|
+
const r=await fetch(API+'/teams/'+teamId);
|
|
2201
|
+
const tm=await r.json();
|
|
2202
|
+
if(!r.ok){toast(tm.error||'\u52A0\u8F7D\u5931\u8D25',false);ov.remove();return}
|
|
2203
|
+
const fc=ov.querySelector('.form-card');
|
|
2204
|
+
let h='<h3>'+esc(tm.name)+'</h3>';
|
|
2205
|
+
if(tm.description)h+='<div style="font-size:12px;color:var(--text-dim);margin-bottom:16px">'+esc(tm.description)+'</div>';
|
|
2206
|
+
h+='<div style="font-size:11px;color:var(--text-dim);margin-bottom:12px;font-family:var(--font-mono)">'+tm.team_id+'</div>';
|
|
2207
|
+
if(tm.members&&tm.members.length){
|
|
2208
|
+
h+='<div style="font-size:13px;font-weight:600;margin-bottom:8px">\u6210\u5458 ('+tm.members.length+')</div>';
|
|
2209
|
+
for(const m of tm.members){
|
|
2210
|
+
h+='<div style="display:flex;align-items:center;gap:8px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:8px;margin-bottom:6px">';
|
|
2211
|
+
h+='<span class="dot '+(m.agent_status||'IDLE')+'" style="width:8px;height:8px"></span>';
|
|
2212
|
+
h+='<span style="font-size:13px;font-weight:500">'+esc(m.agent_name||m.agent_id)+'</span>';
|
|
2213
|
+
if(m.relationship)h+='<span style="font-size:11px;color:var(--purple);margin-left:auto">'+esc(m.relationship)+'</span>';
|
|
2214
|
+
h+='<button style="margin-left:'+(m.relationship?'8px':'auto')+';background:none;border:none;color:var(--red);cursor:pointer;font-size:14px" onclick="removeTeamMember(\\''+teamId+'\\',\\''+m.agent_id+'\\')">×</button>';
|
|
2215
|
+
h+='</div>';
|
|
2216
|
+
}
|
|
2217
|
+
}else{
|
|
2218
|
+
h+='<div style="font-size:12px;color:var(--text-dim)">\u6682\u65E0\u6210\u5458</div>';
|
|
2219
|
+
}
|
|
2220
|
+
h+='<div class="btn-group"><button class="btn btn-danger btn-sm" onclick="deleteTeam(\\''+teamId+'\\')">\u5220\u9664\u56E2\u961F</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
2221
|
+
fc.innerHTML=h;
|
|
2222
|
+
}catch{toast('\u52A0\u8F7D\u5931\u8D25',false);ov.remove()}
|
|
2223
|
+
};
|
|
2224
|
+
window.removeTeamMember=async function(teamId,agentId){
|
|
2225
|
+
try{
|
|
2226
|
+
await fetch(API+'/teams/'+teamId+'/members/'+agentId,{method:'DELETE'});
|
|
2227
|
+
toast('\u6210\u5458\u5DF2\u79FB\u9664',true);
|
|
2228
|
+
document.getElementById('overlay').remove();
|
|
2229
|
+
showTeamDetail(teamId);
|
|
2230
|
+
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
2231
|
+
};
|
|
2232
|
+
window.deleteTeam=async function(teamId){
|
|
2233
|
+
if(!confirm('\u786E\u5B9A\u5220\u9664\u6B64\u56E2\u961F\uFF1F'))return;
|
|
2234
|
+
try{
|
|
2235
|
+
const r=await fetch(API+'/teams/'+teamId,{method:'DELETE'});
|
|
2236
|
+
if(!r.ok){toast('\u5220\u9664\u5931\u8D25',false);return}
|
|
2237
|
+
toast('\u56E2\u961F\u5DF2\u5220\u9664',true);
|
|
2238
|
+
document.getElementById('overlay').remove();
|
|
2239
|
+
load('fleet');
|
|
2240
|
+
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
2241
|
+
};
|
|
2242
|
+
|
|
1402
2243
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1403
2244
|
// PIPELINE
|
|
1404
2245
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
@@ -1430,7 +2271,7 @@ async function loadPipeline(el){
|
|
|
1430
2271
|
allTasks.push(...j.tasks);
|
|
1431
2272
|
h+='<div class="job-card"><div class="job-hd"><span class="job-title">'+esc(j.original_prompt)+'</span><span class="job-id">'+j.job_id+'</span></div>';
|
|
1432
2273
|
h+='<div class="pbar-wrap"><div class="pbar-label">'+res+'/'+tot+' \u5DF2\u5B8C\u6210 ('+pct+'%)</div><div class="pbar"><div class="pbar-fill" style="width:'+pct+'%"></div></div></div>';
|
|
1433
|
-
if(pct===100)h+='<div style="margin-bottom:12px"><button class="btn btn-green btn-sm" onclick="reviewJob(\\''+j.job_id+'\\')">AI \u805A\u5408\u5BA1\u67E5</button></div>';
|
|
2274
|
+
if(pct===100)h+='<div style="margin-bottom:12px;display:flex;gap:6px;flex-wrap:wrap"><button class="btn btn-green btn-sm" onclick="reviewJob(\\''+j.job_id+'\\')">AI \u805A\u5408\u5BA1\u67E5</button><button class="btn btn-primary btn-sm" onclick="showEvalDialog(\\''+j.job_id+'\\')">📊 \u751F\u6210\u7EE9\u6548\u8BC4\u4EF7</button></div>';
|
|
1434
2275
|
h+='<div class="kanban"><div class="lane"><div class="lane-hd y">\u5DF2\u5206\u53D1 ('+dispatched.length+')</div>'+dispatched.map(tcard).join('')+'</div>';
|
|
1435
2276
|
h+='<div class="lane"><div class="lane-hd r">\u5DF2\u8D85\u65F6 ('+overdue.length+')</div>'+overdue.map(tcard).join('')+'</div>';
|
|
1436
2277
|
h+='<div class="lane"><div class="lane-hd g">\u5DF2\u4EA4\u4ED8 ('+done.length+')</div>'+done.map(tcard).join('')+'</div></div></div>';
|
|
@@ -1488,9 +2329,24 @@ function renderPlanStep1(ov){
|
|
|
1488
2329
|
const el=document.getElementById('agent-chip-area');
|
|
1489
2330
|
if(el)el.innerHTML=renderChips(chipFilter);
|
|
1490
2331
|
};
|
|
2332
|
+
window._selectTeam=function(teamId){
|
|
2333
|
+
selectedTeamId=teamId;
|
|
2334
|
+
if(teamId){
|
|
2335
|
+
const tm=cachedTeams.find(t=>t.team_id===teamId);
|
|
2336
|
+
if(tm&&tm.members){
|
|
2337
|
+
selectedAgentIds=new Set(tm.members.map(m=>m.agent_id));
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
const el=document.getElementById('agent-chip-area');
|
|
2341
|
+
if(el)el.innerHTML=renderChips(chipFilter);
|
|
2342
|
+
// Re-render to show team hint
|
|
2343
|
+
renderPlanStep1(document.getElementById('overlay'));
|
|
2344
|
+
};
|
|
1491
2345
|
|
|
1492
2346
|
ov.innerHTML='<div class="form-card"><h3>AI \u667A\u80FD\u89C4\u5212</h3>'
|
|
1493
|
-
+'<div class="fg"><label>\u8F93\u5165\u4F60\u7684\u9700\u6C42</label><textarea id="plan-prompt" rows="3" placeholder="\u4F8B: \u5B8C\u6210\u9996\u9875\u91CD\u6784\uFF0C\u5305\u62EC\u5BFC\u822A\u680F\u3001\u5185\u5BB9\u533A\u548C\u9875\u811A\u7684\u54CD\u5E94\u5F0F\u6539\u7248" style="font-family:var(--font-sans);font-size:13px">'+esc(pendingDemoPrompt)+'</textarea></div>'
|
|
2347
|
+
+'<div class="fg"><label>\u8F93\u5165\u4F60\u7684\u9700\u6C42</label><div class="md-editor"><textarea id="plan-prompt" rows="3" placeholder="\u4F8B: \u5B8C\u6210\u9996\u9875\u91CD\u6784\uFF0C\u5305\u62EC\u5BFC\u822A\u680F\u3001\u5185\u5BB9\u533A\u548C\u9875\u811A\u7684\u54CD\u5E94\u5F0F\u6539\u7248" style="font-family:var(--font-sans);font-size:13px">'+esc(pendingDemoPrompt)+'</textarea><button class="md-expand-btn" onclick="toggleFullscreen(this)" title="\u5C55\u5F00/\u6536\u8D77">⛶</button></div></div>'
|
|
2348
|
+
+'<div class="fg"><label>\u6309\u56E2\u961F\u9009\u62E9 <span style="color:var(--text-dim);font-weight:400">(\u53EF\u9009)</span></label><select id="plan-team" onchange="window._selectTeam(this.value)"><option value="">-- \u4E0D\u6309\u56E2\u961F --</option>'+cachedTeams.map(t=>'<option value="'+t.team_id+'"'+(selectedTeamId===t.team_id?' selected':'')+'>'+esc(t.name)+'</option>').join('')+'</select>'
|
|
2349
|
+
+(selectedTeamId?'<div class="hint" style="color:var(--accent)">👥 \u4F7F\u7528\u56E2\u961F\u5173\u7CFB\u4E0A\u4E0B\u6587</div>':'')+'</div>'
|
|
1494
2350
|
+'<div class="fg"><label>\u9009\u62E9\u53C2\u4E0E\u7684\u78B3\u57FA\u8282\u70B9 <span style="color:var(--text-dim);font-weight:400">(\u9ED8\u8BA4\u9009\u4E2D\u7A7A\u95F2\u8282\u70B9)</span></label>'
|
|
1495
2351
|
+'<div id="agent-chip-area">'+renderChips('all')+'</div></div>'
|
|
1496
2352
|
+'<div class="btn-group">'
|
|
@@ -1511,7 +2367,9 @@ window.planWithAI=async function(){
|
|
|
1511
2367
|
fc.innerHTML='<h3>AI \u667A\u80FD\u89C4\u5212</h3><div class="spinner-wrap"><div class="spinner"></div><div class="spinner-text">AI \u6B63\u5728\u5206\u6790\u9700\u6C42\u3001\u5339\u914D\u8282\u70B9\u3001\u751F\u6210\u8BDD\u672F...</div></div>';
|
|
1512
2368
|
|
|
1513
2369
|
try{
|
|
1514
|
-
const
|
|
2370
|
+
const reqBody={prompt,agent_ids:Array.from(selectedAgentIds)};
|
|
2371
|
+
if(selectedTeamId)reqBody.team_id=selectedTeamId;
|
|
2372
|
+
const r=await fetch(API+'/jobs/plan',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(reqBody)});
|
|
1515
2373
|
const d=await r.json();
|
|
1516
2374
|
if(!r.ok){
|
|
1517
2375
|
toast(d.error||'\u89C4\u5212\u5931\u8D25',false);
|
|
@@ -1624,18 +2482,59 @@ window.reviewJob=async function(jobId){
|
|
|
1624
2482
|
ov.innerHTML='<div class="form-card"><h3>AI \u805A\u5408\u5BA1\u67E5</h3><div class="spinner-wrap"><div class="spinner"></div><div class="spinner-text">AI \u6B63\u5728\u5BA1\u67E5\u6240\u6709\u4EA4\u4ED8\u7269...</div></div></div>';
|
|
1625
2483
|
document.body.appendChild(ov);
|
|
1626
2484
|
try{
|
|
1627
|
-
const r=await fetch(API+'/jobs/'+jobId+'/review',{method:'POST'});
|
|
2485
|
+
const r=await fetch(API+'/jobs/'+jobId+'/review',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({})});
|
|
1628
2486
|
const d=await r.json();
|
|
1629
2487
|
if(!r.ok){toast(d.error||'\u5BA1\u67E5\u5931\u8D25',false);ov.remove();return}
|
|
1630
2488
|
const fc=ov.querySelector('.form-card');
|
|
1631
2489
|
fc.innerHTML='<h3>AI \u805A\u5408\u5BA1\u67E5</h3>'
|
|
1632
2490
|
+'<div style="font-size:12px;color:var(--text-dim);margin-bottom:12px">\u9700\u6C42: '+esc(d.original_prompt)+'</div>'
|
|
1633
|
-
+'<div class="result-display" style="max-height:400px">'+esc(d.review).replace(/\\n/g,'<br>')+'</div>'
|
|
2491
|
+
+'<div class="md-editor"><div class="result-display" style="max-height:400px">'+esc(d.review).replace(/\\n/g,'<br>')+'</div></div>'
|
|
1634
2492
|
+'<div style="font-size:10px;color:var(--text-dim);margin-top:8px;font-family:var(--font-mono)">\u5BA1\u67E5\u65F6\u95F4: '+new Date(d.reviewed_at).toLocaleString()+'</div>'
|
|
1635
2493
|
+'<div class="btn-group"><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
1636
2494
|
}catch(e){toast('\u7F51\u7EDC\u9519\u8BEF: '+e.message,false);ov.remove()}
|
|
1637
2495
|
};
|
|
1638
2496
|
|
|
2497
|
+
window.showEvalDialog=function(jobId){
|
|
2498
|
+
const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';
|
|
2499
|
+
ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});
|
|
2500
|
+
ov.innerHTML='<div class="form-card"><h3>📊 \u751F\u6210\u7EE9\u6548\u8BC4\u4EF7</h3>'
|
|
2501
|
+
+'<div class="fg"><label>\u8BC4\u5206\u4F53\u7CFB</label><select id="eval-system">'
|
|
2502
|
+
+'<option value="ali">\u963F\u91CC\u7EE9\u6548 (3.25 / 3.5 / 3.5+ / 3.75 / 4.0)</option>'
|
|
2503
|
+
+'<option value="letter">SABCD \u7B49\u7EA7 (S / A / B / C / D)</option>'
|
|
2504
|
+
+'<option value="em">EM \u4F53\u7CFB (E / M+ / M / M- / L)</option>'
|
|
2505
|
+
+'</select></div>'
|
|
2506
|
+
+'<div class="btn-group"><button class="btn btn-primary" onclick="generateEval(\\''+jobId+'\\')">\u751F\u6210\u8BC4\u4EF7</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button></div></div>';
|
|
2507
|
+
document.body.appendChild(ov);
|
|
2508
|
+
};
|
|
2509
|
+
window.generateEval=async function(jobId){
|
|
2510
|
+
const ratingSystem=document.getElementById('eval-system').value;
|
|
2511
|
+
const ov=document.getElementById('overlay');
|
|
2512
|
+
const fc=ov.querySelector('.form-card');
|
|
2513
|
+
fc.innerHTML='<h3>📊 \u751F\u6210\u7EE9\u6548\u8BC4\u4EF7</h3><div class="spinner-wrap"><div class="spinner"></div><div class="spinner-text">AI \u6B63\u5728\u8BC4\u4F30\u6BCF\u4E2A\u78B3\u57FA\u8282\u70B9\u7684\u8868\u73B0...</div></div>';
|
|
2514
|
+
try{
|
|
2515
|
+
const r=await fetch(API+'/evaluations/generate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({job_id:jobId,rating_system:ratingSystem})});
|
|
2516
|
+
const d=await r.json();
|
|
2517
|
+
if(!r.ok){toast(d.error||'\u8BC4\u4EF7\u751F\u6210\u5931\u8D25',false);ov.remove();return}
|
|
2518
|
+
let h='<h3>📊 \u7EE9\u6548\u8BC4\u4EF7\u7ED3\u679C</h3>';
|
|
2519
|
+
h+='<div style="font-size:11px;color:var(--text-dim);margin-bottom:16px;font-family:var(--font-mono)">\u751F\u6210\u65F6\u95F4: '+new Date(d.generated_at).toLocaleString()+'</div>';
|
|
2520
|
+
const ratingTiers={ali:['4.0','3.75'],letter:['S','A'],em:['E','M+']};
|
|
2521
|
+
const lowTiers={ali:['3.25'],letter:['D'],em:['L']};
|
|
2522
|
+
for(const ev of d.evaluations){
|
|
2523
|
+
const agent=cachedAgents.find(a=>a.agent_id===ev.agent_id);
|
|
2524
|
+
const isTop=(ratingTiers[ratingSystem]||[]).includes(ev.rating);
|
|
2525
|
+
const isLow=(lowTiers[ratingSystem]||[]).includes(ev.rating);
|
|
2526
|
+
const tier=isTop?'top':isLow?'low':'mid';
|
|
2527
|
+
h+='<div style="padding:12px;background:var(--bg);border:1px solid var(--border);border-radius:8px;margin-bottom:8px">';
|
|
2528
|
+
h+='<div style="display:flex;align-items:center;gap:10px;margin-bottom:6px"><span style="font-weight:600;font-size:13px">'+esc(agent?agent.name:ev.agent_id)+'</span><span class="eval-badge '+tier+'">'+esc(ev.rating)+'</span></div>';
|
|
2529
|
+
h+='<div style="font-size:12px;color:var(--text-dim)">'+esc(ev.comment)+'</div>';
|
|
2530
|
+
h+='<div style="font-size:10px;color:var(--text-dim);margin-top:4px;font-family:var(--font-mono)">\u4EFB\u52A1: '+ev.trace_id+'</div>';
|
|
2531
|
+
h+='</div>';
|
|
2532
|
+
}
|
|
2533
|
+
h+='<div class="btn-group"><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
2534
|
+
fc.innerHTML=h;
|
|
2535
|
+
}catch(e){toast('\u7F51\u7EDC\u9519\u8BEF: '+e.message,false);ov.remove()}
|
|
2536
|
+
};
|
|
2537
|
+
|
|
1639
2538
|
// \u2500\u2500\u2500 Task Detail Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1640
2539
|
window.showTaskDetail=function(traceId){
|
|
1641
2540
|
const t=allTasks.find(x=>x.trace_id===traceId);
|
|
@@ -1656,18 +2555,17 @@ window.showTaskDetail=function(traceId){
|
|
|
1656
2555
|
h+='<div class="detail-row"><div class="detail-label">\u622A\u6B62\u65F6\u95F4</div><div class="detail-value" style="font-family:var(--font-mono)">'+new Date(t.deadline).toLocaleString()+'</div></div>';
|
|
1657
2556
|
|
|
1658
2557
|
if(t.status==='RESOLVED'&&t.result_data){
|
|
1659
|
-
// Show result
|
|
2558
|
+
// Show result + reject option (manager can reject after reviewing)
|
|
1660
2559
|
let resultText='';
|
|
1661
2560
|
if(typeof t.result_data==='object'&&t.result_data){resultText=t.result_data.text||JSON.stringify(t.result_data,null,2)}else{resultText=String(t.result_data||'')}
|
|
1662
|
-
h+='<div class="detail-row"><div class="detail-label">\u4EA4\u4ED8\u7ED3\u679C</div><div class="result-display">'+esc(resultText)+'</div></div>';
|
|
1663
|
-
h+='<div class="btn-group"><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
2561
|
+
h+='<div class="detail-row"><div class="detail-label">\u4EA4\u4ED8\u7ED3\u679C</div><div class="md-editor"><div class="result-display">'+esc(resultText)+'</div></div></div>';
|
|
2562
|
+
h+='<div class="btn-group"><button class="btn btn-danger btn-sm" onclick="taskReject(\\''+t.trace_id+'\\')">\u6253\u56DE\u91CD\u505A (Reject)</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
1664
2563
|
}else{
|
|
1665
|
-
// Input for resume
|
|
1666
|
-
h+='<div class="fg" style="margin-top:16px"><label>\u63D0\u4EA4 Human \u4EA4\u4ED8\u7269</label><textarea id="td-payload" rows="4" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001GitHub PR/Commit URL\u3001\u5DE5\u4F5C\u6C47\u62A5\u7B49..."></textarea><div class="hint">\u652F\u6301\u8D34 GitHub URL\uFF08PR\u3001Commit\u3001Issue\uFF09\uFF0CAI \u5BA1\u67E5\u65F6\u4F1A\u5206\u6790</div></div>';
|
|
2564
|
+
// Input for resume (no reject for unsubmitted tasks)
|
|
2565
|
+
h+='<div class="fg" style="margin-top:16px"><label>\u63D0\u4EA4 Human \u4EA4\u4ED8\u7269</label><div class="md-editor"><textarea id="td-payload" rows="4" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001GitHub PR/Commit URL\u3001\u5DE5\u4F5C\u6C47\u62A5\u7B49..."></textarea><button class="md-expand-btn" onclick="toggleFullscreen(this)" title="\u5C55\u5F00/\u6536\u8D77">⛶</button></div><div class="hint">\u652F\u6301\u8D34 GitHub URL\uFF08PR\u3001Commit\u3001Issue\uFF09\uFF0CAI \u5BA1\u67E5\u65F6\u4F1A\u5206\u6790</div></div>';
|
|
1667
2566
|
h+='<div class="btn-group">';
|
|
1668
2567
|
h+='<button class="btn btn-primary btn-sm" onclick="simulateDelivery(\\''+t.trace_id+'\\')">🤖 \u6A21\u62DF\u4EA4\u4ED8</button>';
|
|
1669
2568
|
h+='<button class="btn btn-green" onclick="taskResume(\\''+t.trace_id+'\\')">\u63D0\u4EA4\u4EA4\u4ED8 (Resume)</button>';
|
|
1670
|
-
h+='<button class="btn btn-danger" onclick="taskReject(\\''+t.trace_id+'\\')">\u6253\u56DE\u91CD\u505A (Reject)</button>';
|
|
1671
2569
|
h+='<button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button>';
|
|
1672
2570
|
h+='</div>';
|
|
1673
2571
|
}
|
|
@@ -1723,7 +2621,7 @@ function loadTerminal(el){
|
|
|
1723
2621
|
el.innerHTML='<div class="section-hd"><h2>I/O \u4EA4\u4ED8\u7EC8\u7AEF</h2></div>'
|
|
1724
2622
|
+'<div class="form-card" style="margin-top:12px"><h3>> \u63D0\u4EA4\u78B3\u57FA\u8282\u70B9\u4EA7\u51FA</h3>'
|
|
1725
2623
|
+'<div class="fg"><label>Trace ID (\u8FFD\u8E2A\u7801)</label><input id="t-tid" placeholder="\u4F8B: TK-9527"/></div>'
|
|
1726
|
-
+'<div class="fg"><label>\u4EA4\u4ED8\u8F7D\u8377</label><textarea id="t-payload" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001GitHub PR/Commit URL\u3001\u5DE5\u4F5C\u6C47\u62A5\u7B49..."></textarea><div class="hint">\u652F\u6301\u8D34 GitHub URL\uFF08PR\u3001Commit\u3001Issue\uFF09\uFF0CAI \u5BA1\u67E5\u65F6\u4F1A\u5206\u6790</div></div>'
|
|
2624
|
+
+'<div class="fg"><label>\u4EA4\u4ED8\u8F7D\u8377</label><div class="md-editor"><textarea id="t-payload" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001GitHub PR/Commit URL\u3001\u5DE5\u4F5C\u6C47\u62A5\u7B49..."></textarea><button class="md-expand-btn" onclick="toggleFullscreen(this)" title="\u5C55\u5F00/\u6536\u8D77">⛶</button></div><div class="hint">\u652F\u6301\u8D34 GitHub URL\uFF08PR\u3001Commit\u3001Issue\uFF09\uFF0CAI \u5BA1\u67E5\u65F6\u4F1A\u5206\u6790</div></div>'
|
|
1727
2625
|
+'<div class="btn-group"><button class="btn btn-primary" onclick="doResume()">\u63D0\u4EA4\u5E76\u6062\u590D (Resume)</button><button class="btn btn-danger" onclick="doReject()">\u6253\u56DE\u91CD\u505A (Reject)</button></div></div>';
|
|
1728
2626
|
}
|
|
1729
2627
|
window.doResume=async function(){
|
|
@@ -1772,11 +2670,12 @@ window.showSettings=async function(){
|
|
|
1772
2670
|
statusHtml='<div style="background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:12px;color:var(--red)">⚠ \u672A\u914D\u7F6E API Key\uFF0CAI \u89C4\u5212\u529F\u80FD\u4E0D\u53EF\u7528</div>';
|
|
1773
2671
|
}
|
|
1774
2672
|
fc.innerHTML='<h3>LLM \u8BBE\u7F6E</h3>'+statusHtml
|
|
1775
|
-
+'<div class="fg"><label
|
|
1776
|
-
+'<div class="fg"><label>API Key</label><input id="cfg-key" type="password" placeholder="'+(cfg.api_key_set?'\u5DF2\u914D\u7F6E\uFF0C\u7559\u7A7A\u5219\u4E0D\u4FEE\u6539':'\u8F93\u5165\u4F60\u7684 API Key...')+'"/><div class="hint">
|
|
2673
|
+
+'<div class="fg"><label>API \u683C\u5F0F</label><select id="cfg-provider"><option value="anthropic"'+(cfg.provider==='anthropic'||cfg.provider==='claude'?' selected':'')+'>Anthropic Messages API</option><option value="openai"'+(cfg.provider==='openai'?' selected':'')+'>OpenAI Chat Completions API</option><option value="responses"'+(cfg.provider==='responses'?' selected':'')+'>OpenAI Responses API</option></select><div class="hint">\u9009\u62E9 API \u683C\u5F0F\u3002\u81EA\u5B9A\u4E49 Base URL \u53EF\u8FDE\u63A5\u4EFB\u4F55\u517C\u5BB9\u670D\u52A1\u3002</div></div>'
|
|
2674
|
+
+'<div class="fg"><label>API Key</label><input id="cfg-key" type="password" placeholder="'+(cfg.api_key_set?'\u5DF2\u914D\u7F6E\uFF0C\u7559\u7A7A\u5219\u4E0D\u4FEE\u6539':'\u8F93\u5165\u4F60\u7684 API Key...')+'"/><div class="hint">Anthropic: sk-ant-... | OpenAI: sk-...</div></div>'
|
|
1777
2675
|
+'<div class="fg"><label>\u6A21\u578B <span style="color:var(--text-dim);font-weight:400">(\u53EF\u9009\uFF0C\u7559\u7A7A\u7528\u9ED8\u8BA4)</span></label><input id="cfg-model" value="'+esc(cfg.model||'')+'" placeholder="\u4F8B: claude-sonnet-4-20250514 / gpt-4o"/></div>'
|
|
1778
2676
|
+'<div class="fg"><label>API Base URL <span style="color:var(--text-dim);font-weight:400">(\u53EF\u9009\uFF0C\u7559\u7A7A\u7528\u5B98\u65B9\u5730\u5740)</span></label><input id="cfg-baseurl" value="'+esc(cfg.base_url||'')+'" placeholder="\u4F8B: https://your-proxy.com"/><div class="hint">\u79C1\u6709\u90E8\u7F72: \u586B\u5199\u4F60\u7684\u6A21\u578B\u670D\u52A1\u5730\u5740\uFF0C\u5982 vLLM / Ollama / Azure \u7B49</div></div>'
|
|
1779
|
-
+'<div class="btn-group"><button class="btn btn-primary" onclick="saveSettings()">\u4FDD\u5B58</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button></div>'
|
|
2677
|
+
+'<div class="btn-group"><button class="btn btn-primary" onclick="saveSettings()">\u4FDD\u5B58</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button></div>'
|
|
2678
|
+
+'<div style="margin-top:24px;padding-top:16px;border-top:1px solid var(--border)"><div style="font-size:12px;color:var(--text-dim);margin-bottom:8px">⚠ \u5371\u9669\u64CD\u4F5C</div><button class="btn btn-sm" style="background:var(--red);color:#fff" onclick="resetAllData()">\u4E00\u952E\u6E05\u7A7A\u6240\u6709\u6570\u636E</button><div class="hint">\u6E05\u7A7A\u6240\u6709\u8282\u70B9\u3001\u56E2\u961F\u3001\u4EFB\u52A1\u548C\u8BC4\u4EF7\u6570\u636E\uFF0C\u53EF\u91CD\u65B0\u52A0\u8F7D Demo \u573A\u666F\u3002LLM \u914D\u7F6E\u4E0D\u53D7\u5F71\u54CD\u3002</div></div>';
|
|
1780
2679
|
}catch{
|
|
1781
2680
|
ov.querySelector('.form-card').innerHTML='<h3>LLM \u8BBE\u7F6E</h3><p style="color:var(--red)">\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25</p>';
|
|
1782
2681
|
}
|
|
@@ -1797,6 +2696,16 @@ window.saveSettings=async function(){
|
|
|
1797
2696
|
document.getElementById('overlay').remove();
|
|
1798
2697
|
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
1799
2698
|
};
|
|
2699
|
+
window.resetAllData=async function(){
|
|
2700
|
+
if(!confirm('\u786E\u5B9A\u6E05\u7A7A\u6240\u6709\u6570\u636E\uFF1F\u8282\u70B9\u3001\u56E2\u961F\u3001\u4EFB\u52A1\u3001\u8BC4\u4EF7\u5C06\u5168\u90E8\u5220\u9664\u3002\\n\\nLLM \u914D\u7F6E\u4E0D\u53D7\u5F71\u54CD\uFF0C\u6E05\u7A7A\u540E\u53EF\u91CD\u65B0\u52A0\u8F7D Demo\u3002'))return;
|
|
2701
|
+
try{
|
|
2702
|
+
const r=await fetch(API+'/config/reset',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'});
|
|
2703
|
+
if(!r.ok){toast('\u6E05\u7A7A\u5931\u8D25',false);return}
|
|
2704
|
+
toast('\u6240\u6709\u6570\u636E\u5DF2\u6E05\u7A7A',true);
|
|
2705
|
+
const ov=document.getElementById('overlay');if(ov)ov.remove();
|
|
2706
|
+
load('fleet');
|
|
2707
|
+
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
2708
|
+
};
|
|
1800
2709
|
|
|
1801
2710
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1802
2711
|
// TABS & INIT
|
|
@@ -1839,6 +2748,8 @@ function createServer(port = 2026) {
|
|
|
1839
2748
|
app.use("/api/v1/tasks", tasks_default);
|
|
1840
2749
|
app.use("/api/v1/jobs", sync_default);
|
|
1841
2750
|
app.use("/api/v1/config", config_default);
|
|
2751
|
+
app.use("/api/v1/teams", teams_default);
|
|
2752
|
+
app.use("/api/v1/evaluations", evaluations_default);
|
|
1842
2753
|
const dashboardHtml = getDashboardHtml();
|
|
1843
2754
|
app.get("/", (_req, res) => {
|
|
1844
2755
|
res.type("html").send(dashboardHtml);
|