flock-core 0.5.10__py3-none-any.whl → 0.5.20__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 (91) 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 +283 -0
  11. flock/{service.py → api/service.py} +121 -63
  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/graph_builder.py +7 -7
  31. flock/dashboard/routes/__init__.py +21 -0
  32. flock/dashboard/routes/control.py +327 -0
  33. flock/dashboard/routes/helpers.py +340 -0
  34. flock/dashboard/routes/themes.py +76 -0
  35. flock/dashboard/routes/traces.py +521 -0
  36. flock/dashboard/routes/websocket.py +108 -0
  37. flock/dashboard/service.py +44 -1294
  38. flock/engines/dspy/__init__.py +20 -0
  39. flock/engines/dspy/artifact_materializer.py +216 -0
  40. flock/engines/dspy/signature_builder.py +474 -0
  41. flock/engines/dspy/streaming_executor.py +858 -0
  42. flock/engines/dspy_engine.py +45 -1330
  43. flock/engines/examples/simple_batch_engine.py +2 -2
  44. flock/examples.py +7 -7
  45. flock/logging/logging.py +1 -16
  46. flock/models/__init__.py +10 -0
  47. flock/models/system_artifacts.py +33 -0
  48. flock/orchestrator/__init__.py +45 -0
  49. flock/{artifact_collector.py → orchestrator/artifact_collector.py} +3 -3
  50. flock/orchestrator/artifact_manager.py +168 -0
  51. flock/{batch_accumulator.py → orchestrator/batch_accumulator.py} +2 -2
  52. flock/orchestrator/component_runner.py +389 -0
  53. flock/orchestrator/context_builder.py +167 -0
  54. flock/{correlation_engine.py → orchestrator/correlation_engine.py} +2 -2
  55. flock/orchestrator/event_emitter.py +167 -0
  56. flock/orchestrator/initialization.py +184 -0
  57. flock/orchestrator/lifecycle_manager.py +226 -0
  58. flock/orchestrator/mcp_manager.py +202 -0
  59. flock/orchestrator/scheduler.py +189 -0
  60. flock/orchestrator/server_manager.py +234 -0
  61. flock/orchestrator/tracing.py +147 -0
  62. flock/storage/__init__.py +10 -0
  63. flock/storage/artifact_aggregator.py +158 -0
  64. flock/storage/in_memory/__init__.py +6 -0
  65. flock/storage/in_memory/artifact_filter.py +114 -0
  66. flock/storage/in_memory/history_aggregator.py +115 -0
  67. flock/storage/sqlite/__init__.py +10 -0
  68. flock/storage/sqlite/agent_history_queries.py +154 -0
  69. flock/storage/sqlite/consumption_loader.py +100 -0
  70. flock/storage/sqlite/query_builder.py +112 -0
  71. flock/storage/sqlite/query_params_builder.py +91 -0
  72. flock/storage/sqlite/schema_manager.py +168 -0
  73. flock/storage/sqlite/summary_queries.py +194 -0
  74. flock/utils/__init__.py +14 -0
  75. flock/utils/async_utils.py +67 -0
  76. flock/{runtime.py → utils/runtime.py} +3 -3
  77. flock/utils/time_utils.py +53 -0
  78. flock/utils/type_resolution.py +38 -0
  79. flock/{utilities.py → utils/utilities.py} +2 -2
  80. flock/utils/validation.py +57 -0
  81. flock/utils/visibility.py +79 -0
  82. flock/utils/visibility_utils.py +134 -0
  83. {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/METADATA +69 -61
  84. {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/RECORD +89 -31
  85. flock/agent.py +0 -1578
  86. flock/orchestrator.py +0 -1746
  87. /flock/{visibility.py → core/visibility.py} +0 -0
  88. /flock/{helper → utils}/cli_helper.py +0 -0
  89. {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/WHEEL +0 -0
  90. {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/entry_points.txt +0 -0
  91. {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/licenses/LICENSE +0 -0
@@ -10,18 +10,46 @@ 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 (
14
+ Agent,
15
+ AgentListResponse,
16
+ AgentRunRequest,
17
+ AgentRunResponse,
18
+ AgentSubscription,
19
+ ArtifactListResponse,
20
+ ArtifactPublishRequest,
21
+ ArtifactPublishResponse,
22
+ ArtifactSummaryResponse,
23
+ CorrelationStatusResponse,
24
+ HealthResponse,
25
+ ProducedArtifact,
26
+ )
27
+ from flock.core.store import ArtifactEnvelope, ConsumptionRecord, FilterConfig
13
28
  from flock.registry import type_registry
14
- from flock.store import ArtifactEnvelope, ConsumptionRecord, FilterConfig
15
29
 
16
30
 
17
31
  if TYPE_CHECKING:
18
- from flock.orchestrator import Flock
32
+ from flock.core import Flock
19
33
 
20
34
 
21
35
  class BlackboardHTTPService:
22
36
  def __init__(self, orchestrator: Flock) -> None:
23
37
  self.orchestrator = orchestrator
24
- self.app = FastAPI(title="Blackboard Agents Service", version="1.0.0")
38
+ self.app = FastAPI(
39
+ title="Flock REST API Documentation",
40
+ version="1.0.0",
41
+ description="RESTful API for interacting with Flock agents and artifacts",
42
+ openapi_tags=[
43
+ {
44
+ "name": "Public API",
45
+ "description": "**Production-ready endpoints** for publishing artifacts, running agents, and querying data. Use these in your applications.",
46
+ },
47
+ {
48
+ "name": "Health & Metrics",
49
+ "description": "Monitoring endpoints for health checks and metrics collection.",
50
+ },
51
+ ],
52
+ )
25
53
  self._register_routes()
26
54
 
27
55
  def _register_routes(self) -> None:
@@ -92,19 +120,25 @@ class BlackboardHTTPService:
92
120
  end=_parse_datetime(end, "to"),
93
121
  )
94
122
 
95
- @app.post("/api/v1/artifacts")
96
- async def publish_artifact(body: dict[str, Any]) -> dict[str, str]:
97
- type_name = body.get("type")
98
- payload = body.get("payload") or {}
99
- if not type_name:
100
- raise HTTPException(status_code=400, detail="type is required")
123
+ @app.post(
124
+ "/api/v1/artifacts",
125
+ response_model=ArtifactPublishResponse,
126
+ tags=["Public API"],
127
+ )
128
+ async def publish_artifact(
129
+ body: ArtifactPublishRequest,
130
+ ) -> ArtifactPublishResponse:
101
131
  try:
102
- await orchestrator.publish({"type": type_name, **payload})
132
+ await orchestrator.publish({"type": body.type, **body.payload})
103
133
  except Exception as exc: # pragma: no cover - FastAPI converts
104
134
  raise HTTPException(status_code=400, detail=str(exc)) from exc
105
- return {"status": "accepted"}
135
+ return ArtifactPublishResponse(status="accepted")
106
136
 
107
- @app.get("/api/v1/artifacts")
137
+ @app.get(
138
+ "/api/v1/artifacts",
139
+ response_model=ArtifactListResponse,
140
+ tags=["Public API"],
141
+ )
108
142
  async def list_artifacts(
109
143
  type_names: list[str] | None = Query(None, alias="type"),
110
144
  produced_by: list[str] | None = Query(None),
@@ -116,7 +150,7 @@ class BlackboardHTTPService:
116
150
  limit: int = Query(50, ge=1, le=500),
117
151
  offset: int = Query(0, ge=0),
118
152
  embed_meta: bool = Query(False, alias="embed_meta"),
119
- ) -> dict[str, Any]:
153
+ ) -> ArtifactListResponse:
120
154
  filters = _make_filter_config(
121
155
  type_names,
122
156
  produced_by,
@@ -140,12 +174,16 @@ class BlackboardHTTPService:
140
174
  )
141
175
  else:
142
176
  items.append(_serialize_artifact(artifact))
143
- return {
144
- "items": items,
145
- "pagination": {"limit": limit, "offset": offset, "total": total},
146
- }
177
+ return ArtifactListResponse(
178
+ items=items,
179
+ pagination={"limit": limit, "offset": offset, "total": total},
180
+ )
147
181
 
148
- @app.get("/api/v1/artifacts/summary")
182
+ @app.get(
183
+ "/api/v1/artifacts/summary",
184
+ response_model=ArtifactSummaryResponse,
185
+ tags=["Public API"],
186
+ )
149
187
  async def summarize_artifacts(
150
188
  type_names: list[str] | None = Query(None, alias="type"),
151
189
  produced_by: list[str] | None = Query(None),
@@ -154,7 +192,7 @@ class BlackboardHTTPService:
154
192
  start: str | None = Query(None, alias="from"),
155
193
  end: str | None = Query(None, alias="to"),
156
194
  visibility: list[str] | None = Query(None),
157
- ) -> dict[str, Any]:
195
+ ) -> ArtifactSummaryResponse:
158
196
  filters = _make_filter_config(
159
197
  type_names,
160
198
  produced_by,
@@ -165,33 +203,30 @@ class BlackboardHTTPService:
165
203
  end,
166
204
  )
167
205
  summary = await orchestrator.store.summarize_artifacts(filters)
168
- return {"summary": summary}
206
+ return ArtifactSummaryResponse(summary=summary)
169
207
 
170
- @app.get("/api/v1/artifacts/{artifact_id}")
208
+ @app.get("/api/v1/artifacts/{artifact_id}", tags=["Public API"])
171
209
  async def get_artifact(artifact_id: UUID) -> dict[str, Any]:
172
210
  artifact = await orchestrator.store.get(artifact_id)
173
211
  if artifact is None:
174
212
  raise HTTPException(status_code=404, detail="artifact not found")
175
213
  return _serialize_artifact(artifact)
176
214
 
177
- @app.post("/api/v1/agents/{name}/run")
178
- async def run_agent(name: str, body: dict[str, Any]) -> dict[str, Any]:
215
+ @app.post(
216
+ "/api/v1/agents/{name}/run",
217
+ response_model=AgentRunResponse,
218
+ tags=["Public API"],
219
+ )
220
+ async def run_agent(name: str, body: AgentRunRequest) -> AgentRunResponse:
179
221
  try:
180
222
  agent = orchestrator.get_agent(name)
181
223
  except KeyError as exc:
182
224
  raise HTTPException(status_code=404, detail="agent not found") from exc
183
225
 
184
- inputs_data: list[dict[str, Any]] = body.get("inputs") or []
185
226
  inputs = []
186
- for item in inputs_data:
187
- type_name = item.get("type")
188
- payload = item.get("payload") or {}
189
- if not type_name:
190
- raise HTTPException(
191
- status_code=400, detail="Each input requires 'type'."
192
- )
193
- model = type_registry.resolve(type_name)
194
- instance = model(**payload)
227
+ for item in body.inputs:
228
+ model = type_registry.resolve(item.type)
229
+ instance = model(**item.payload)
195
230
  inputs.append(instance)
196
231
 
197
232
  try:
@@ -201,40 +236,42 @@ class BlackboardHTTPService:
201
236
  status_code=500, detail=f"Agent execution failed: {exc}"
202
237
  ) from exc
203
238
 
204
- return {
205
- "artifacts": [
206
- {
207
- "id": str(artifact.id),
208
- "type": artifact.type,
209
- "payload": artifact.payload,
210
- "produced_by": artifact.produced_by,
211
- }
239
+ return AgentRunResponse(
240
+ artifacts=[
241
+ ProducedArtifact(
242
+ id=str(artifact.id),
243
+ type=artifact.type,
244
+ payload=artifact.payload,
245
+ produced_by=artifact.produced_by,
246
+ )
212
247
  for artifact in outputs
213
248
  ]
214
- }
249
+ )
215
250
 
216
- @app.get("/api/v1/agents")
217
- async def list_agents() -> dict[str, Any]:
218
- return {
219
- "agents": [
220
- {
221
- "name": agent.name,
222
- "description": agent.description,
223
- "subscriptions": [
224
- {
225
- "types": list(subscription.type_names),
226
- "mode": subscription.mode,
227
- "delivery": subscription.delivery,
228
- }
251
+ @app.get(
252
+ "/api/v1/agents", response_model=AgentListResponse, tags=["Public API"]
253
+ )
254
+ async def list_agents() -> AgentListResponse:
255
+ return AgentListResponse(
256
+ agents=[
257
+ Agent(
258
+ name=agent.name,
259
+ description=agent.description or "",
260
+ subscriptions=[
261
+ AgentSubscription(
262
+ types=list(subscription.type_names),
263
+ mode=subscription.mode,
264
+ delivery=subscription.delivery,
265
+ )
229
266
  for subscription in agent.subscriptions
230
267
  ],
231
- "outputs": [output.spec.type_name for output in agent.outputs],
232
- }
268
+ outputs=[output.spec.type_name for output in agent.outputs],
269
+ )
233
270
  for agent in orchestrator.agents
234
271
  ]
235
- }
272
+ )
236
273
 
237
- @app.get("/api/v1/agents/{agent_id}/history-summary")
274
+ @app.get("/api/v1/agents/{agent_id}/history-summary", tags=["Public API"])
238
275
  async def agent_history(
239
276
  agent_id: str,
240
277
  type_names: list[str] | None = Query(None, alias="type"),
@@ -257,11 +294,32 @@ class BlackboardHTTPService:
257
294
  summary = await orchestrator.store.agent_history_summary(agent_id, filters)
258
295
  return {"agent_id": agent_id, "summary": summary}
259
296
 
260
- @app.get("/health")
261
- async def health() -> dict[str, str]: # pragma: no cover - trivial
262
- return {"status": "ok"}
297
+ @app.get(
298
+ "/api/v1/correlations/{correlation_id}/status",
299
+ response_model=CorrelationStatusResponse,
300
+ tags=["Public API"],
301
+ )
302
+ async def get_correlation_status(
303
+ correlation_id: str,
304
+ ) -> CorrelationStatusResponse:
305
+ """Get the status of a workflow by correlation ID.
306
+
307
+ Returns workflow state (active/completed/failed/not_found), pending work status,
308
+ artifact counts, error counts, and timestamps.
309
+
310
+ This endpoint is useful for polling to check if a workflow has completed.
311
+ """
312
+ try:
313
+ status = await orchestrator.get_correlation_status(correlation_id)
314
+ return CorrelationStatusResponse(**status)
315
+ except ValueError as exc:
316
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
317
+
318
+ @app.get("/health", response_model=HealthResponse, tags=["Health & Metrics"])
319
+ async def health() -> HealthResponse: # pragma: no cover - trivial
320
+ return HealthResponse(status="ok")
263
321
 
264
- @app.get("/metrics")
322
+ @app.get("/metrics", tags=["Health & Metrics"])
265
323
  async def metrics() -> PlainTextResponse:
266
324
  lines = [
267
325
  f"blackboard_{key} {value}"
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
+ ]