flock-core 0.5.11__py3-none-any.whl → 0.5.21__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 (94) hide show
  1. flock/__init__.py +1 -1
  2. flock/agent/__init__.py +30 -0
  3. flock/agent/builder_helpers.py +192 -0
  4. flock/agent/builder_validator.py +169 -0
  5. flock/agent/component_lifecycle.py +325 -0
  6. flock/agent/context_resolver.py +141 -0
  7. flock/agent/mcp_integration.py +212 -0
  8. flock/agent/output_processor.py +304 -0
  9. flock/api/__init__.py +20 -0
  10. flock/{api_models.py → api/models.py} +0 -2
  11. flock/{service.py → api/service.py} +3 -3
  12. flock/cli.py +2 -2
  13. flock/components/__init__.py +41 -0
  14. flock/components/agent/__init__.py +22 -0
  15. flock/{components.py → components/agent/base.py} +4 -3
  16. flock/{utility/output_utility_component.py → components/agent/output_utility.py} +12 -7
  17. flock/components/orchestrator/__init__.py +22 -0
  18. flock/{orchestrator_component.py → components/orchestrator/base.py} +5 -293
  19. flock/components/orchestrator/circuit_breaker.py +95 -0
  20. flock/components/orchestrator/collection.py +143 -0
  21. flock/components/orchestrator/deduplication.py +78 -0
  22. flock/core/__init__.py +30 -0
  23. flock/core/agent.py +953 -0
  24. flock/{artifacts.py → core/artifacts.py} +1 -1
  25. flock/{context_provider.py → core/context_provider.py} +3 -3
  26. flock/core/orchestrator.py +1102 -0
  27. flock/{store.py → core/store.py} +99 -454
  28. flock/{subscription.py → core/subscription.py} +1 -1
  29. flock/dashboard/collector.py +5 -5
  30. flock/dashboard/events.py +1 -1
  31. flock/dashboard/graph_builder.py +7 -7
  32. flock/dashboard/routes/__init__.py +21 -0
  33. flock/dashboard/routes/control.py +327 -0
  34. flock/dashboard/routes/helpers.py +340 -0
  35. flock/dashboard/routes/themes.py +76 -0
  36. flock/dashboard/routes/traces.py +521 -0
  37. flock/dashboard/routes/websocket.py +108 -0
  38. flock/dashboard/service.py +43 -1316
  39. flock/engines/dspy/__init__.py +20 -0
  40. flock/engines/dspy/artifact_materializer.py +216 -0
  41. flock/engines/dspy/signature_builder.py +474 -0
  42. flock/engines/dspy/streaming_executor.py +812 -0
  43. flock/engines/dspy_engine.py +45 -1330
  44. flock/engines/examples/simple_batch_engine.py +2 -2
  45. flock/engines/streaming/__init__.py +3 -0
  46. flock/engines/streaming/sinks.py +489 -0
  47. flock/examples.py +7 -7
  48. flock/logging/logging.py +1 -16
  49. flock/models/__init__.py +10 -0
  50. flock/orchestrator/__init__.py +45 -0
  51. flock/{artifact_collector.py → orchestrator/artifact_collector.py} +3 -3
  52. flock/orchestrator/artifact_manager.py +168 -0
  53. flock/{batch_accumulator.py → orchestrator/batch_accumulator.py} +2 -2
  54. flock/orchestrator/component_runner.py +389 -0
  55. flock/orchestrator/context_builder.py +167 -0
  56. flock/{correlation_engine.py → orchestrator/correlation_engine.py} +2 -2
  57. flock/orchestrator/event_emitter.py +167 -0
  58. flock/orchestrator/initialization.py +184 -0
  59. flock/orchestrator/lifecycle_manager.py +226 -0
  60. flock/orchestrator/mcp_manager.py +202 -0
  61. flock/orchestrator/scheduler.py +189 -0
  62. flock/orchestrator/server_manager.py +234 -0
  63. flock/orchestrator/tracing.py +147 -0
  64. flock/storage/__init__.py +10 -0
  65. flock/storage/artifact_aggregator.py +158 -0
  66. flock/storage/in_memory/__init__.py +6 -0
  67. flock/storage/in_memory/artifact_filter.py +114 -0
  68. flock/storage/in_memory/history_aggregator.py +115 -0
  69. flock/storage/sqlite/__init__.py +10 -0
  70. flock/storage/sqlite/agent_history_queries.py +154 -0
  71. flock/storage/sqlite/consumption_loader.py +100 -0
  72. flock/storage/sqlite/query_builder.py +112 -0
  73. flock/storage/sqlite/query_params_builder.py +91 -0
  74. flock/storage/sqlite/schema_manager.py +168 -0
  75. flock/storage/sqlite/summary_queries.py +194 -0
  76. flock/utils/__init__.py +14 -0
  77. flock/utils/async_utils.py +67 -0
  78. flock/{runtime.py → utils/runtime.py} +3 -3
  79. flock/utils/time_utils.py +53 -0
  80. flock/utils/type_resolution.py +38 -0
  81. flock/{utilities.py → utils/utilities.py} +2 -2
  82. flock/utils/validation.py +57 -0
  83. flock/utils/visibility.py +79 -0
  84. flock/utils/visibility_utils.py +134 -0
  85. {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/METADATA +19 -5
  86. {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/RECORD +92 -34
  87. flock/agent.py +0 -1578
  88. flock/orchestrator.py +0 -1983
  89. /flock/{visibility.py → core/visibility.py} +0 -0
  90. /flock/{system_artifacts.py → models/system_artifacts.py} +0 -0
  91. /flock/{helper → utils}/cli_helper.py +0 -0
  92. {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/WHEEL +0 -0
  93. {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/entry_points.txt +0 -0
  94. {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,304 @@
1
+ """Agent output processing - validation, filtering, and artifact creation.
2
+
3
+ Phase 4: Extracted from agent.py to eliminate C-rated complexity in _make_outputs_for_group.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import logging
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from flock.core.artifacts import Artifact
12
+ from flock.logging.logging import get_logger
13
+ from flock.registry import type_registry
14
+ from flock.utils.runtime import Context, EvalResult
15
+ from flock.utils.type_resolution import TypeResolutionHelper
16
+
17
+
18
+ if TYPE_CHECKING:
19
+ from flock.agent import AgentOutput, OutputGroup
20
+
21
+
22
+ logger = get_logger(__name__)
23
+
24
+
25
+ class OutputProcessor:
26
+ """Handles agent output validation, filtering, and artifact creation.
27
+
28
+ This module encapsulates all output processing logic including:
29
+ - Engine contract validation (expected vs actual artifact counts)
30
+ - WHERE filtering (reduces artifacts based on predicates)
31
+ - VALIDATE checks (fail-fast on validation errors)
32
+ - Dynamic visibility resolution
33
+ - Artifact matching and payload extraction
34
+ """
35
+
36
+ def __init__(self, agent_name: str):
37
+ """Initialize OutputProcessor for a specific agent.
38
+
39
+ Args:
40
+ agent_name: Name of the agent (for error messages and logging)
41
+ """
42
+ self._agent_name = agent_name
43
+ self._logger = logging.getLogger(__name__)
44
+
45
+ async def make_outputs_for_group(
46
+ self,
47
+ ctx: Context,
48
+ result: EvalResult,
49
+ output_group: OutputGroup,
50
+ ) -> list[Artifact]:
51
+ """Phase 3/5: Validate, filter, and create artifacts for specific OutputGroup.
52
+
53
+ This function:
54
+ 1. Validates that the engine fulfilled its contract (produced expected count)
55
+ 2. Applies WHERE filtering (reduces artifacts, no error)
56
+ 3. Applies VALIDATE checks (raises ValueError if validation fails)
57
+ 4. Applies visibility (static or dynamic)
58
+ 5. Creates final artifacts with agent metadata
59
+
60
+ Args:
61
+ ctx: Context for this group
62
+ result: EvalResult from engine for THIS group
63
+ output_group: OutputGroup defining expected outputs
64
+
65
+ Returns:
66
+ List of artifacts matching this group's outputs
67
+
68
+ Raises:
69
+ ValueError: If engine violated contract or validation failed
70
+ """
71
+ produced: list[Artifact] = []
72
+
73
+ for output_decl in output_group.outputs:
74
+ # 1. Find ALL matching artifacts for this type
75
+ expected_canonical = type_registry.resolve_name(output_decl.spec.type_name)
76
+
77
+ matching_artifacts: list[Artifact] = []
78
+ for artifact in result.artifacts:
79
+ artifact_canonical = TypeResolutionHelper.safe_resolve(
80
+ type_registry, artifact.type
81
+ )
82
+ if artifact_canonical == expected_canonical:
83
+ matching_artifacts.append(artifact)
84
+
85
+ # 2. STRICT VALIDATION: Engine must produce exactly what was promised
86
+ # (This happens BEFORE filtering so engine contract is validated first)
87
+ expected_count = output_decl.count
88
+ actual_count = len(matching_artifacts)
89
+
90
+ if actual_count != expected_count:
91
+ raise ValueError(
92
+ f"Engine contract violation in agent '{self._agent_name}': "
93
+ f"Expected {expected_count} artifact(s) of type '{output_decl.spec.type_name}', "
94
+ f"but engine produced {actual_count}. "
95
+ f"Check your engine implementation to ensure it generates the correct number of outputs."
96
+ )
97
+
98
+ # 3. Apply WHERE filtering (Phase 5)
99
+ # Filtering reduces the number of published artifacts (this is intentional)
100
+ # NOTE: Predicates expect Pydantic model instances, not dicts
101
+ model_cls = type_registry.resolve(output_decl.spec.type_name)
102
+
103
+ if output_decl.filter_predicate:
104
+ original_count = len(matching_artifacts)
105
+ filtered = []
106
+ for a in matching_artifacts:
107
+ # Reconstruct Pydantic model from payload dict
108
+ model_instance = model_cls(**a.payload)
109
+ if output_decl.filter_predicate(model_instance):
110
+ filtered.append(a)
111
+ matching_artifacts = filtered
112
+ logger.debug(
113
+ f"Agent {self._agent_name}: WHERE filter reduced artifacts from "
114
+ f"{original_count} to {len(matching_artifacts)} for type {output_decl.spec.type_name}"
115
+ )
116
+
117
+ # 4. Apply VALIDATE checks (Phase 5)
118
+ # Validation failures raise errors (fail-fast)
119
+ if output_decl.validate_predicate:
120
+ if callable(output_decl.validate_predicate):
121
+ # Single predicate
122
+ for artifact in matching_artifacts:
123
+ # Reconstruct Pydantic model from payload dict
124
+ model_instance = model_cls(**artifact.payload)
125
+ if not output_decl.validate_predicate(model_instance):
126
+ raise ValueError(
127
+ f"Validation failed for {output_decl.spec.type_name} "
128
+ f"in agent '{self._agent_name}'"
129
+ )
130
+ elif isinstance(output_decl.validate_predicate, list):
131
+ # List of (callable, error_msg) tuples
132
+ for artifact in matching_artifacts:
133
+ # Reconstruct Pydantic model from payload dict
134
+ model_instance = model_cls(**artifact.payload)
135
+ for check, error_msg in output_decl.validate_predicate:
136
+ if not check(model_instance):
137
+ raise ValueError(
138
+ f"{error_msg}: {output_decl.spec.type_name}"
139
+ )
140
+
141
+ # 5. Apply visibility and create artifacts (Phase 5)
142
+ for artifact_from_engine in matching_artifacts:
143
+ metadata = {
144
+ "correlation_id": ctx.correlation_id,
145
+ "artifact_id": artifact_from_engine.id, # Preserve engine's ID
146
+ }
147
+
148
+ # Determine visibility (static or dynamic)
149
+ visibility = output_decl.default_visibility
150
+ if callable(visibility):
151
+ # Dynamic visibility based on artifact content
152
+ # Reconstruct Pydantic model from payload dict
153
+ model_instance = model_cls(**artifact_from_engine.payload)
154
+ visibility = visibility(model_instance)
155
+
156
+ # Override metadata visibility
157
+ metadata["visibility"] = visibility
158
+
159
+ # Re-wrap the artifact with agent metadata
160
+ artifact = output_decl.apply(
161
+ artifact_from_engine.payload,
162
+ produced_by=self._agent_name,
163
+ metadata=metadata,
164
+ )
165
+ produced.append(artifact)
166
+ # Phase 6 SECURITY FIX: REMOVED publishing - orchestrator now handles it
167
+ # This fixes Vulnerability #2 (WRITE Bypass) - agents can no longer publish directly
168
+ # await ctx.board.publish(artifact)
169
+
170
+ return produced
171
+
172
+ async def make_outputs(
173
+ self, ctx: Context, result: EvalResult, output_groups: list[OutputGroup]
174
+ ) -> list[Artifact]:
175
+ """Output creation method for all output groups.
176
+
177
+ This method processes all output groups from a single engine evaluation.
178
+
179
+ Args:
180
+ ctx: Execution context
181
+ result: EvalResult from engine
182
+ output_groups: All output groups for the agent
183
+
184
+ Returns:
185
+ List of produced artifacts
186
+ """
187
+ if not output_groups:
188
+ # Utility agents may not publish anything
189
+ return list(result.artifacts)
190
+
191
+ produced: list[Artifact] = []
192
+
193
+ # For Phase 2: Iterate ALL output_groups (even though we only have 1 engine call)
194
+ # Phase 3 will modify this to call engine once PER group
195
+ for output_group in output_groups:
196
+ for output_decl in output_group.outputs:
197
+ # Phase 6: Find the matching artifact from engine result to preserve its ID
198
+ matching_artifact = self.find_matching_artifact(output_decl, result)
199
+
200
+ payload = self.select_payload(output_decl, result)
201
+ if payload is None:
202
+ continue
203
+ metadata = {
204
+ "correlation_id": ctx.correlation_id,
205
+ }
206
+
207
+ # Phase 6: Preserve artifact ID from engine (for streaming message preview)
208
+ if matching_artifact:
209
+ metadata["artifact_id"] = matching_artifact.id
210
+
211
+ artifact = output_decl.apply(
212
+ payload, produced_by=self._agent_name, metadata=metadata
213
+ )
214
+ produced.append(artifact)
215
+ # Phase 6: REMOVED publishing - orchestrator now handles it
216
+ # await ctx.board.publish(artifact)
217
+
218
+ return produced
219
+
220
+ def prepare_group_context(
221
+ self, ctx: Context, group_idx: int, output_group: OutputGroup
222
+ ) -> Context:
223
+ """Phase 3: Prepare context specific to this OutputGroup.
224
+
225
+ Creates a modified context for this group's engine call, potentially
226
+ with group-specific instructions or metadata.
227
+
228
+ Args:
229
+ ctx: Base context
230
+ group_idx: Index of this group (0-based)
231
+ output_group: The OutputGroup being processed
232
+
233
+ Returns:
234
+ Context for this group (may be the same instance or modified)
235
+ """
236
+ # For now, return the same context
237
+ # Phase 4 will add group-specific system prompts here
238
+ # Future: ctx.clone() and add group_description to system prompt
239
+ return ctx
240
+
241
+ def find_matching_artifact(
242
+ self, output_decl: AgentOutput, result: EvalResult
243
+ ) -> Artifact | None:
244
+ """Phase 6: Find artifact from engine result that matches this output declaration.
245
+
246
+ Returns the artifact object (with its ID) so we can preserve it when creating
247
+ the final published artifact. This ensures streaming events use the same ID.
248
+
249
+ Args:
250
+ output_decl: Output declaration to match
251
+ result: Engine result containing artifacts
252
+
253
+ Returns:
254
+ Matching artifact or None if not found
255
+ """
256
+ if not result.artifacts:
257
+ return None
258
+
259
+ # Normalize the expected type name to canonical form
260
+ expected_canonical = type_registry.resolve_name(output_decl.spec.type_name)
261
+
262
+ for artifact in result.artifacts:
263
+ # Normalize artifact type name to canonical form for comparison
264
+ artifact_canonical = TypeResolutionHelper.safe_resolve(
265
+ type_registry, artifact.type
266
+ )
267
+ if artifact_canonical == expected_canonical:
268
+ return artifact
269
+
270
+ return None
271
+
272
+ def select_payload(
273
+ self, output_decl: AgentOutput, result: EvalResult
274
+ ) -> dict[str, Any] | None:
275
+ """Extract payload from engine result for output declaration.
276
+
277
+ Args:
278
+ output_decl: Output declaration defining expected type
279
+ result: Engine result containing artifacts
280
+
281
+ Returns:
282
+ Payload dict or None if not found
283
+ """
284
+ # Normalize the expected type name to canonical form
285
+ expected_canonical = type_registry.resolve_name(output_decl.spec.type_name)
286
+
287
+ # Try to find payload in artifacts first
288
+ if result.artifacts:
289
+ for artifact in result.artifacts:
290
+ # Normalize artifact type name to canonical form for comparison
291
+ artifact_canonical = TypeResolutionHelper.safe_resolve(
292
+ type_registry, artifact.type
293
+ )
294
+ if artifact_canonical == expected_canonical:
295
+ return artifact.payload
296
+
297
+ # Fallback to state entries keyed by type name
298
+ maybe_data = result.state.get(output_decl.spec.type_name)
299
+ if isinstance(maybe_data, dict):
300
+ return maybe_data
301
+ return None
302
+
303
+
304
+ __all__ = ["OutputProcessor"]
flock/api/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ """HTTP API service layer for Flock.
2
+
3
+ This module contains HTTP service implementations and API models for
4
+ serving the Flock orchestrator over HTTP with REST endpoints.
5
+ """
6
+
7
+ from flock.api.models import (
8
+ ArtifactPublishRequest,
9
+ ArtifactPublishResponse,
10
+ CorrelationStatusResponse,
11
+ )
12
+ from flock.api.service import BlackboardHTTPService
13
+
14
+
15
+ __all__ = [
16
+ "ArtifactPublishRequest",
17
+ "ArtifactPublishResponse",
18
+ "BlackboardHTTPService",
19
+ "CorrelationStatusResponse",
20
+ ]
@@ -6,9 +6,7 @@ This improves API documentation and enables SDK generation.
6
6
  All models maintain 100% backwards compatibility with existing wire format.
7
7
  """
8
8
 
9
- from datetime import datetime
10
9
  from typing import Any, Literal
11
- from uuid import UUID
12
10
 
13
11
  from pydantic import BaseModel, Field
14
12
 
@@ -10,7 +10,7 @@ from uuid import UUID
10
10
  from fastapi import FastAPI, HTTPException, Query
11
11
  from fastapi.responses import PlainTextResponse
12
12
 
13
- from flock.api_models import (
13
+ from flock.api.models import (
14
14
  Agent,
15
15
  AgentListResponse,
16
16
  AgentRunRequest,
@@ -24,12 +24,12 @@ from flock.api_models import (
24
24
  HealthResponse,
25
25
  ProducedArtifact,
26
26
  )
27
+ from flock.core.store import ArtifactEnvelope, ConsumptionRecord, FilterConfig
27
28
  from flock.registry import type_registry
28
- from flock.store import ArtifactEnvelope, ConsumptionRecord, FilterConfig
29
29
 
30
30
 
31
31
  if TYPE_CHECKING:
32
- from flock.orchestrator import Flock
32
+ from flock.core import Flock
33
33
 
34
34
 
35
35
  class BlackboardHTTPService:
flock/cli.py CHANGED
@@ -12,8 +12,8 @@ from typer.models import OptionInfo
12
12
 
13
13
  # Lazy import: only import examples when CLI commands are invoked
14
14
  # This prevents polluting type_registry on every package import
15
- from flock.service import BlackboardHTTPService
16
- from flock.store import SQLiteBlackboardStore
15
+ from flock.api.service import BlackboardHTTPService
16
+ from flock.core.store import SQLiteBlackboardStore
17
17
 
18
18
 
19
19
  app = typer.Typer(help="Blackboard Agents CLI")
@@ -0,0 +1,41 @@
1
+ """Component library for extending Flock agents and orchestrators."""
2
+
3
+ # Agent components
4
+ from flock.components.agent import (
5
+ AgentComponent,
6
+ AgentComponentConfig,
7
+ EngineComponent,
8
+ OutputUtilityComponent,
9
+ OutputUtilityConfig,
10
+ TracedModelMeta,
11
+ )
12
+
13
+ # Orchestrator components
14
+ from flock.components.orchestrator import (
15
+ BuiltinCollectionComponent,
16
+ CircuitBreakerComponent,
17
+ CollectionResult,
18
+ DeduplicationComponent,
19
+ OrchestratorComponent,
20
+ OrchestratorComponentConfig,
21
+ ScheduleDecision,
22
+ )
23
+
24
+
25
+ __all__ = [
26
+ # Agent components
27
+ "AgentComponent",
28
+ "AgentComponentConfig",
29
+ "EngineComponent",
30
+ "OutputUtilityComponent",
31
+ "OutputUtilityConfig",
32
+ "TracedModelMeta",
33
+ # Orchestrator components
34
+ "BuiltinCollectionComponent",
35
+ "CircuitBreakerComponent",
36
+ "CollectionResult",
37
+ "DeduplicationComponent",
38
+ "OrchestratorComponent",
39
+ "OrchestratorComponentConfig",
40
+ "ScheduleDecision",
41
+ ]
@@ -0,0 +1,22 @@
1
+ """Agent component library - Base classes and built-in components."""
2
+
3
+ from flock.components.agent.base import (
4
+ AgentComponent,
5
+ AgentComponentConfig,
6
+ EngineComponent,
7
+ TracedModelMeta,
8
+ )
9
+ from flock.components.agent.output_utility import (
10
+ OutputUtilityComponent,
11
+ OutputUtilityConfig,
12
+ )
13
+
14
+
15
+ __all__ = [
16
+ "AgentComponent",
17
+ "AgentComponentConfig",
18
+ "EngineComponent",
19
+ "OutputUtilityComponent",
20
+ "OutputUtilityConfig",
21
+ "TracedModelMeta",
22
+ ]
@@ -1,4 +1,4 @@
1
- """Agent component abstractions."""
1
+ """Agent component base classes and configuration."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -13,8 +13,8 @@ from flock.logging.auto_trace import AutoTracedMeta
13
13
 
14
14
  if TYPE_CHECKING: # pragma: no cover - type checking only
15
15
  from flock.agent import Agent, OutputGroup
16
- from flock.artifacts import Artifact
17
- from flock.runtime import Context, EvalInputs, EvalResult
16
+ from flock.core.artifacts import Artifact
17
+ from flock.utils.runtime import Context, EvalInputs, EvalResult
18
18
 
19
19
  T = TypeVar("T", bound="AgentComponentConfig")
20
20
 
@@ -219,4 +219,5 @@ __all__ = [
219
219
  "AgentComponent",
220
220
  "AgentComponentConfig",
221
221
  "EngineComponent",
222
+ "TracedModelMeta",
222
223
  ]
@@ -1,25 +1,24 @@
1
- # src/flock/components/utility/output_utility_component.py
2
- """Output formatting and display functionality for agents using unified component architecture."""
1
+ """Output formatting and display functionality for agents."""
3
2
 
4
3
  import re
5
4
  from typing import TYPE_CHECKING, Any
6
5
 
7
6
  from pydantic import Field
8
7
 
9
- from flock.components import AgentComponent, AgentComponentConfig
8
+ from flock.components.agent.base import AgentComponent, AgentComponentConfig
10
9
  from flock.logging.formatters.themed_formatter import (
11
10
  ThemedAgentResultFormatter,
12
11
  )
13
12
  from flock.logging.formatters.themes import OutputTheme
14
13
  from flock.logging.logging import get_logger
15
- from flock.runtime import Context, EvalInputs, EvalResult
14
+ from flock.utils.runtime import Context, EvalInputs, EvalResult
16
15
 
17
16
 
18
17
  if TYPE_CHECKING: # pragma: no cover - type checking only
19
- from flock.agent import Agent
18
+ from flock.core import Agent
20
19
 
21
20
 
22
- logger = get_logger("components.utility.output")
21
+ logger = get_logger("components.agent.output_utility")
23
22
 
24
23
 
25
24
  class OutputUtilityConfig(AgentComponentConfig):
@@ -188,7 +187,7 @@ class OutputUtilityComponent(AgentComponent):
188
187
  if ctx:
189
188
  import asyncio
190
189
 
191
- from flock.agent import Agent
190
+ from flock.core import Agent
192
191
 
193
192
  # Wait until no streams are active
194
193
  max_wait = 30 # seconds
@@ -244,3 +243,9 @@ class OutputUtilityComponent(AgentComponent):
244
243
  def add_custom_formatter(self, key: str, formatter_name: str) -> None:
245
244
  """Add a custom formatter for a specific output key."""
246
245
  self.config.custom_formatters[key] = formatter_name
246
+
247
+
248
+ __all__ = [
249
+ "OutputUtilityComponent",
250
+ "OutputUtilityConfig",
251
+ ]
@@ -0,0 +1,22 @@
1
+ """Orchestrator component library - Base classes and built-in components."""
2
+
3
+ from flock.components.orchestrator.base import (
4
+ CollectionResult,
5
+ OrchestratorComponent,
6
+ OrchestratorComponentConfig,
7
+ ScheduleDecision,
8
+ )
9
+ from flock.components.orchestrator.circuit_breaker import CircuitBreakerComponent
10
+ from flock.components.orchestrator.collection import BuiltinCollectionComponent
11
+ from flock.components.orchestrator.deduplication import DeduplicationComponent
12
+
13
+
14
+ __all__ = [
15
+ "BuiltinCollectionComponent",
16
+ "CircuitBreakerComponent",
17
+ "CollectionResult",
18
+ "DeduplicationComponent",
19
+ "OrchestratorComponent",
20
+ "OrchestratorComponentConfig",
21
+ "ScheduleDecision",
22
+ ]