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
@@ -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"]