flock-core 0.5.0b71__py3-none-any.whl → 0.5.1__py3-none-any.whl
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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/agent.py +39 -1
- flock/artifacts.py +17 -10
- flock/cli.py +1 -1
- flock/dashboard/__init__.py +2 -0
- flock/dashboard/collector.py +282 -6
- flock/dashboard/events.py +6 -0
- flock/dashboard/graph_builder.py +563 -0
- flock/dashboard/launcher.py +11 -6
- flock/dashboard/models/__init__.py +1 -0
- flock/dashboard/models/graph.py +156 -0
- flock/dashboard/service.py +175 -14
- flock/dashboard/static_v2/assets/index-DFRnI_mt.js +111 -0
- flock/dashboard/static_v2/assets/index-fPLNdmp1.css +1 -0
- flock/dashboard/static_v2/index.html +13 -0
- flock/dashboard/websocket.py +2 -2
- flock/engines/dspy_engine.py +294 -20
- flock/frontend/README.md +6 -6
- flock/frontend/src/App.tsx +23 -31
- flock/frontend/src/__tests__/integration/graph-snapshot.test.tsx +647 -0
- flock/frontend/src/components/details/DetailWindowContainer.tsx +13 -17
- flock/frontend/src/components/details/MessageDetailWindow.tsx +439 -0
- flock/frontend/src/components/details/MessageHistoryTab.tsx +128 -53
- flock/frontend/src/components/details/RunStatusTab.tsx +79 -38
- flock/frontend/src/components/graph/AgentNode.test.tsx +3 -1
- flock/frontend/src/components/graph/AgentNode.tsx +8 -6
- flock/frontend/src/components/graph/GraphCanvas.tsx +13 -8
- flock/frontend/src/components/graph/MessageNode.test.tsx +3 -1
- flock/frontend/src/components/graph/MessageNode.tsx +16 -3
- flock/frontend/src/components/layout/DashboardLayout.tsx +12 -9
- flock/frontend/src/components/modules/HistoricalArtifactsModule.tsx +4 -14
- flock/frontend/src/components/modules/ModuleRegistry.ts +5 -3
- flock/frontend/src/hooks/useModules.ts +12 -4
- flock/frontend/src/hooks/usePersistence.ts +5 -3
- flock/frontend/src/services/api.ts +3 -19
- flock/frontend/src/services/graphService.test.ts +330 -0
- flock/frontend/src/services/graphService.ts +75 -0
- flock/frontend/src/services/websocket.ts +104 -268
- flock/frontend/src/store/filterStore.test.ts +89 -1
- flock/frontend/src/store/filterStore.ts +38 -16
- flock/frontend/src/store/graphStore.test.ts +538 -173
- flock/frontend/src/store/graphStore.ts +374 -465
- flock/frontend/src/store/moduleStore.ts +51 -33
- flock/frontend/src/store/uiStore.ts +23 -11
- flock/frontend/src/types/graph.ts +77 -44
- flock/frontend/src/utils/mockData.ts +16 -3
- flock/frontend/vite.config.ts +2 -2
- flock/orchestrator.py +27 -7
- flock/patches/__init__.py +5 -0
- flock/patches/dspy_streaming_patch.py +82 -0
- flock/service.py +2 -2
- flock/store.py +169 -4
- flock/themes/darkmatrix.toml +2 -2
- flock/themes/deep.toml +2 -2
- flock/themes/neopolitan.toml +4 -4
- {flock_core-0.5.0b71.dist-info → flock_core-0.5.1.dist-info}/METADATA +20 -13
- {flock_core-0.5.0b71.dist-info → flock_core-0.5.1.dist-info}/RECORD +59 -53
- flock/frontend/src/__tests__/e2e/critical-scenarios.test.tsx +0 -586
- flock/frontend/src/__tests__/integration/filtering-e2e.test.tsx +0 -391
- flock/frontend/src/__tests__/integration/graph-rendering.test.tsx +0 -640
- flock/frontend/src/services/websocket.test.ts +0 -595
- flock/frontend/src/utils/transforms.test.ts +0 -860
- flock/frontend/src/utils/transforms.ts +0 -323
- {flock_core-0.5.0b71.dist-info → flock_core-0.5.1.dist-info}/WHEEL +0 -0
- {flock_core-0.5.0b71.dist-info → flock_core-0.5.1.dist-info}/entry_points.txt +0 -0
- {flock_core-0.5.0b71.dist-info → flock_core-0.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { useGraphStore } from '../../store/graphStore';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
3
2
|
|
|
4
3
|
interface RunStatusTabProps {
|
|
5
4
|
nodeId: string;
|
|
@@ -20,48 +19,57 @@ interface RunStatusEntry {
|
|
|
20
19
|
errorMessage?: string;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
const RunStatusTab: React.FC<RunStatusTabProps> = ({ nodeId, nodeType }) => {
|
|
24
|
-
const
|
|
22
|
+
const RunStatusTab: React.FC<RunStatusTabProps> = ({ nodeId, nodeType: _nodeType }) => {
|
|
23
|
+
const [runHistory, setRunHistory] = useState<RunStatusEntry[]>([]);
|
|
24
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
25
|
+
const [error, setError] = useState<string | null>(null);
|
|
25
26
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
// Phase 4.1 Feature Gap Fix: Fetch agent run history from backend API
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const fetchRunHistory = async () => {
|
|
30
|
+
if (_nodeType !== 'agent') {
|
|
31
|
+
setRunHistory([]);
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (run.agent_name === nodeId) {
|
|
37
|
-
const startTime = run.started_at ? new Date(run.started_at).getTime() : Date.now();
|
|
38
|
-
const endTime = run.completed_at ? new Date(run.completed_at).getTime() : Date.now();
|
|
36
|
+
setIsLoading(true);
|
|
37
|
+
setError(null);
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
} else if (run.status === 'error') {
|
|
45
|
-
status = 'error';
|
|
46
|
-
} else {
|
|
47
|
-
status = 'idle';
|
|
39
|
+
try {
|
|
40
|
+
const response = await fetch(`/api/agents/${nodeId}/runs`);
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
throw new Error(`Failed to fetch run history: ${response.statusText}`);
|
|
48
43
|
}
|
|
49
44
|
|
|
50
|
-
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
|
|
47
|
+
// Convert API response to RunStatusEntry format
|
|
48
|
+
const history = data.runs.map((run: any) => ({
|
|
51
49
|
runId: run.run_id,
|
|
52
|
-
startTime,
|
|
53
|
-
endTime,
|
|
54
|
-
duration: run.duration_ms
|
|
55
|
-
status,
|
|
56
|
-
metrics:
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
startTime: new Date(run.start_time).getTime(),
|
|
51
|
+
endTime: new Date(run.end_time).getTime(),
|
|
52
|
+
duration: run.duration_ms,
|
|
53
|
+
status: run.status === 'completed' ? 'idle' : run.status === 'active' ? 'processing' : 'error',
|
|
54
|
+
metrics: {
|
|
55
|
+
tokensUsed: run.metrics?.tokens_used,
|
|
56
|
+
costUsd: run.metrics?.cost_usd,
|
|
57
|
+
artifactsProduced: run.metrics?.artifacts_produced,
|
|
58
|
+
},
|
|
59
|
+
errorMessage: run.error_message,
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
setRunHistory(history);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error('Failed to fetch run history:', err);
|
|
65
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
66
|
+
} finally {
|
|
67
|
+
setIsLoading(false);
|
|
59
68
|
}
|
|
60
|
-
}
|
|
69
|
+
};
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}, [nodeId, nodeType, runs]);
|
|
71
|
+
fetchRunHistory();
|
|
72
|
+
}, [nodeId, _nodeType]);
|
|
65
73
|
|
|
66
74
|
const formatTimestamp = (timestamp: number) => {
|
|
67
75
|
return new Date(timestamp).toLocaleString();
|
|
@@ -113,7 +121,33 @@ const RunStatusTab: React.FC<RunStatusTabProps> = ({ nodeId, nodeType }) => {
|
|
|
113
121
|
color: 'var(--color-text-primary)',
|
|
114
122
|
}}
|
|
115
123
|
>
|
|
116
|
-
{
|
|
124
|
+
{isLoading ? (
|
|
125
|
+
<div
|
|
126
|
+
data-testid="loading-runs"
|
|
127
|
+
style={{
|
|
128
|
+
padding: 'var(--space-layout-md)',
|
|
129
|
+
color: 'var(--color-text-muted)',
|
|
130
|
+
fontSize: 'var(--font-size-body-sm)',
|
|
131
|
+
fontFamily: 'var(--font-family-sans)',
|
|
132
|
+
textAlign: 'center',
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
Loading run history...
|
|
136
|
+
</div>
|
|
137
|
+
) : error ? (
|
|
138
|
+
<div
|
|
139
|
+
data-testid="error-runs"
|
|
140
|
+
style={{
|
|
141
|
+
padding: 'var(--space-layout-md)',
|
|
142
|
+
color: 'var(--color-error-light)',
|
|
143
|
+
fontSize: 'var(--font-size-body-sm)',
|
|
144
|
+
fontFamily: 'var(--font-family-sans)',
|
|
145
|
+
textAlign: 'center',
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
Error: {error}
|
|
149
|
+
</div>
|
|
150
|
+
) : runHistory.length === 0 ? (
|
|
117
151
|
<div
|
|
118
152
|
data-testid="empty-runs"
|
|
119
153
|
style={{
|
|
@@ -124,7 +158,14 @@ const RunStatusTab: React.FC<RunStatusTabProps> = ({ nodeId, nodeType }) => {
|
|
|
124
158
|
textAlign: 'center',
|
|
125
159
|
}}
|
|
126
160
|
>
|
|
127
|
-
|
|
161
|
+
<div style={{ marginBottom: 'var(--space-component-sm)' }}>
|
|
162
|
+
🚧 Run tracking coming soon!
|
|
163
|
+
</div>
|
|
164
|
+
<div style={{ fontSize: 'var(--font-size-caption)', color: 'var(--color-text-tertiary)' }}>
|
|
165
|
+
This feature will track individual agent executions with timing and metrics.
|
|
166
|
+
<br />
|
|
167
|
+
For now, check the Message History tab to see consumed and published messages.
|
|
168
|
+
</div>
|
|
128
169
|
</div>
|
|
129
170
|
) : (
|
|
130
171
|
<table
|
|
@@ -2,9 +2,11 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
3
|
import { ReactFlowProvider } from '@xyflow/react';
|
|
4
4
|
import AgentNode from './AgentNode';
|
|
5
|
-
import { AgentNodeData } from '../../types/graph';
|
|
6
5
|
import { NodeProps } from '@xyflow/react';
|
|
7
6
|
|
|
7
|
+
// UI Optimization Migration (Phase 4.1 - Spec 002): AgentNodeData removed, use Record<string, any>
|
|
8
|
+
type AgentNodeData = Record<string, any>;
|
|
9
|
+
|
|
8
10
|
describe('AgentNode', () => {
|
|
9
11
|
const createNodeProps = (data: AgentNodeData, selected = false): NodeProps =>
|
|
10
12
|
({
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { memo, useState, useEffect, useRef } from 'react';
|
|
2
2
|
import { NodeProps, Handle, Position } from '@xyflow/react';
|
|
3
|
-
import { AgentNodeData } from '../../types/graph';
|
|
4
3
|
import { useUIStore } from '../../store/uiStore';
|
|
5
4
|
import { useSettingsStore } from '../../store/settingsStore';
|
|
6
5
|
|
|
6
|
+
// UI Optimization Migration (Phase 4.1 - Spec 002): Backend GraphNode.data is Record<string, any>
|
|
7
|
+
// Agent-specific properties populated by backend snapshot
|
|
7
8
|
const AgentNode = memo(({ data, selected }: NodeProps) => {
|
|
8
|
-
const nodeData = data as
|
|
9
|
+
const nodeData = data as Record<string, any>;
|
|
9
10
|
const name = nodeData.name;
|
|
10
11
|
const status = nodeData.status;
|
|
11
12
|
const sentCount = nodeData.sentCount;
|
|
@@ -19,14 +20,14 @@ const AgentNode = memo(({ data, selected }: NodeProps) => {
|
|
|
19
20
|
// Merge known types with actual counts - show all types even with 0 count
|
|
20
21
|
// Start with actual counts, then add known types that haven't happened yet
|
|
21
22
|
const displayReceivedByType: Record<string, number> = { ...receivedByType };
|
|
22
|
-
subscriptions.forEach(type => {
|
|
23
|
+
subscriptions.forEach((type: string) => {
|
|
23
24
|
if (!(type in displayReceivedByType)) {
|
|
24
25
|
displayReceivedByType[type] = 0;
|
|
25
26
|
}
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
const displaySentByType: Record<string, number> = { ...sentByType };
|
|
29
|
-
outputTypes.forEach(type => {
|
|
30
|
+
outputTypes.forEach((type: string) => {
|
|
30
31
|
if (!(type in displaySentByType)) {
|
|
31
32
|
displaySentByType[type] = 0;
|
|
32
33
|
}
|
|
@@ -43,10 +44,11 @@ const AgentNode = memo(({ data, selected }: NodeProps) => {
|
|
|
43
44
|
const changedKeys = new Set<string>();
|
|
44
45
|
|
|
45
46
|
Object.entries(allCounts).forEach(([key, count]) => {
|
|
46
|
-
|
|
47
|
+
const numCount = count as number;
|
|
48
|
+
if (prevCounts.current[key] !== undefined && prevCounts.current[key] !== numCount) {
|
|
47
49
|
changedKeys.add(key);
|
|
48
50
|
}
|
|
49
|
-
prevCounts.current[key] =
|
|
51
|
+
prevCounts.current[key] = numCount;
|
|
50
52
|
});
|
|
51
53
|
|
|
52
54
|
if (changedKeys.size > 0) {
|
|
@@ -38,14 +38,12 @@ const GraphCanvas: React.FC = () => {
|
|
|
38
38
|
const openDetailWindow = useUIStore((state) => state.openDetailWindow);
|
|
39
39
|
const nodes = useGraphStore((state) => state.nodes);
|
|
40
40
|
const edges = useGraphStore((state) => state.edges);
|
|
41
|
-
const agents = useGraphStore((state) => state.agents);
|
|
42
|
-
const messages = useGraphStore((state) => state.messages);
|
|
43
|
-
const runs = useGraphStore((state) => state.runs);
|
|
44
41
|
const generateAgentViewGraph = useGraphStore((state) => state.generateAgentViewGraph);
|
|
45
42
|
const generateBlackboardViewGraph = useGraphStore((state) => state.generateBlackboardViewGraph);
|
|
46
|
-
const updateNodePosition = useGraphStore((state) => state.updateNodePosition);
|
|
43
|
+
const updateNodePosition = useGraphStore ((state) => state.updateNodePosition);
|
|
47
44
|
const addModule = useModuleStore((state) => state.addModule);
|
|
48
|
-
|
|
45
|
+
// UI Optimization Migration (Phase 4 - Spec 002): Use filterStore.applyFilters (backend-driven)
|
|
46
|
+
const applyFilters = useFilterStore((state) => state.applyFilters);
|
|
49
47
|
|
|
50
48
|
const correlationId = useFilterStore((state) => state.correlationId);
|
|
51
49
|
const timeRange = useFilterStore((state) => state.timeRange);
|
|
@@ -84,23 +82,30 @@ const GraphCanvas: React.FC = () => {
|
|
|
84
82
|
[]
|
|
85
83
|
);
|
|
86
84
|
|
|
87
|
-
// Generate graph when mode changes
|
|
85
|
+
// UI Optimization Migration (Phase 4.1 - Spec 002): Generate graph when mode changes
|
|
86
|
+
// Backend snapshot includes ALL latest data, no need to watch OLD agents/messages/runs Maps
|
|
87
|
+
// Note: generateAgentViewGraph and generateBlackboardViewGraph are stable zustand functions
|
|
88
|
+
// DO NOT add them to dependencies or it will cause infinite loop when nodes update
|
|
88
89
|
useEffect(() => {
|
|
89
90
|
if (mode === 'agent') {
|
|
90
91
|
generateAgentViewGraph();
|
|
91
92
|
} else {
|
|
92
93
|
generateBlackboardViewGraph();
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
96
|
+
}, [mode]);
|
|
95
97
|
|
|
96
98
|
// Regenerate graph when edge settings change to apply new edge styles
|
|
99
|
+
// Note: generateAgentViewGraph and generateBlackboardViewGraph are stable zustand functions
|
|
100
|
+
// DO NOT add them to dependencies or it will cause infinite loop when nodes update
|
|
97
101
|
useEffect(() => {
|
|
98
102
|
if (mode === 'agent') {
|
|
99
103
|
generateAgentViewGraph();
|
|
100
104
|
} else {
|
|
101
105
|
generateBlackboardViewGraph();
|
|
102
106
|
}
|
|
103
|
-
|
|
107
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
108
|
+
}, [edgeType, edgeStrokeWidth, edgeAnimation, mode]);
|
|
104
109
|
|
|
105
110
|
// Apply filters whenever filter store state changes
|
|
106
111
|
useEffect(() => {
|
|
@@ -2,9 +2,11 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
3
|
import { ReactFlowProvider } from '@xyflow/react';
|
|
4
4
|
import MessageNode from './MessageNode';
|
|
5
|
-
import { MessageNodeData } from '../../types/graph';
|
|
6
5
|
import { NodeProps } from '@xyflow/react';
|
|
7
6
|
|
|
7
|
+
// UI Optimization Migration (Phase 4.1 - Spec 002): MessageNodeData removed, use Record<string, any>
|
|
8
|
+
type MessageNodeData = Record<string, any>;
|
|
9
|
+
|
|
8
10
|
describe('MessageNode', () => {
|
|
9
11
|
const createNodeProps = (data: MessageNodeData, selected = false): NodeProps =>
|
|
10
12
|
({
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { memo } from 'react';
|
|
2
2
|
import { NodeProps, Handle, Position } from '@xyflow/react';
|
|
3
3
|
import JsonView from '@uiw/react-json-view';
|
|
4
|
-
import { MessageNodeData } from '../../types/graph';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// UI Optimization Migration (Phase 4.1 - Spec 002): Backend GraphNode.data is Record<string, any>
|
|
6
|
+
// Message/artifact-specific properties populated by backend snapshot
|
|
7
|
+
const MessageNode = memo(({ id, data, selected }: NodeProps) => {
|
|
8
|
+
const nodeData = data as Record<string, any>;
|
|
8
9
|
const artifactType = nodeData.artifactType;
|
|
9
10
|
const payload = nodeData.payload;
|
|
10
11
|
const producedBy = nodeData.producedBy;
|
|
@@ -12,6 +13,9 @@ const MessageNode = memo(({ data, selected }: NodeProps) => {
|
|
|
12
13
|
const isStreaming = nodeData.isStreaming || false;
|
|
13
14
|
const streamingText = nodeData.streamingText || '';
|
|
14
15
|
|
|
16
|
+
// Phase 6: Show artifact ID for debugging/verification
|
|
17
|
+
const artifactId = id; // Node ID is the artifact ID
|
|
18
|
+
|
|
15
19
|
return (
|
|
16
20
|
<div
|
|
17
21
|
className={`message-node ${selected ? 'selected' : ''}`}
|
|
@@ -45,6 +49,15 @@ const MessageNode = memo(({ data, selected }: NodeProps) => {
|
|
|
45
49
|
}}>
|
|
46
50
|
{artifactType}
|
|
47
51
|
</div>
|
|
52
|
+
<div style={{
|
|
53
|
+
fontSize: '10px',
|
|
54
|
+
color: '#a8a29e',
|
|
55
|
+
marginBottom: '4px',
|
|
56
|
+
fontFamily: 'monospace',
|
|
57
|
+
wordBreak: 'break-all'
|
|
58
|
+
}}>
|
|
59
|
+
id: {artifactId}
|
|
60
|
+
</div>
|
|
48
61
|
<div style={{
|
|
49
62
|
fontSize: '11px',
|
|
50
63
|
color: '#a8a29e',
|
|
@@ -40,7 +40,6 @@ const DashboardLayout: React.FC = () => {
|
|
|
40
40
|
|
|
41
41
|
const handleToggleAgentDetails = () => {
|
|
42
42
|
const detailWindows = useUIStore.getState().detailWindows;
|
|
43
|
-
const agents = useGraphStore.getState().agents;
|
|
44
43
|
|
|
45
44
|
// Check if any detail windows are open
|
|
46
45
|
if (detailWindows.size > 0) {
|
|
@@ -49,9 +48,13 @@ const DashboardLayout: React.FC = () => {
|
|
|
49
48
|
useUIStore.getState().closeDetailWindow(nodeId);
|
|
50
49
|
});
|
|
51
50
|
} else {
|
|
51
|
+
// UI Optimization Migration (Phase 4.1): Read agent nodes from state.nodes
|
|
52
|
+
const nodes = useGraphStore.getState().nodes;
|
|
53
|
+
const agentNodes = nodes.filter((node) => node.type === 'agent');
|
|
54
|
+
|
|
52
55
|
// Open detail windows for all agents
|
|
53
|
-
|
|
54
|
-
useUIStore.getState().openDetailWindow(
|
|
56
|
+
agentNodes.forEach((node) => {
|
|
57
|
+
useUIStore.getState().openDetailWindow(node.id);
|
|
55
58
|
});
|
|
56
59
|
}
|
|
57
60
|
};
|
|
@@ -63,15 +66,15 @@ const DashboardLayout: React.FC = () => {
|
|
|
63
66
|
});
|
|
64
67
|
|
|
65
68
|
const handleClearStore = () => {
|
|
66
|
-
if (confirm('Clear all dashboard data? This will remove all
|
|
67
|
-
// Clear
|
|
69
|
+
if (confirm('Clear all dashboard data? This will remove all graph data and session data.')) {
|
|
70
|
+
// UI Optimization Migration (Phase 4.1): Clear NEW Phase 2 state only
|
|
68
71
|
useGraphStore.setState({
|
|
69
|
-
agents: new Map(),
|
|
70
|
-
messages: new Map(),
|
|
71
72
|
events: [],
|
|
72
|
-
runs: new Map(),
|
|
73
73
|
nodes: [],
|
|
74
|
-
edges: []
|
|
74
|
+
edges: [],
|
|
75
|
+
statistics: null,
|
|
76
|
+
agentStatus: new Map(),
|
|
77
|
+
streamingTokens: new Map(),
|
|
75
78
|
});
|
|
76
79
|
|
|
77
80
|
// Clear UI store
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { fetchArtifactSummary, fetchArtifacts, type ArtifactListItem, type ArtifactQueryOptions } from '../../services/api';
|
|
3
|
-
import { mapArtifactToMessage } from '../../utils/artifacts';
|
|
4
3
|
import { useFilterStore } from '../../store/filterStore';
|
|
5
|
-
import { useGraphStore } from '../../store/graphStore';
|
|
6
|
-
import { useUIStore } from '../../store/uiStore';
|
|
7
4
|
import type { ModuleContext } from './ModuleRegistry';
|
|
8
5
|
import JsonAttributeRenderer from './JsonAttributeRenderer';
|
|
9
6
|
import styles from './HistoricalArtifactsModule.module.css';
|
|
@@ -147,17 +144,10 @@ const HistoricalArtifactsModule: React.FC<HistoricalArtifactsModuleProps> = ({ c
|
|
|
147
144
|
|
|
148
145
|
mergeCorrelationMetadata(response.items);
|
|
149
146
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (uiState.mode === 'agent') {
|
|
155
|
-
graphStore.generateAgentViewGraph();
|
|
156
|
-
} else {
|
|
157
|
-
graphStore.generateBlackboardViewGraph();
|
|
158
|
-
}
|
|
159
|
-
graphStore.applyFilters();
|
|
160
|
-
}
|
|
147
|
+
// UI Optimization Migration (Phase 2/4 - Spec 002): Backend-driven architecture
|
|
148
|
+
// Graph updates happen automatically via GraphCanvas useEffect when filters change
|
|
149
|
+
// This module only manages the artifact table view, not the graph
|
|
150
|
+
// No need to manually trigger graph updates here
|
|
161
151
|
|
|
162
152
|
const summaryResponse = await fetchArtifactSummary({
|
|
163
153
|
...queryOptions,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Message } from '../../types/graph';
|
|
3
3
|
import type { TimeRange, ArtifactSummary } from '../../types/filters';
|
|
4
4
|
|
|
5
|
+
// UI Optimization Migration (Phase 4.1 - Spec 002): ModuleContext uses OLD Phase 1 architecture
|
|
6
|
+
// TODO: Update module system to use GraphNode[] instead of Maps
|
|
5
7
|
export interface ModuleContext {
|
|
6
|
-
// Data access
|
|
7
|
-
agents: Map<string, Agent
|
|
8
|
+
// Data access (DEPRECATED - Phase 1 architecture, use events array instead)
|
|
9
|
+
agents: Map<string, any>; // OLD: was Map<string, Agent>
|
|
8
10
|
messages: Map<string, Message>;
|
|
9
11
|
events: Message[];
|
|
10
12
|
|
|
@@ -6,10 +6,15 @@
|
|
|
6
6
|
* Calls module onMount/onUnmount lifecycle hooks when instances change.
|
|
7
7
|
*
|
|
8
8
|
* SPECIFICATION: docs/specs/003-real-time-dashboard/FRONTEND_ARCHITECTURE.md Section 7.4
|
|
9
|
-
* - Build ModuleContext from store data (
|
|
9
|
+
* - Build ModuleContext from store data (events, filters)
|
|
10
10
|
* - Call module onMount lifecycle hooks when instances added
|
|
11
11
|
* - Call module onUnmount lifecycle hooks when instances removed
|
|
12
12
|
* - Provide publish and invoke actions in context
|
|
13
|
+
*
|
|
14
|
+
* UI Optimization Migration (Phase 4.1 - Spec 002):
|
|
15
|
+
* - agents/messages Maps are DEPRECATED (Phase 1 architecture)
|
|
16
|
+
* - Modules should use events array instead
|
|
17
|
+
* - Empty Maps provided for backward compatibility
|
|
13
18
|
*/
|
|
14
19
|
|
|
15
20
|
import { useEffect, useMemo, useRef } from 'react';
|
|
@@ -34,9 +39,11 @@ import { moduleRegistry, type ModuleContext } from '../components/modules/Module
|
|
|
34
39
|
export function useModules() {
|
|
35
40
|
// Subscribe to store state
|
|
36
41
|
const instances = useModuleStore((state) => state.instances);
|
|
37
|
-
const agents = useGraphStore((state) => state.agents);
|
|
38
|
-
const messages = useGraphStore((state) => state.messages);
|
|
39
42
|
const events = useGraphStore((state) => state.events);
|
|
43
|
+
|
|
44
|
+
// UI Optimization Migration (Phase 4.1): Provide empty Maps for deprecated fields
|
|
45
|
+
const agents = useMemo(() => new Map(), []);
|
|
46
|
+
const messages = useMemo(() => new Map(), []);
|
|
40
47
|
const correlationId = useFilterStore((state) => state.correlationId);
|
|
41
48
|
const timeRange = useFilterStore((state) => state.timeRange);
|
|
42
49
|
const artifactTypes = useFilterStore((state) => state.selectedArtifactTypes);
|
|
@@ -72,7 +79,8 @@ export function useModules() {
|
|
|
72
79
|
console.log('[Module Context] Invoke agent:', agentName, 'with inputs:', inputs);
|
|
73
80
|
},
|
|
74
81
|
}),
|
|
75
|
-
|
|
82
|
+
// Note: agents and messages are stable empty Maps, so excluded from deps
|
|
83
|
+
[events, correlationId, timeRange, artifactTypes, producers, tags, visibility, summary]
|
|
76
84
|
);
|
|
77
85
|
|
|
78
86
|
/**
|
|
@@ -57,7 +57,6 @@ function debounce<T extends (...args: any[]) => void>(
|
|
|
57
57
|
*/
|
|
58
58
|
export function usePersistence() {
|
|
59
59
|
const mode = useUIStore((state) => state.mode);
|
|
60
|
-
const updateNodePosition = useGraphStore((state) => state.updateNodePosition);
|
|
61
60
|
|
|
62
61
|
// Use ref to maintain debounced function identity across renders
|
|
63
62
|
const debouncedSaveRef = useRef<((nodeId: string, mode: VisualizationMode, position: Position) => void) | null>(null);
|
|
@@ -92,6 +91,8 @@ export function usePersistence() {
|
|
|
92
91
|
|
|
93
92
|
/**
|
|
94
93
|
* Load node positions from IndexedDB for current mode
|
|
94
|
+
* Note: Deliberately excludes updateNodePosition from dependencies to prevent infinite loops
|
|
95
|
+
* The function is stable from zustand, so we can safely use it without re-creating the callback
|
|
95
96
|
*/
|
|
96
97
|
const loadNodePositions = useCallback(async (currentMode: VisualizationMode) => {
|
|
97
98
|
try {
|
|
@@ -104,15 +105,16 @@ export function usePersistence() {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
// Apply loaded positions to graph store
|
|
108
|
+
// Use graphStore directly to avoid dependency on updateNodePosition selector
|
|
107
109
|
layouts.forEach((layout) => {
|
|
108
|
-
updateNodePosition(layout.node_id, { x: layout.x, y: layout.y });
|
|
110
|
+
useGraphStore.getState().updateNodePosition(layout.node_id, { x: layout.x, y: layout.y });
|
|
109
111
|
});
|
|
110
112
|
|
|
111
113
|
console.log(`[usePersistence] Loaded ${layouts.length} node positions for ${currentMode} view`);
|
|
112
114
|
} catch (error) {
|
|
113
115
|
console.error(`[usePersistence] Failed to load node positions for ${currentMode} view:`, error);
|
|
114
116
|
}
|
|
115
|
-
}, [
|
|
117
|
+
}, []); // Empty deps - function is now stable!
|
|
116
118
|
|
|
117
119
|
/**
|
|
118
120
|
* Load positions on mount and when mode changes
|
|
@@ -171,25 +171,9 @@ export async function fetchAgents(): Promise<Agent[]> {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
*/
|
|
178
|
-
export async function fetchRegisteredAgents(): Promise<import('../types/graph').Agent[]> {
|
|
179
|
-
const agents = await fetchAgents();
|
|
180
|
-
return agents.map(agent => ({
|
|
181
|
-
id: agent.name,
|
|
182
|
-
name: agent.name,
|
|
183
|
-
status: 'idle' as const,
|
|
184
|
-
subscriptions: agent.subscriptions || [],
|
|
185
|
-
outputTypes: agent.output_types || [],
|
|
186
|
-
lastActive: Date.now(),
|
|
187
|
-
sentCount: 0,
|
|
188
|
-
recvCount: 0,
|
|
189
|
-
receivedByType: {},
|
|
190
|
-
sentByType: {},
|
|
191
|
-
}));
|
|
192
|
-
}
|
|
174
|
+
// UI Optimization Migration (Phase 4.1 - Spec 002): Removed fetchRegisteredAgents()
|
|
175
|
+
// OLD Phase 1 function that transformed API agents to graph store format
|
|
176
|
+
// Backend now provides agent data directly in GraphSnapshot
|
|
193
177
|
|
|
194
178
|
/**
|
|
195
179
|
* Publish an artifact to the orchestrator
|