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.
- 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 +283 -0
- flock/{service.py → api/service.py} +121 -63
- 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/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 +44 -1294
- 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 +858 -0
- flock/engines/dspy_engine.py +45 -1330
- flock/engines/examples/simple_batch_engine.py +2 -2
- flock/examples.py +7 -7
- flock/logging/logging.py +1 -16
- flock/models/__init__.py +10 -0
- flock/models/system_artifacts.py +33 -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.10.dist-info → flock_core-0.5.20.dist-info}/METADATA +69 -61
- {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/RECORD +89 -31
- flock/agent.py +0 -1578
- flock/orchestrator.py +0 -1746
- /flock/{visibility.py → core/visibility.py} +0 -0
- /flock/{helper → utils}/cli_helper.py +0 -0
- {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/WHEEL +0 -0
- {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/entry_points.txt +0 -0
- {flock_core-0.5.10.dist-info → flock_core-0.5.20.dist-info}/licenses/LICENSE +0 -0
flock/__init__.py
CHANGED
flock/agent/__init__.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Agent implementation modules.
|
|
2
|
+
|
|
3
|
+
This package contains internal implementation details for the Agent class.
|
|
4
|
+
|
|
5
|
+
Phase 5B Additions:
|
|
6
|
+
- BuilderHelpers: PublishBuilder, RunHandle, Pipeline helper classes
|
|
7
|
+
- BuilderValidator: Validation and normalization logic for AgentBuilder
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from flock.agent.builder_helpers import Pipeline, PublishBuilder, RunHandle
|
|
11
|
+
from flock.agent.builder_validator import BuilderValidator
|
|
12
|
+
from flock.agent.component_lifecycle import ComponentLifecycle
|
|
13
|
+
from flock.agent.context_resolver import ContextResolver
|
|
14
|
+
from flock.agent.mcp_integration import MCPIntegration
|
|
15
|
+
from flock.agent.output_processor import OutputProcessor
|
|
16
|
+
from flock.core.visibility import AgentIdentity
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"AgentIdentity",
|
|
21
|
+
"BuilderHelpers",
|
|
22
|
+
"BuilderValidator",
|
|
23
|
+
"ComponentLifecycle",
|
|
24
|
+
"ContextResolver",
|
|
25
|
+
"MCPIntegration",
|
|
26
|
+
"OutputProcessor",
|
|
27
|
+
"Pipeline",
|
|
28
|
+
"PublishBuilder",
|
|
29
|
+
"RunHandle",
|
|
30
|
+
]
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Helper classes for AgentBuilder fluent API.
|
|
2
|
+
|
|
3
|
+
Phase 5B: Extracted from agent.py to reduce file size and improve modularity.
|
|
4
|
+
|
|
5
|
+
This module contains three helper classes that support the fluent builder pattern:
|
|
6
|
+
- PublishBuilder: Enables .only_for() and .visibility() configuration sugar
|
|
7
|
+
- RunHandle: Represents chained agent execution (agent.run().then().execute())
|
|
8
|
+
- Pipeline: Represents sequential agent pipeline execution
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Sequence
|
|
14
|
+
from typing import TYPE_CHECKING, Any
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel
|
|
17
|
+
|
|
18
|
+
from flock.core.visibility import Visibility, only_for
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from flock.core import Agent
|
|
23
|
+
from flock.core.artifacts import Artifact
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PublishBuilder:
|
|
27
|
+
"""Helper returned by `.publishes(...)` to support `.only_for` sugar.
|
|
28
|
+
|
|
29
|
+
This class enables method chaining after .publishes() calls, allowing
|
|
30
|
+
conditional visibility configuration and delegation back to AgentBuilder.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> agent.publishes(Report).only_for("manager", "analyst")
|
|
34
|
+
>>> agent.publishes(Alert).visibility(PrivateVisibility())
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, parent: Any, outputs: Sequence[Any]) -> None:
|
|
38
|
+
"""Initialize PublishBuilder.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
parent: AgentBuilder instance to return to for chaining
|
|
42
|
+
outputs: List of AgentOutput objects to configure
|
|
43
|
+
"""
|
|
44
|
+
self._parent = parent
|
|
45
|
+
self._outputs = list(outputs)
|
|
46
|
+
|
|
47
|
+
def only_for(self, *agent_names: str) -> Any:
|
|
48
|
+
"""Set visibility to allow only specific agents.
|
|
49
|
+
|
|
50
|
+
Convenience method that creates PrivateVisibility with allowlist.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
*agent_names: Names of agents that can see these outputs
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Parent AgentBuilder for continued method chaining
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
>>> agent.publishes(Report).only_for("manager", "analyst")
|
|
60
|
+
"""
|
|
61
|
+
visibility = only_for(*agent_names)
|
|
62
|
+
for output in self._outputs:
|
|
63
|
+
output.default_visibility = visibility
|
|
64
|
+
return self._parent
|
|
65
|
+
|
|
66
|
+
def visibility(self, value: Visibility) -> Any:
|
|
67
|
+
"""Set explicit visibility for published outputs.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
value: Visibility instance (PublicVisibility, PrivateVisibility, etc.)
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Parent AgentBuilder for continued method chaining
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
>>> agent.publishes(Report).visibility(TenantVisibility())
|
|
77
|
+
"""
|
|
78
|
+
for output in self._outputs:
|
|
79
|
+
output.default_visibility = value
|
|
80
|
+
return self._parent
|
|
81
|
+
|
|
82
|
+
def __getattr__(self, item):
|
|
83
|
+
"""Delegate unknown attributes to parent AgentBuilder.
|
|
84
|
+
|
|
85
|
+
This enables seamless chaining like:
|
|
86
|
+
>>> agent.publishes(Report).only_for("alice").consumes(Task)
|
|
87
|
+
"""
|
|
88
|
+
return getattr(self._parent, item)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class RunHandle:
|
|
92
|
+
"""Represents a chained run starting from a given agent.
|
|
93
|
+
|
|
94
|
+
Enables fluent API for sequential agent execution:
|
|
95
|
+
>>> await agent1.run(input).then(agent2).then(agent3).execute()
|
|
96
|
+
|
|
97
|
+
The chain executes agents in sequence, passing outputs from one to the next.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
def __init__(self, agent: Agent, inputs: list[BaseModel]) -> None:
|
|
101
|
+
"""Initialize RunHandle.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
agent: First agent in the chain
|
|
105
|
+
inputs: Initial inputs to process
|
|
106
|
+
"""
|
|
107
|
+
self.agent = agent
|
|
108
|
+
self.inputs = inputs
|
|
109
|
+
self._chain: list[Agent] = [agent]
|
|
110
|
+
|
|
111
|
+
def then(self, builder: Any) -> RunHandle:
|
|
112
|
+
"""Add another agent to the execution chain.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
builder: AgentBuilder whose agent to add to chain
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
self for continued chaining
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
>>> await agent1.run(task).then(agent2).then(agent3).execute()
|
|
122
|
+
"""
|
|
123
|
+
self._chain.append(builder.agent)
|
|
124
|
+
return self
|
|
125
|
+
|
|
126
|
+
async def execute(self) -> list[Artifact]:
|
|
127
|
+
"""Execute the agent chain sequentially.
|
|
128
|
+
|
|
129
|
+
Runs each agent in order, passing outputs from one as inputs to the next.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Final list of artifacts from the last agent in the chain
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> results = await agent1.run(input).then(agent2).execute()
|
|
136
|
+
"""
|
|
137
|
+
orchestrator = self.agent._orchestrator
|
|
138
|
+
artifacts = await orchestrator.direct_invoke(self.agent, self.inputs)
|
|
139
|
+
for agent in self._chain[1:]:
|
|
140
|
+
artifacts = await orchestrator.direct_invoke(agent, artifacts)
|
|
141
|
+
return artifacts
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class Pipeline:
|
|
145
|
+
"""Pipeline of agents executed in sequence.
|
|
146
|
+
|
|
147
|
+
Alternative to RunHandle for building multi-agent pipelines:
|
|
148
|
+
>>> pipeline = Pipeline([agent1, agent2, agent3])
|
|
149
|
+
>>> results = await pipeline.execute()
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def __init__(self, builders: Sequence[Any]) -> None:
|
|
153
|
+
"""Initialize Pipeline.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
builders: Sequence of AgentBuilder instances
|
|
157
|
+
"""
|
|
158
|
+
self.builders = list(builders)
|
|
159
|
+
|
|
160
|
+
def then(self, builder: Any) -> Pipeline:
|
|
161
|
+
"""Add another agent to the pipeline.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
builder: AgentBuilder to add
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
self for continued chaining
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
>>> pipeline = Pipeline([agent1]).then(agent2).then(agent3)
|
|
171
|
+
"""
|
|
172
|
+
self.builders.append(builder)
|
|
173
|
+
return self
|
|
174
|
+
|
|
175
|
+
async def execute(self) -> list[Artifact]:
|
|
176
|
+
"""Execute all agents in the pipeline sequentially.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Final list of artifacts from the last agent
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> results = await Pipeline([agent1, agent2, agent3]).execute()
|
|
183
|
+
"""
|
|
184
|
+
orchestrator = self.builders[0].agent._orchestrator
|
|
185
|
+
artifacts: list[Artifact] = []
|
|
186
|
+
for builder in self.builders:
|
|
187
|
+
inputs = artifacts if artifacts else []
|
|
188
|
+
artifacts = await orchestrator.direct_invoke(builder.agent, inputs)
|
|
189
|
+
return artifacts
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
__all__ = ["Pipeline", "PublishBuilder", "RunHandle"]
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Validation and normalization logic for AgentBuilder fluent API.
|
|
2
|
+
|
|
3
|
+
Phase 5B: Extracted from agent.py to reduce file size and improve modularity.
|
|
4
|
+
|
|
5
|
+
This module contains validation methods that warn about common configuration issues
|
|
6
|
+
and normalization methods that convert dict-based specs to proper dataclass instances.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from datetime import timedelta
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
from flock.core.subscription import BatchSpec, JoinSpec
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from flock.core import Agent
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BuilderValidator:
|
|
22
|
+
"""Validation and normalization logic for AgentBuilder configuration.
|
|
23
|
+
|
|
24
|
+
This class provides static methods for:
|
|
25
|
+
- Warning about feedback loop risks (self-trigger detection)
|
|
26
|
+
- Warning about excessive best_of or max_concurrency values
|
|
27
|
+
- Normalizing dict-based JoinSpec and BatchSpec to proper instances
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def validate_self_trigger_risk(agent: Agent) -> None:
|
|
32
|
+
"""T074: Warn if agent consumes and publishes same type (feedback loop risk).
|
|
33
|
+
|
|
34
|
+
Detects when an agent subscribes to and publishes the same artifact type,
|
|
35
|
+
which could create infinite feedback loops if not configured carefully.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
agent: Agent instance to validate
|
|
39
|
+
"""
|
|
40
|
+
from flock.logging.logging import get_logger
|
|
41
|
+
|
|
42
|
+
logger = get_logger(__name__)
|
|
43
|
+
|
|
44
|
+
# Get types agent consumes
|
|
45
|
+
consuming_types = set()
|
|
46
|
+
for sub in agent.subscriptions:
|
|
47
|
+
consuming_types.update(sub.type_names)
|
|
48
|
+
|
|
49
|
+
# Get types agent publishes
|
|
50
|
+
publishing_types = {
|
|
51
|
+
output.spec.type_name
|
|
52
|
+
for group in agent.output_groups
|
|
53
|
+
for output in group.outputs
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Check for overlap
|
|
57
|
+
overlap = consuming_types.intersection(publishing_types)
|
|
58
|
+
if overlap and agent.prevent_self_trigger:
|
|
59
|
+
logger.warning(
|
|
60
|
+
f"Agent '{agent.name}' consumes and publishes {overlap}. "
|
|
61
|
+
f"Feedback loop risk detected. Agent has prevent_self_trigger=True (safe), "
|
|
62
|
+
f"but consider adding filtering: .consumes(Type, where=lambda x: ...) "
|
|
63
|
+
f"or use .prevent_self_trigger(False) for intentional feedback."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def validate_best_of(agent_name: str, n: int) -> None:
|
|
68
|
+
"""T074: Warn if best_of value is excessively high.
|
|
69
|
+
|
|
70
|
+
High best_of values (>100) dramatically increase cost and latency
|
|
71
|
+
by running the same evaluation multiple times.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
agent_name: Name of agent being validated
|
|
75
|
+
n: best_of value to validate
|
|
76
|
+
"""
|
|
77
|
+
from flock.logging.logging import get_logger
|
|
78
|
+
|
|
79
|
+
logger = get_logger(__name__)
|
|
80
|
+
|
|
81
|
+
if n > 100:
|
|
82
|
+
logger.warning(
|
|
83
|
+
f"Agent '{agent_name}' has best_of({n}) which is very high. "
|
|
84
|
+
f"Typical values are 3-10. High values increase cost and latency. "
|
|
85
|
+
f"Consider reducing unless you have specific requirements."
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def validate_concurrency(agent_name: str, n: int) -> None:
|
|
90
|
+
"""T074: Warn if max_concurrency is excessively high.
|
|
91
|
+
|
|
92
|
+
Excessive concurrency (>1000) may overwhelm resources and cause
|
|
93
|
+
rate limiting, memory issues, or performance degradation.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
agent_name: Name of agent being validated
|
|
97
|
+
n: max_concurrency value to validate
|
|
98
|
+
"""
|
|
99
|
+
from flock.logging.logging import get_logger
|
|
100
|
+
|
|
101
|
+
logger = get_logger(__name__)
|
|
102
|
+
|
|
103
|
+
if n > 1000:
|
|
104
|
+
logger.warning(
|
|
105
|
+
f"Agent '{agent_name}' has max_concurrency({n}) which is very high. "
|
|
106
|
+
f"Typical values are 1-50. Excessive concurrency may cause resource issues. "
|
|
107
|
+
f"Consider reducing unless you have specific infrastructure."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def normalize_join(value: dict | JoinSpec | None) -> JoinSpec | None:
|
|
112
|
+
"""Normalize dict-based JoinSpec to proper JoinSpec instance.
|
|
113
|
+
|
|
114
|
+
Converts dict syntax to JoinSpec dataclass for backward compatibility:
|
|
115
|
+
>>> normalize_join({"by": "user_id", "within": 60.0})
|
|
116
|
+
JoinSpec(by="user_id", within=timedelta(seconds=60.0))
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
value: Either a JoinSpec instance, a dict with join config, or None
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
JoinSpec instance or None
|
|
123
|
+
"""
|
|
124
|
+
if value is None or isinstance(value, JoinSpec):
|
|
125
|
+
return value
|
|
126
|
+
|
|
127
|
+
# Phase 2: New JoinSpec API with 'by' and 'within' (time OR count)
|
|
128
|
+
within_value = value.get("within")
|
|
129
|
+
if isinstance(within_value, (int, float)):
|
|
130
|
+
# Count window or seconds as float - keep as is
|
|
131
|
+
within = (
|
|
132
|
+
int(within_value)
|
|
133
|
+
if isinstance(within_value, int)
|
|
134
|
+
else timedelta(seconds=within_value)
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
# Default to 1 minute time window
|
|
138
|
+
within = timedelta(minutes=1)
|
|
139
|
+
|
|
140
|
+
return JoinSpec(
|
|
141
|
+
by=value["by"], # Required
|
|
142
|
+
within=within,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def normalize_batch(value: dict | BatchSpec | None) -> BatchSpec | None:
|
|
147
|
+
"""Normalize dict-based BatchSpec to proper BatchSpec instance.
|
|
148
|
+
|
|
149
|
+
Converts dict syntax to BatchSpec dataclass for backward compatibility:
|
|
150
|
+
>>> normalize_batch({"size": 10, "within": 5.0})
|
|
151
|
+
BatchSpec(size=10, within=5.0, by=None)
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
value: Either a BatchSpec instance, a dict with batch config, or None
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
BatchSpec instance or None
|
|
158
|
+
"""
|
|
159
|
+
if value is None or isinstance(value, BatchSpec):
|
|
160
|
+
return value
|
|
161
|
+
|
|
162
|
+
return BatchSpec(
|
|
163
|
+
size=int(value.get("size", 1)),
|
|
164
|
+
within=float(value.get("within", 0.0)),
|
|
165
|
+
by=value.get("by"),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
__all__ = ["BuilderValidator"]
|