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.
- flock/__init__.py +1 -1
- flock/agent/__init__.py +30 -0
- flock/agent/builder_helpers.py +192 -0
- flock/agent/builder_validator.py +169 -0
- flock/agent/component_lifecycle.py +325 -0
- flock/agent/context_resolver.py +141 -0
- flock/agent/mcp_integration.py +212 -0
- flock/agent/output_processor.py +304 -0
- flock/api/__init__.py +20 -0
- flock/{api_models.py → api/models.py} +0 -2
- flock/{service.py → api/service.py} +3 -3
- flock/cli.py +2 -2
- flock/components/__init__.py +41 -0
- flock/components/agent/__init__.py +22 -0
- flock/{components.py → components/agent/base.py} +4 -3
- flock/{utility/output_utility_component.py → components/agent/output_utility.py} +12 -7
- flock/components/orchestrator/__init__.py +22 -0
- flock/{orchestrator_component.py → components/orchestrator/base.py} +5 -293
- flock/components/orchestrator/circuit_breaker.py +95 -0
- flock/components/orchestrator/collection.py +143 -0
- flock/components/orchestrator/deduplication.py +78 -0
- flock/core/__init__.py +30 -0
- flock/core/agent.py +953 -0
- flock/{artifacts.py → core/artifacts.py} +1 -1
- flock/{context_provider.py → core/context_provider.py} +3 -3
- flock/core/orchestrator.py +1102 -0
- flock/{store.py → core/store.py} +99 -454
- flock/{subscription.py → core/subscription.py} +1 -1
- flock/dashboard/collector.py +5 -5
- flock/dashboard/events.py +1 -1
- flock/dashboard/graph_builder.py +7 -7
- flock/dashboard/routes/__init__.py +21 -0
- flock/dashboard/routes/control.py +327 -0
- flock/dashboard/routes/helpers.py +340 -0
- flock/dashboard/routes/themes.py +76 -0
- flock/dashboard/routes/traces.py +521 -0
- flock/dashboard/routes/websocket.py +108 -0
- flock/dashboard/service.py +43 -1316
- flock/engines/dspy/__init__.py +20 -0
- flock/engines/dspy/artifact_materializer.py +216 -0
- flock/engines/dspy/signature_builder.py +474 -0
- flock/engines/dspy/streaming_executor.py +812 -0
- flock/engines/dspy_engine.py +45 -1330
- flock/engines/examples/simple_batch_engine.py +2 -2
- flock/engines/streaming/__init__.py +3 -0
- flock/engines/streaming/sinks.py +489 -0
- flock/examples.py +7 -7
- flock/logging/logging.py +1 -16
- flock/models/__init__.py +10 -0
- flock/orchestrator/__init__.py +45 -0
- flock/{artifact_collector.py → orchestrator/artifact_collector.py} +3 -3
- flock/orchestrator/artifact_manager.py +168 -0
- flock/{batch_accumulator.py → orchestrator/batch_accumulator.py} +2 -2
- flock/orchestrator/component_runner.py +389 -0
- flock/orchestrator/context_builder.py +167 -0
- flock/{correlation_engine.py → orchestrator/correlation_engine.py} +2 -2
- flock/orchestrator/event_emitter.py +167 -0
- flock/orchestrator/initialization.py +184 -0
- flock/orchestrator/lifecycle_manager.py +226 -0
- flock/orchestrator/mcp_manager.py +202 -0
- flock/orchestrator/scheduler.py +189 -0
- flock/orchestrator/server_manager.py +234 -0
- flock/orchestrator/tracing.py +147 -0
- flock/storage/__init__.py +10 -0
- flock/storage/artifact_aggregator.py +158 -0
- flock/storage/in_memory/__init__.py +6 -0
- flock/storage/in_memory/artifact_filter.py +114 -0
- flock/storage/in_memory/history_aggregator.py +115 -0
- flock/storage/sqlite/__init__.py +10 -0
- flock/storage/sqlite/agent_history_queries.py +154 -0
- flock/storage/sqlite/consumption_loader.py +100 -0
- flock/storage/sqlite/query_builder.py +112 -0
- flock/storage/sqlite/query_params_builder.py +91 -0
- flock/storage/sqlite/schema_manager.py +168 -0
- flock/storage/sqlite/summary_queries.py +194 -0
- flock/utils/__init__.py +14 -0
- flock/utils/async_utils.py +67 -0
- flock/{runtime.py → utils/runtime.py} +3 -3
- flock/utils/time_utils.py +53 -0
- flock/utils/type_resolution.py +38 -0
- flock/{utilities.py → utils/utilities.py} +2 -2
- flock/utils/validation.py +57 -0
- flock/utils/visibility.py +79 -0
- flock/utils/visibility_utils.py +134 -0
- {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/METADATA +19 -5
- {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/RECORD +92 -34
- flock/agent.py +0 -1578
- flock/orchestrator.py +0 -1983
- /flock/{visibility.py → core/visibility.py} +0 -0
- /flock/{system_artifacts.py → models/system_artifacts.py} +0 -0
- /flock/{helper → utils}/cli_helper.py +0 -0
- {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/WHEEL +0 -0
- {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/entry_points.txt +0 -0
- {flock_core-0.5.11.dist-info → flock_core-0.5.21.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""DSPy engine implementation modules.
|
|
2
|
+
|
|
3
|
+
Phase 6: Modularized DSPy engine implementation to improve maintainability.
|
|
4
|
+
|
|
5
|
+
This package contains extracted components from the main DSPy engine:
|
|
6
|
+
- SignatureBuilder: DSPy signature generation with semantic field naming
|
|
7
|
+
- StreamingExecutor: Streaming execution (CLI Rich display + WebSocket-only)
|
|
8
|
+
- ArtifactMaterializer: Output normalization and artifact creation
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from flock.engines.dspy.artifact_materializer import DSPyArtifactMaterializer
|
|
12
|
+
from flock.engines.dspy.signature_builder import DSPySignatureBuilder
|
|
13
|
+
from flock.engines.dspy.streaming_executor import DSPyStreamingExecutor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"DSPyArtifactMaterializer",
|
|
18
|
+
"DSPySignatureBuilder",
|
|
19
|
+
"DSPyStreamingExecutor",
|
|
20
|
+
]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Artifact materialization from DSPy outputs.
|
|
2
|
+
|
|
3
|
+
Phase 6: Extracted from dspy_engine.py to reduce file size and improve modularity.
|
|
4
|
+
|
|
5
|
+
This module handles conversion of DSPy Prediction outputs to Flock artifacts,
|
|
6
|
+
including JSON parsing, normalization, and fan-out support.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from collections.abc import Iterable, Mapping
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel
|
|
16
|
+
|
|
17
|
+
from flock.core.artifacts import Artifact
|
|
18
|
+
from flock.logging.logging import get_logger
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DSPyArtifactMaterializer:
|
|
25
|
+
"""Materializes Flock artifacts from DSPy program outputs.
|
|
26
|
+
|
|
27
|
+
Responsibilities:
|
|
28
|
+
- Normalize DSPy outputs (JSON parsing, BaseModel handling)
|
|
29
|
+
- Select correct output payload from multi-output results
|
|
30
|
+
- Create artifacts with fan-out support (count > 1)
|
|
31
|
+
- Handle validation errors gracefully
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def normalize_output_payload(self, raw: Any) -> dict[str, Any]:
|
|
35
|
+
"""Normalize raw DSPy output to dict format.
|
|
36
|
+
|
|
37
|
+
Handles:
|
|
38
|
+
- BaseModel instances → model_dump()
|
|
39
|
+
- JSON strings → parsed dict
|
|
40
|
+
- DSPy streaming markers like `[[ ## output ## ]]`
|
|
41
|
+
- Markdown fenced code blocks
|
|
42
|
+
- Extracting JSON from text
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
raw: Raw output from DSPy program
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Normalized dict payload
|
|
49
|
+
"""
|
|
50
|
+
if isinstance(raw, BaseModel):
|
|
51
|
+
return raw.model_dump()
|
|
52
|
+
if isinstance(raw, str):
|
|
53
|
+
text = raw.strip()
|
|
54
|
+
candidates: list[str] = []
|
|
55
|
+
|
|
56
|
+
# Primary attempt - full string
|
|
57
|
+
if text:
|
|
58
|
+
candidates.append(text)
|
|
59
|
+
|
|
60
|
+
# Handle DSPy streaming markers like `[[ ## output ## ]]`
|
|
61
|
+
if text.startswith("[[") and "]]" in text:
|
|
62
|
+
_, remainder = text.split("]]", 1)
|
|
63
|
+
remainder = remainder.strip()
|
|
64
|
+
if remainder:
|
|
65
|
+
candidates.append(remainder)
|
|
66
|
+
|
|
67
|
+
# Handle Markdown-style fenced blocks
|
|
68
|
+
if text.startswith("```") and text.endswith("```"):
|
|
69
|
+
fenced = text.strip("`").strip()
|
|
70
|
+
if fenced:
|
|
71
|
+
candidates.append(fenced)
|
|
72
|
+
|
|
73
|
+
# Extract first JSON-looking segment if present
|
|
74
|
+
for opener, closer in (("{", "}"), ("[", "]")):
|
|
75
|
+
start = text.find(opener)
|
|
76
|
+
end = text.rfind(closer)
|
|
77
|
+
if start != -1 and end != -1 and end > start:
|
|
78
|
+
segment = text[start : end + 1].strip()
|
|
79
|
+
if segment:
|
|
80
|
+
candidates.append(segment)
|
|
81
|
+
|
|
82
|
+
seen: set[str] = set()
|
|
83
|
+
for candidate in candidates:
|
|
84
|
+
if candidate in seen:
|
|
85
|
+
continue
|
|
86
|
+
seen.add(candidate)
|
|
87
|
+
try:
|
|
88
|
+
return json.loads(candidate)
|
|
89
|
+
except json.JSONDecodeError:
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
return {"text": text}
|
|
93
|
+
if isinstance(raw, Mapping):
|
|
94
|
+
return dict(raw)
|
|
95
|
+
return {"value": raw}
|
|
96
|
+
|
|
97
|
+
def materialize_artifacts(
|
|
98
|
+
self,
|
|
99
|
+
payload: dict[str, Any],
|
|
100
|
+
outputs: Iterable[Any],
|
|
101
|
+
produced_by: str,
|
|
102
|
+
pre_generated_id: Any = None,
|
|
103
|
+
):
|
|
104
|
+
"""Materialize artifacts from payload, handling fan-out (count > 1).
|
|
105
|
+
|
|
106
|
+
For fan-out outputs (count > 1), splits the list into individual artifacts.
|
|
107
|
+
For single outputs (count = 1), creates one artifact from dict.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
payload: Normalized output dict from DSPy
|
|
111
|
+
outputs: AgentOutput declarations defining what to create
|
|
112
|
+
produced_by: Agent name
|
|
113
|
+
pre_generated_id: Pre-generated ID for streaming (only used for single outputs)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Tuple of (artifacts list, errors list)
|
|
117
|
+
"""
|
|
118
|
+
artifacts: list[Artifact] = []
|
|
119
|
+
errors: list[str] = []
|
|
120
|
+
for output in outputs or []:
|
|
121
|
+
model_cls = output.spec.model
|
|
122
|
+
data = self.select_output_payload(payload, model_cls, output.spec.type_name)
|
|
123
|
+
|
|
124
|
+
# FAN-OUT: If count > 1, data should be a list and we create multiple artifacts
|
|
125
|
+
if output.count > 1:
|
|
126
|
+
if not isinstance(data, list):
|
|
127
|
+
errors.append(
|
|
128
|
+
f"Fan-out expected list for {output.spec.type_name} (count={output.count}), "
|
|
129
|
+
f"got {type(data).__name__}"
|
|
130
|
+
)
|
|
131
|
+
continue
|
|
132
|
+
|
|
133
|
+
# Create one artifact for each item in the list
|
|
134
|
+
for item_data in data:
|
|
135
|
+
try:
|
|
136
|
+
instance = model_cls(**item_data)
|
|
137
|
+
except Exception as exc: # noqa: BLE001 - collect validation errors for logs
|
|
138
|
+
errors.append(f"{output.spec.type_name}: {exc!s}")
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
# Fan-out artifacts auto-generate their IDs (can't reuse pre_generated_id)
|
|
142
|
+
artifact_kwargs = {
|
|
143
|
+
"type": output.spec.type_name,
|
|
144
|
+
"payload": instance.model_dump(),
|
|
145
|
+
"produced_by": produced_by,
|
|
146
|
+
}
|
|
147
|
+
artifacts.append(Artifact(**artifact_kwargs))
|
|
148
|
+
else:
|
|
149
|
+
# SINGLE OUTPUT: Create one artifact from dict
|
|
150
|
+
try:
|
|
151
|
+
instance = model_cls(**data)
|
|
152
|
+
except Exception as exc: # noqa: BLE001 - collect validation errors for logs
|
|
153
|
+
errors.append(str(exc))
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
# Use the pre-generated ID if provided (for streaming), otherwise let Artifact auto-generate
|
|
157
|
+
artifact_kwargs = {
|
|
158
|
+
"type": output.spec.type_name,
|
|
159
|
+
"payload": instance.model_dump(),
|
|
160
|
+
"produced_by": produced_by,
|
|
161
|
+
}
|
|
162
|
+
if pre_generated_id is not None:
|
|
163
|
+
artifact_kwargs["id"] = pre_generated_id
|
|
164
|
+
|
|
165
|
+
artifacts.append(Artifact(**artifact_kwargs))
|
|
166
|
+
return artifacts, errors
|
|
167
|
+
|
|
168
|
+
def select_output_payload(
|
|
169
|
+
self,
|
|
170
|
+
payload: Mapping[str, Any],
|
|
171
|
+
model_cls: type[BaseModel],
|
|
172
|
+
type_name: str,
|
|
173
|
+
) -> dict[str, Any] | list[dict[str, Any]]:
|
|
174
|
+
"""Select the correct output payload from the normalized output dict.
|
|
175
|
+
|
|
176
|
+
Handles both simple type names and fully qualified names (with module prefix).
|
|
177
|
+
Returns either a dict (single output) or list[dict] (fan-out/batch).
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
payload: Normalized output dict
|
|
181
|
+
model_cls: Pydantic model class
|
|
182
|
+
type_name: Type name from OutputSpec
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Either dict (single) or list[dict] (fan-out/batch)
|
|
186
|
+
"""
|
|
187
|
+
candidates = [
|
|
188
|
+
payload.get(type_name), # Try exact type_name (may be "__main__.Movie")
|
|
189
|
+
payload.get(model_cls.__name__), # Try simple class name ("Movie")
|
|
190
|
+
payload.get(model_cls.__name__.lower()), # Try lowercase ("movie")
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
# Extract value based on type
|
|
194
|
+
for candidate in candidates:
|
|
195
|
+
if candidate is not None:
|
|
196
|
+
# Handle lists (fan-out and batching)
|
|
197
|
+
if isinstance(candidate, list):
|
|
198
|
+
# Convert Pydantic instances to dicts
|
|
199
|
+
return [
|
|
200
|
+
item.model_dump() if isinstance(item, BaseModel) else item
|
|
201
|
+
for item in candidate
|
|
202
|
+
]
|
|
203
|
+
# Handle single Pydantic instance
|
|
204
|
+
if isinstance(candidate, BaseModel):
|
|
205
|
+
return candidate.model_dump()
|
|
206
|
+
# Handle dict
|
|
207
|
+
if isinstance(candidate, Mapping):
|
|
208
|
+
return dict(candidate)
|
|
209
|
+
|
|
210
|
+
# Fallback: return entire payload (will likely fail validation)
|
|
211
|
+
if isinstance(payload, Mapping):
|
|
212
|
+
return dict(payload)
|
|
213
|
+
return {}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
__all__ = ["DSPyArtifactMaterializer"]
|