flock-core 0.5.3__py3-none-any.whl → 0.5.5__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 +20 -1
- flock/artifact_collector.py +2 -3
- flock/batch_accumulator.py +4 -4
- flock/components.py +32 -0
- flock/correlation_engine.py +9 -4
- flock/dashboard/collector.py +4 -0
- flock/dashboard/events.py +74 -0
- flock/dashboard/graph_builder.py +272 -0
- flock/dashboard/models/graph.py +3 -1
- flock/dashboard/service.py +363 -14
- flock/dashboard/static_v2/assets/index-DFRnI_mt.js +1 -1
- flock/dashboard/static_v2/index.html +3 -3
- flock/engines/dspy_engine.py +41 -3
- flock/engines/examples/__init__.py +6 -0
- flock/engines/examples/simple_batch_engine.py +61 -0
- flock/frontend/README.md +4 -4
- flock/frontend/docs/DESIGN_SYSTEM.md +1 -1
- flock/frontend/package-lock.json +2 -2
- flock/frontend/package.json +2 -2
- flock/frontend/src/components/controls/PublishControl.test.tsx +11 -11
- flock/frontend/src/components/controls/PublishControl.tsx +1 -1
- flock/frontend/src/components/graph/AgentNode.tsx +4 -0
- flock/frontend/src/components/graph/GraphCanvas.tsx +4 -0
- flock/frontend/src/components/graph/LogicOperationsDisplay.tsx +463 -0
- flock/frontend/src/components/graph/PendingBatchEdge.tsx +141 -0
- flock/frontend/src/components/graph/PendingJoinEdge.tsx +144 -0
- flock/frontend/src/components/settings/SettingsPanel.css +1 -1
- flock/frontend/src/components/settings/ThemeSelector.tsx +2 -2
- flock/frontend/src/services/graphService.ts +3 -1
- flock/frontend/src/services/indexeddb.ts +1 -1
- flock/frontend/src/services/websocket.ts +99 -1
- flock/frontend/src/store/graphStore.test.ts +2 -1
- flock/frontend/src/store/graphStore.ts +36 -5
- flock/frontend/src/styles/variables.css +1 -1
- flock/frontend/src/types/graph.ts +86 -0
- flock/orchestrator.py +268 -13
- flock/patches/__init__.py +1 -0
- flock/patches/dspy_streaming_patch.py +1 -0
- flock/runtime.py +3 -0
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/METADATA +11 -1
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/RECORD +44 -39
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/WHEEL +0 -0
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/entry_points.txt +0 -0
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>🦆🐓 Flock 🐤🐧</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-DFRnI_mt.js"></script>
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-fPLNdmp1.css">
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DFRnI_mt.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-fPLNdmp1.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
|
-
<div id="root"></div>
|
|
11
|
+
<div id="root"></div>
|
|
12
12
|
</body>
|
|
13
13
|
</html>
|
flock/engines/dspy_engine.py
CHANGED
|
@@ -153,6 +153,19 @@ class DSPyEngine(EngineComponent):
|
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
async def evaluate(self, agent, ctx, inputs: EvalInputs) -> EvalResult: # type: ignore[override]
|
|
156
|
+
return await self._evaluate_internal(agent, ctx, inputs, batched=False)
|
|
157
|
+
|
|
158
|
+
async def evaluate_batch(self, agent, ctx, inputs: EvalInputs) -> EvalResult: # type: ignore[override]
|
|
159
|
+
return await self._evaluate_internal(agent, ctx, inputs, batched=True)
|
|
160
|
+
|
|
161
|
+
async def _evaluate_internal(
|
|
162
|
+
self,
|
|
163
|
+
agent,
|
|
164
|
+
ctx,
|
|
165
|
+
inputs: EvalInputs,
|
|
166
|
+
*,
|
|
167
|
+
batched: bool,
|
|
168
|
+
) -> EvalResult:
|
|
156
169
|
if not inputs.artifacts:
|
|
157
170
|
return EvalResult(artifacts=[], state=dict(inputs.state))
|
|
158
171
|
|
|
@@ -169,7 +182,13 @@ class DSPyEngine(EngineComponent):
|
|
|
169
182
|
|
|
170
183
|
primary_artifact = self._select_primary_artifact(inputs.artifacts)
|
|
171
184
|
input_model = self._resolve_input_model(primary_artifact)
|
|
172
|
-
|
|
185
|
+
if batched:
|
|
186
|
+
validated_input = [
|
|
187
|
+
self._validate_input_payload(input_model, artifact.payload)
|
|
188
|
+
for artifact in inputs.artifacts
|
|
189
|
+
]
|
|
190
|
+
else:
|
|
191
|
+
validated_input = self._validate_input_payload(input_model, primary_artifact.payload)
|
|
173
192
|
output_model = self._resolve_output_model(agent)
|
|
174
193
|
|
|
175
194
|
# Fetch conversation context from blackboard
|
|
@@ -183,6 +202,7 @@ class DSPyEngine(EngineComponent):
|
|
|
183
202
|
input_schema=input_model,
|
|
184
203
|
output_schema=output_model,
|
|
185
204
|
has_context=has_context,
|
|
205
|
+
batched=batched,
|
|
186
206
|
)
|
|
187
207
|
|
|
188
208
|
sys_desc = self._system_description(self.instructions or agent.description)
|
|
@@ -193,7 +213,11 @@ class DSPyEngine(EngineComponent):
|
|
|
193
213
|
pre_generated_artifact_id = uuid4()
|
|
194
214
|
|
|
195
215
|
# Build execution payload with context
|
|
196
|
-
if
|
|
216
|
+
if batched:
|
|
217
|
+
execution_payload = {"input": validated_input}
|
|
218
|
+
if has_context:
|
|
219
|
+
execution_payload["context"] = context_history
|
|
220
|
+
elif has_context:
|
|
197
221
|
execution_payload = {
|
|
198
222
|
"input": validated_input,
|
|
199
223
|
"context": context_history,
|
|
@@ -383,6 +407,7 @@ class DSPyEngine(EngineComponent):
|
|
|
383
407
|
input_schema: type[BaseModel] | None,
|
|
384
408
|
output_schema: type[BaseModel] | None,
|
|
385
409
|
has_context: bool = False,
|
|
410
|
+
batched: bool = False,
|
|
386
411
|
) -> Any:
|
|
387
412
|
"""Prepare DSPy signature, optionally including context field."""
|
|
388
413
|
fields = {
|
|
@@ -398,7 +423,15 @@ class DSPyEngine(EngineComponent):
|
|
|
398
423
|
),
|
|
399
424
|
)
|
|
400
425
|
|
|
401
|
-
|
|
426
|
+
if batched:
|
|
427
|
+
if input_schema is not None:
|
|
428
|
+
input_type = list[input_schema]
|
|
429
|
+
else:
|
|
430
|
+
input_type = list[dict[str, Any]]
|
|
431
|
+
else:
|
|
432
|
+
input_type = input_schema or dict
|
|
433
|
+
|
|
434
|
+
fields["input"] = (input_type, dspy_mod.InputField())
|
|
402
435
|
fields["output"] = (output_schema or dict, dspy_mod.OutputField())
|
|
403
436
|
|
|
404
437
|
signature = dspy_mod.Signature(fields)
|
|
@@ -406,6 +439,11 @@ class DSPyEngine(EngineComponent):
|
|
|
406
439
|
instruction = description or "Produce a valid output that matches the 'output' schema."
|
|
407
440
|
if has_context:
|
|
408
441
|
instruction += " Consider the conversation context provided to inform your response."
|
|
442
|
+
if batched:
|
|
443
|
+
instruction += (
|
|
444
|
+
" The 'input' field will contain a list of items representing the batch; "
|
|
445
|
+
"process the entire collection coherently."
|
|
446
|
+
)
|
|
409
447
|
instruction += " Return only JSON."
|
|
410
448
|
|
|
411
449
|
return signature.with_instructions(instruction)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Reference batch-aware engine used in tutorials and tests."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from flock.components import EngineComponent
|
|
8
|
+
from flock.registry import flock_type
|
|
9
|
+
from flock.runtime import EvalInputs, EvalResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@flock_type(name="BatchItem")
|
|
13
|
+
class BatchItem(BaseModel):
|
|
14
|
+
"""Input payload used by reference tests and tutorials."""
|
|
15
|
+
|
|
16
|
+
value: int = Field(description="Numeric value contributed by the artifact")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@flock_type(name="BatchSummary")
|
|
20
|
+
class BatchSummary(BaseModel):
|
|
21
|
+
"""Output payload describing the batch that was processed."""
|
|
22
|
+
|
|
23
|
+
batch_size: int = Field(description="Number of items included in this evaluation")
|
|
24
|
+
values: list[int] = Field(description="Original values processed", default_factory=list)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SimpleBatchEngine(EngineComponent):
|
|
28
|
+
"""Example engine that processes items individually or in batches.
|
|
29
|
+
|
|
30
|
+
- ``evaluate`` is used when the agent is invoked directly without BatchSpec.
|
|
31
|
+
- ``evaluate_batch`` is triggered when BatchSpec flushes accumulated artifacts.
|
|
32
|
+
|
|
33
|
+
The engine simply annotates each item with the current batch size so tests can
|
|
34
|
+
verify that all artifacts were processed together.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
async def evaluate(self, agent, ctx, inputs: EvalInputs) -> EvalResult:
|
|
38
|
+
item = inputs.first_as(BatchItem)
|
|
39
|
+
if item is None:
|
|
40
|
+
return EvalResult.empty()
|
|
41
|
+
|
|
42
|
+
annotated = BatchSummary(batch_size=1, values=[item.value])
|
|
43
|
+
state = dict(inputs.state)
|
|
44
|
+
state.setdefault("batch_size", annotated.batch_size)
|
|
45
|
+
state.setdefault("processed_values", list(annotated.values))
|
|
46
|
+
|
|
47
|
+
return EvalResult.from_object(annotated, agent=agent, state=state)
|
|
48
|
+
|
|
49
|
+
async def evaluate_batch(self, agent, ctx, inputs: EvalInputs) -> EvalResult:
|
|
50
|
+
items = inputs.all_as(BatchItem)
|
|
51
|
+
if not items:
|
|
52
|
+
return EvalResult.empty()
|
|
53
|
+
|
|
54
|
+
batch_size = len(items)
|
|
55
|
+
summary = BatchSummary(batch_size=batch_size, values=[item.value for item in items])
|
|
56
|
+
|
|
57
|
+
state = dict(inputs.state)
|
|
58
|
+
state["batch_size"] = summary.batch_size
|
|
59
|
+
state["processed_values"] = list(summary.values)
|
|
60
|
+
|
|
61
|
+
return EvalResult.from_object(summary, agent=agent, state=state)
|
flock/frontend/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Flock
|
|
1
|
+
# Flock Dashboard
|
|
2
2
|
|
|
3
|
-
A real-time visualization dashboard for monitoring and controlling Flock
|
|
3
|
+
A real-time visualization dashboard for monitoring and controlling Flock agent orchestration systems. Built with modern web technologies to provide an intuitive, high-performance interface for observing multi-agent workflows.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
The Flock
|
|
7
|
+
The Flock Dashboard provides real-time visibility into your agent orchestration system through an interactive graph-based interface. Watch agents activate, messages flow, and data transform in real-time as your multi-agent system operates.
|
|
8
8
|
|
|
9
9
|
The dashboard offers two complementary visualization modes:
|
|
10
10
|
- **Agent View**: Shows agents as nodes with message flows as edges - perfect for understanding agent communication patterns
|
|
@@ -149,7 +149,7 @@ Launch the module via the context menu (or `Add Module → Historical Blackboard
|
|
|
149
149
|
|
|
150
150
|
- **Node.js**: Version 18 or higher
|
|
151
151
|
- **Package Manager**: npm (included with Node.js) or yarn
|
|
152
|
-
- **Flock
|
|
152
|
+
- **Flock Backend**: Running orchestrator instance (typically on port 8344)
|
|
153
153
|
|
|
154
154
|
### Installation
|
|
155
155
|
|
flock/frontend/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flock-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "flock-ui",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.8",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@types/dagre": "^0.7.53",
|
flock/frontend/package.json
CHANGED
|
@@ -398,7 +398,7 @@ describe('PublishControl', () => {
|
|
|
398
398
|
});
|
|
399
399
|
|
|
400
400
|
// Auto-filter checkbox tests
|
|
401
|
-
it('should render auto-set filter checkbox
|
|
401
|
+
it('should render auto-set filter checkbox unchecked by default', async () => {
|
|
402
402
|
mockFetch.mockResolvedValueOnce({
|
|
403
403
|
ok: true,
|
|
404
404
|
json: async () => ({ artifact_types: mockArtifactTypes }),
|
|
@@ -412,7 +412,7 @@ describe('PublishControl', () => {
|
|
|
412
412
|
|
|
413
413
|
const checkbox = screen.getByLabelText(/set filter to correlation id/i) as HTMLInputElement;
|
|
414
414
|
expect(checkbox).toBeInTheDocument();
|
|
415
|
-
expect(checkbox.checked).toBe(
|
|
415
|
+
expect(checkbox.checked).toBe(false);
|
|
416
416
|
});
|
|
417
417
|
|
|
418
418
|
it('should set filter to correlation ID when checkbox is checked and publish succeeds', async () => {
|
|
@@ -439,7 +439,8 @@ describe('PublishControl', () => {
|
|
|
439
439
|
const artifactTypeSelect = screen.getByLabelText(/artifact type/i);
|
|
440
440
|
const checkbox = screen.getByLabelText(/set filter to correlation id/i) as HTMLInputElement;
|
|
441
441
|
|
|
442
|
-
//
|
|
442
|
+
// Check the checkbox to enable auto-filter
|
|
443
|
+
fireEvent.click(checkbox);
|
|
443
444
|
expect(checkbox.checked).toBe(true);
|
|
444
445
|
|
|
445
446
|
fireEvent.change(artifactTypeSelect, { target: { value: 'Idea' } });
|
|
@@ -487,8 +488,7 @@ describe('PublishControl', () => {
|
|
|
487
488
|
const artifactTypeSelect = screen.getByLabelText(/artifact type/i);
|
|
488
489
|
const checkbox = screen.getByLabelText(/set filter to correlation id/i) as HTMLInputElement;
|
|
489
490
|
|
|
490
|
-
//
|
|
491
|
-
fireEvent.click(checkbox);
|
|
491
|
+
// Checkbox should already be unchecked by default
|
|
492
492
|
expect(checkbox.checked).toBe(false);
|
|
493
493
|
|
|
494
494
|
// Clear any existing filter
|
|
@@ -529,15 +529,15 @@ describe('PublishControl', () => {
|
|
|
529
529
|
|
|
530
530
|
const checkbox = screen.getByLabelText(/set filter to correlation id/i) as HTMLInputElement;
|
|
531
531
|
|
|
532
|
-
// Initially
|
|
533
|
-
expect(checkbox.checked).toBe(true);
|
|
534
|
-
|
|
535
|
-
// Uncheck
|
|
536
|
-
fireEvent.click(checkbox);
|
|
532
|
+
// Initially unchecked
|
|
537
533
|
expect(checkbox.checked).toBe(false);
|
|
538
534
|
|
|
539
|
-
// Check
|
|
535
|
+
// Check
|
|
540
536
|
fireEvent.click(checkbox);
|
|
541
537
|
expect(checkbox.checked).toBe(true);
|
|
538
|
+
|
|
539
|
+
// Uncheck again
|
|
540
|
+
fireEvent.click(checkbox);
|
|
541
|
+
expect(checkbox.checked).toBe(false);
|
|
542
542
|
});
|
|
543
543
|
});
|
|
@@ -22,7 +22,7 @@ const PublishControl: React.FC = () => {
|
|
|
22
22
|
const [errors, setErrors] = useState<ValidationErrors>({});
|
|
23
23
|
const [successMessage, setSuccessMessage] = useState('');
|
|
24
24
|
const [errorMessage, setErrorMessage] = useState('');
|
|
25
|
-
const [autoSetFilter, setAutoSetFilter] = useState(
|
|
25
|
+
const [autoSetFilter, setAutoSetFilter] = useState(false); // Default: unchecked (user can opt-in to auto-filter)
|
|
26
26
|
|
|
27
27
|
const setShowControls = useSettingsStore((state) => state.setShowControls);
|
|
28
28
|
|
|
@@ -2,6 +2,7 @@ import { memo, useState, useEffect, useRef } from 'react';
|
|
|
2
2
|
import { NodeProps, Handle, Position } from '@xyflow/react';
|
|
3
3
|
import { useUIStore } from '../../store/uiStore';
|
|
4
4
|
import { useSettingsStore } from '../../store/settingsStore';
|
|
5
|
+
import LogicOperationsDisplay from './LogicOperationsDisplay';
|
|
5
6
|
|
|
6
7
|
// UI Optimization Migration (Phase 4.1 - Spec 002): Backend GraphNode.data is Record<string, any>
|
|
7
8
|
// Agent-specific properties populated by backend snapshot
|
|
@@ -16,6 +17,7 @@ const AgentNode = memo(({ data, selected }: NodeProps) => {
|
|
|
16
17
|
const receivedByType = nodeData.receivedByType || {};
|
|
17
18
|
const sentByType = nodeData.sentByType || {};
|
|
18
19
|
const streamingTokens = nodeData.streamingTokens || [];
|
|
20
|
+
const logicOperations = nodeData.logicOperations || []; // Phase 1.4: Logic operations state
|
|
19
21
|
|
|
20
22
|
// Merge known types with actual counts - show all types even with 0 count
|
|
21
23
|
// Start with actual counts, then add known types that haven't happened yet
|
|
@@ -307,6 +309,8 @@ const AgentNode = memo(({ data, selected }: NodeProps) => {
|
|
|
307
309
|
</div>
|
|
308
310
|
</div>
|
|
309
311
|
)}
|
|
312
|
+
{/* Phase 1.4: Logic Operations Display (JoinSpec/BatchSpec waiting states) */}
|
|
313
|
+
<LogicOperationsDisplay logicOperations={logicOperations} compactNodeView={compactNodeView} />
|
|
310
314
|
</div>
|
|
311
315
|
)}
|
|
312
316
|
{compactNodeView && (
|
|
@@ -15,6 +15,8 @@ import AgentNode from './AgentNode';
|
|
|
15
15
|
import MessageNode from './MessageNode';
|
|
16
16
|
import MessageFlowEdge from './MessageFlowEdge';
|
|
17
17
|
import TransformEdge from './TransformEdge';
|
|
18
|
+
import PendingJoinEdge from './PendingJoinEdge';
|
|
19
|
+
import PendingBatchEdge from './PendingBatchEdge';
|
|
18
20
|
import MiniMap from './MiniMap';
|
|
19
21
|
import { useGraphStore } from '../../store/graphStore';
|
|
20
22
|
import { useFilterStore } from '../../store/filterStore';
|
|
@@ -78,6 +80,8 @@ const GraphCanvas: React.FC = () => {
|
|
|
78
80
|
() => ({
|
|
79
81
|
message_flow: MessageFlowEdge,
|
|
80
82
|
transformation: TransformEdge,
|
|
83
|
+
pending_join: PendingJoinEdge, // Phase 1.5: Pending edges for JoinSpec correlation groups
|
|
84
|
+
pending_batch: PendingBatchEdge, // Phase 1.5: Pending edges for BatchSpec accumulation
|
|
81
85
|
}),
|
|
82
86
|
[]
|
|
83
87
|
);
|