flock-core 0.5.9__py3-none-any.whl → 0.5.11__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.

Files changed (54) hide show
  1. flock/agent.py +149 -62
  2. flock/api/themes.py +6 -2
  3. flock/api_models.py +285 -0
  4. flock/artifact_collector.py +6 -3
  5. flock/batch_accumulator.py +3 -1
  6. flock/cli.py +3 -1
  7. flock/components.py +45 -56
  8. flock/context_provider.py +531 -0
  9. flock/correlation_engine.py +8 -4
  10. flock/dashboard/collector.py +48 -29
  11. flock/dashboard/events.py +10 -4
  12. flock/dashboard/launcher.py +3 -1
  13. flock/dashboard/models/graph.py +9 -3
  14. flock/dashboard/service.py +187 -93
  15. flock/dashboard/websocket.py +17 -4
  16. flock/engines/dspy_engine.py +174 -98
  17. flock/engines/examples/simple_batch_engine.py +9 -3
  18. flock/examples.py +6 -2
  19. flock/frontend/src/services/indexeddb.test.ts +4 -4
  20. flock/frontend/src/services/indexeddb.ts +1 -1
  21. flock/helper/cli_helper.py +14 -1
  22. flock/logging/auto_trace.py +6 -1
  23. flock/logging/formatters/enum_builder.py +3 -1
  24. flock/logging/formatters/theme_builder.py +32 -17
  25. flock/logging/formatters/themed_formatter.py +38 -22
  26. flock/logging/logging.py +21 -7
  27. flock/logging/telemetry.py +9 -3
  28. flock/logging/telemetry_exporter/duckdb_exporter.py +27 -25
  29. flock/logging/trace_and_logged.py +14 -5
  30. flock/mcp/__init__.py +3 -6
  31. flock/mcp/client.py +49 -19
  32. flock/mcp/config.py +12 -6
  33. flock/mcp/manager.py +6 -2
  34. flock/mcp/servers/sse/flock_sse_server.py +9 -3
  35. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +6 -2
  36. flock/mcp/tool.py +18 -6
  37. flock/mcp/types/handlers.py +3 -1
  38. flock/mcp/types/types.py +9 -3
  39. flock/orchestrator.py +449 -58
  40. flock/orchestrator_component.py +15 -5
  41. flock/patches/dspy_streaming_patch.py +12 -4
  42. flock/registry.py +9 -3
  43. flock/runtime.py +69 -18
  44. flock/service.py +135 -64
  45. flock/store.py +29 -10
  46. flock/subscription.py +6 -4
  47. flock/system_artifacts.py +33 -0
  48. flock/utilities.py +41 -13
  49. flock/utility/output_utility_component.py +31 -11
  50. {flock_core-0.5.9.dist-info → flock_core-0.5.11.dist-info}/METADATA +150 -26
  51. {flock_core-0.5.9.dist-info → flock_core-0.5.11.dist-info}/RECORD +54 -51
  52. {flock_core-0.5.9.dist-info → flock_core-0.5.11.dist-info}/WHEEL +0 -0
  53. {flock_core-0.5.9.dist-info → flock_core-0.5.11.dist-info}/entry_points.txt +0 -0
  54. {flock_core-0.5.9.dist-info → flock_core-0.5.11.dist-info}/licenses/LICENSE +0 -0
@@ -54,7 +54,9 @@ class RunRecord:
54
54
  error_message: str | None = None
55
55
 
56
56
  def to_graph_run(self) -> GraphRun:
57
- status = self.status if self.status in {"active", "completed", "error"} else "active"
57
+ status = (
58
+ self.status if self.status in {"active", "completed", "error"} else "active"
59
+ )
58
60
  return GraphRun(
59
61
  run_id=self.run_id,
60
62
  agent_name=self.agent_name,
@@ -102,7 +104,10 @@ class DashboardEventCollector(AgentComponent):
102
104
 
103
105
  # Use PrivateAttr for non-Pydantic fields (AgentComponent extends BaseModel)
104
106
  _events: deque[
105
- AgentActivatedEvent | MessagePublishedEvent | AgentCompletedEvent | AgentErrorEvent
107
+ AgentActivatedEvent
108
+ | MessagePublishedEvent
109
+ | AgentCompletedEvent
110
+ | AgentErrorEvent
106
111
  ] = PrivateAttr(default=None)
107
112
 
108
113
  # Track run start times for duration calculation
@@ -114,7 +119,9 @@ class DashboardEventCollector(AgentComponent):
114
119
  # Graph assembly helpers
115
120
  _graph_lock: asyncio.Lock = PrivateAttr(default_factory=asyncio.Lock)
116
121
  _run_registry: dict[str, RunRecord] = PrivateAttr(default_factory=dict)
117
- _artifact_consumers: dict[str, set[str]] = PrivateAttr(default_factory=lambda: defaultdict(set))
122
+ _artifact_consumers: dict[str, set[str]] = PrivateAttr(
123
+ default_factory=lambda: defaultdict(set)
124
+ )
118
125
  _agent_status: dict[str, str] = PrivateAttr(default_factory=dict)
119
126
  _agent_snapshots: dict[str, AgentSnapshot] = PrivateAttr(default_factory=dict)
120
127
 
@@ -185,13 +192,15 @@ class DashboardEventCollector(AgentComponent):
185
192
  await self._update_agent_snapshot_locked(agent)
186
193
 
187
194
  # Build subscription info from agent's subscriptions
188
- subscription_info = SubscriptionInfo(from_agents=[], channels=[], mode="both")
195
+ subscription_info = SubscriptionInfo(from_agents=[], tags=[], mode="both")
189
196
 
190
197
  if agent.subscriptions:
191
198
  # Get first subscription's config (agents typically have one)
192
199
  sub = agent.subscriptions[0]
193
- subscription_info.from_agents = list(sub.from_agents) if sub.from_agents else []
194
- subscription_info.channels = list(sub.channels) if sub.channels else []
200
+ subscription_info.from_agents = (
201
+ list(sub.from_agents) if sub.from_agents else []
202
+ )
203
+ subscription_info.tags = list(sub.tags) if sub.tags else []
195
204
  subscription_info.mode = sub.mode
196
205
 
197
206
  # Create and store event
@@ -210,7 +219,9 @@ class DashboardEventCollector(AgentComponent):
210
219
  )
211
220
 
212
221
  self._events.append(event)
213
- logger.info(f"Agent activated: {agent.name} (correlation_id={event.correlation_id})")
222
+ logger.info(
223
+ f"Agent activated: {agent.name} (correlation_id={event.correlation_id})"
224
+ )
214
225
 
215
226
  # Broadcast via WebSocket if manager is configured
216
227
  if self._websocket_manager:
@@ -220,7 +231,9 @@ class DashboardEventCollector(AgentComponent):
220
231
 
221
232
  return inputs
222
233
 
223
- async def on_post_publish(self, agent: "Agent", ctx: Context, artifact: "Artifact") -> None:
234
+ async def on_post_publish(
235
+ self, agent: "Agent", ctx: Context, artifact: "Artifact"
236
+ ) -> None:
224
237
  """Emit message_published event when artifact is published.
225
238
 
226
239
  Args:
@@ -391,7 +404,9 @@ class DashboardEventCollector(AgentComponent):
391
404
  }
392
405
  runs = [record.to_graph_run() for record in self._run_registry.values()]
393
406
  agent_status = dict(self._agent_status)
394
- return GraphState(consumptions=consumptions, runs=runs, agent_status=agent_status)
407
+ return GraphState(
408
+ consumptions=consumptions, runs=runs, agent_status=agent_status
409
+ )
395
410
 
396
411
  async def snapshot_agent_registry(self) -> dict[str, AgentSnapshot]:
397
412
  """Return a snapshot of all known agents (active and inactive)."""
@@ -456,21 +471,17 @@ class DashboardEventCollector(AgentComponent):
456
471
  async def _update_agent_snapshot_locked(self, agent: "Agent") -> None:
457
472
  now = datetime.now(UTC)
458
473
  description = agent.description or ""
459
- subscriptions = sorted(
460
- {
461
- type_name
462
- for subscription in getattr(agent, "subscriptions", [])
463
- for type_name in getattr(subscription, "type_names", [])
464
- }
465
- )
466
- output_types = sorted(
467
- {
468
- output.spec.type_name
469
- for output in getattr(agent, "outputs", [])
470
- if getattr(output, "spec", None) is not None
471
- and getattr(output.spec, "type_name", "")
472
- }
473
- )
474
+ subscriptions = sorted({
475
+ type_name
476
+ for subscription in getattr(agent, "subscriptions", [])
477
+ for type_name in getattr(subscription, "type_names", [])
478
+ })
479
+ output_types = sorted({
480
+ output.spec.type_name
481
+ for output in getattr(agent, "outputs", [])
482
+ if getattr(output, "spec", None) is not None
483
+ and getattr(output.spec, "type_name", "")
484
+ })
474
485
  labels = sorted(agent.labels)
475
486
 
476
487
  signature_payload = {
@@ -519,7 +530,9 @@ class DashboardEventCollector(AgentComponent):
519
530
  first_seen=snapshot.first_seen,
520
531
  last_seen=snapshot.last_seen,
521
532
  signature=snapshot.signature,
522
- logic_operations=[dict(op) for op in snapshot.logic_operations], # Phase 1.2
533
+ logic_operations=[
534
+ dict(op) for op in snapshot.logic_operations
535
+ ], # Phase 1.2
523
536
  )
524
537
 
525
538
  def _snapshot_to_record(self, snapshot: AgentSnapshot) -> AgentSnapshotRecord:
@@ -545,19 +558,25 @@ class DashboardEventCollector(AgentComponent):
545
558
  """
546
559
  # Get visibility kind from class name, stripping "Visibility" suffix
547
560
  class_name = type(visibility).__name__
548
- kind = class_name[: -len("Visibility")] if class_name.endswith("Visibility") else class_name
561
+ kind = class_name.removesuffix("Visibility")
549
562
 
550
563
  spec = VisibilitySpec(kind=kind)
551
564
 
552
565
  # Extract type-specific fields
553
566
  if kind == "Private":
554
- spec.agents = list(visibility.agents) if hasattr(visibility, "agents") else []
567
+ spec.agents = (
568
+ list(visibility.agents) if hasattr(visibility, "agents") else []
569
+ )
555
570
  elif kind == "Labelled":
556
571
  spec.required_labels = (
557
- list(visibility.required_labels) if hasattr(visibility, "required_labels") else []
572
+ list(visibility.required_labels)
573
+ if hasattr(visibility, "required_labels")
574
+ else []
558
575
  )
559
576
  elif kind == "Tenant":
560
- spec.tenant_id = visibility.tenant_id if hasattr(visibility, "tenant_id") else None
577
+ spec.tenant_id = (
578
+ visibility.tenant_id if hasattr(visibility, "tenant_id") else None
579
+ )
561
580
 
562
581
  return spec
563
582
 
flock/dashboard/events.py CHANGED
@@ -14,7 +14,7 @@ class SubscriptionInfo(BaseModel):
14
14
  """Subscription configuration for an agent."""
15
15
 
16
16
  from_agents: list[str] = Field(default_factory=list)
17
- channels: list[str] = Field(default_factory=list)
17
+ tags: list[str] = Field(default_factory=list)
18
18
  mode: str = "both" # "both" | "events" | "direct"
19
19
 
20
20
 
@@ -123,8 +123,12 @@ class StreamingOutputEvent(BaseModel):
123
123
  is_final: bool = False # True when agent completes this output stream
124
124
 
125
125
  # Artifact tracking (Phase 6: for message streaming preview)
126
- artifact_id: str | None = None # Pre-generated artifact ID for streaming message nodes
127
- artifact_type: str | None = None # Artifact type name (e.g., "__main__.BookOutline")
126
+ artifact_id: str | None = (
127
+ None # Pre-generated artifact ID for streaming message nodes
128
+ )
129
+ artifact_type: str | None = (
130
+ None # Artifact type name (e.g., "__main__.BookOutline")
131
+ )
128
132
 
129
133
 
130
134
  class AgentCompletedEvent(BaseModel):
@@ -149,7 +153,9 @@ class AgentCompletedEvent(BaseModel):
149
153
  artifacts_produced: list[str] = Field(default_factory=list) # [artifact_id]
150
154
 
151
155
  # Metrics and state
152
- metrics: dict[str, Any] = Field(default_factory=dict) # {"tokens_used": 1234, "cost": 0.05}
156
+ metrics: dict[str, Any] = Field(
157
+ default_factory=dict
158
+ ) # {"tokens_used": 1234, "cost": 0.05}
153
159
  final_state: dict[str, Any] = Field(default_factory=dict) # Context.state snapshot
154
160
 
155
161
 
@@ -94,7 +94,9 @@ class DashboardLauncher:
94
94
 
95
95
  def _start_dev_server(self) -> None:
96
96
  """Start npm dev server for hot-reload development."""
97
- print(f"[Dashboard] Starting dev server (DASHBOARD_DEV=1) on port {self.port}...")
97
+ print(
98
+ f"[Dashboard] Starting dev server (DASHBOARD_DEV=1) on port {self.port}..."
99
+ )
98
100
 
99
101
  try:
100
102
  self._npm_process = subprocess.Popen(
@@ -104,7 +104,9 @@ class GraphStatistics(BaseModel):
104
104
  consumed_by_agent: dict[str, GraphAgentMetrics] = Field(
105
105
  default_factory=dict, alias="consumedByAgent"
106
106
  )
107
- artifact_summary: dict[str, Any] = Field(default_factory=dict, alias="artifactSummary")
107
+ artifact_summary: dict[str, Any] = Field(
108
+ default_factory=dict, alias="artifactSummary"
109
+ )
108
110
 
109
111
 
110
112
  class GraphArtifact(BaseModel):
@@ -128,8 +130,12 @@ class GraphRun(BaseModel):
128
130
  agent_name: str = Field(alias="agentName")
129
131
  correlation_id: str | None = Field(default=None, alias="correlationId")
130
132
  status: Literal["active", "completed", "error"] = "active"
131
- consumed_artifacts: list[str] = Field(default_factory=list, alias="consumedArtifacts")
132
- produced_artifacts: list[str] = Field(default_factory=list, alias="producedArtifacts")
133
+ consumed_artifacts: list[str] = Field(
134
+ default_factory=list, alias="consumedArtifacts"
135
+ )
136
+ produced_artifacts: list[str] = Field(
137
+ default_factory=list, alias="producedArtifacts"
138
+ )
133
139
  duration_ms: float | None = Field(default=None, alias="durationMs")
134
140
  started_at: datetime | None = Field(default=None, alias="startedAt")
135
141
  completed_at: datetime | None = Field(default=None, alias="completedAt")