flock-core 0.5.0b2__py3-none-any.whl → 0.5.0b5__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/cli/execute_flock.py +1 -1
- flock/cli/manage_agents.py +3 -3
- flock/components/evaluation/declarative_evaluation_component.py +10 -10
- flock/components/routing/conditional_routing_component.py +7 -6
- flock/components/routing/default_routing_component.py +3 -3
- flock/components/routing/llm_routing_component.py +24 -26
- flock/components/utility/memory_utility_component.py +3 -3
- flock/components/utility/metrics_utility_component.py +11 -11
- flock/components/utility/output_utility_component.py +11 -9
- flock/core/__init__.py +24 -10
- flock/core/agent/flock_agent_components.py +16 -16
- flock/core/agent/flock_agent_integration.py +88 -29
- flock/core/agent/flock_agent_serialization.py +23 -20
- flock/core/api/endpoints.py +2 -2
- flock/core/api/service.py +2 -2
- flock/core/component/__init__.py +7 -7
- flock/core/component/{evaluation_component_base.py → evaluation_component.py} +2 -2
- flock/core/component/{routing_component_base.py → routing_component.py} +3 -4
- flock/core/component/{utility_component_base.py → utility_component.py} +3 -3
- flock/core/evaluation/utils.py +2 -1
- flock/core/execution/batch_executor.py +1 -1
- flock/core/execution/evaluation_executor.py +1 -1
- flock/core/execution/opik_executor.py +1 -1
- flock/core/flock.py +12 -12
- flock/core/flock_agent.py +68 -38
- flock/core/flock_factory.py +21 -18
- flock/core/flock_scheduler.py +1 -1
- flock/core/flock_server_manager.py +8 -8
- flock/core/mcp/flock_mcp_server.py +11 -11
- flock/core/mcp/{flock_mcp_tool_base.py → flock_mcp_tool.py} +2 -2
- flock/core/mcp/mcp_client.py +9 -9
- flock/core/mcp/mcp_client_manager.py +9 -9
- flock/core/mcp/mcp_config.py +24 -24
- flock/core/orchestration/flock_execution.py +13 -13
- flock/core/orchestration/flock_initialization.py +6 -6
- flock/core/orchestration/flock_server_manager.py +8 -6
- flock/core/registry/__init__.py +16 -10
- flock/core/registry/registry_hub.py +7 -4
- flock/core/registry/server_registry.py +6 -6
- flock/core/serialization/flock_serializer.py +3 -2
- flock/core/util/hydrator.py +1 -1
- flock/mcp/servers/sse/flock_sse_server.py +10 -10
- flock/mcp/servers/stdio/flock_stdio_server.py +10 -10
- flock/mcp/servers/streamable_http/flock_streamable_http_server.py +10 -10
- flock/mcp/servers/websockets/flock_websocket_server.py +10 -10
- flock/webapp/app/chat.py +1 -1
- flock/webapp/app/services/flock_service.py +1 -1
- flock/workflow/activities.py +10 -10
- {flock_core-0.5.0b2.dist-info → flock_core-0.5.0b5.dist-info}/METADATA +1 -1
- {flock_core-0.5.0b2.dist-info → flock_core-0.5.0b5.dist-info}/RECORD +53 -55
- flock/core/flock_registry.py.backup +0 -688
- flock/workflow/activities_unified.py +0 -230
- {flock_core-0.5.0b2.dist-info → flock_core-0.5.0b5.dist-info}/WHEEL +0 -0
- {flock_core-0.5.0b2.dist-info → flock_core-0.5.0b5.dist-info}/entry_points.txt +0 -0
- {flock_core-0.5.0b2.dist-info → flock_core-0.5.0b5.dist-info}/licenses/LICENSE +0 -0
flock/core/flock_agent.py
CHANGED
|
@@ -12,14 +12,14 @@ from flock.core.agent.flock_agent_execution import FlockAgentExecution
|
|
|
12
12
|
from flock.core.agent.flock_agent_integration import FlockAgentIntegration
|
|
13
13
|
from flock.core.agent.flock_agent_serialization import FlockAgentSerialization
|
|
14
14
|
from flock.core.component.agent_component_base import AgentComponent
|
|
15
|
-
from flock.core.component.
|
|
16
|
-
|
|
15
|
+
from flock.core.component.evaluation_component import (
|
|
16
|
+
EvaluationComponent,
|
|
17
17
|
)
|
|
18
|
-
from flock.core.component.
|
|
18
|
+
from flock.core.component.routing_component import RoutingComponent
|
|
19
19
|
from flock.core.config.flock_agent_config import FlockAgentConfig
|
|
20
20
|
from flock.core.context.context import FlockContext
|
|
21
21
|
from flock.core.logging.logging import get_logger
|
|
22
|
-
from flock.core.mcp.flock_mcp_server import
|
|
22
|
+
from flock.core.mcp.flock_mcp_server import FlockMCPServer
|
|
23
23
|
|
|
24
24
|
# Mixins and Serialization components
|
|
25
25
|
from flock.core.mixin.dspy_integration import DSPyIntegrationMixin
|
|
@@ -30,14 +30,8 @@ logger = get_logger("agent.unified")
|
|
|
30
30
|
|
|
31
31
|
T = TypeVar("T", bound="FlockAgent")
|
|
32
32
|
|
|
33
|
-
SignatureType = (
|
|
34
|
-
str
|
|
35
|
-
| Callable[..., str]
|
|
36
|
-
| type[BaseModel]
|
|
37
|
-
| Callable[..., type[BaseModel]]
|
|
38
|
-
| None
|
|
39
|
-
)
|
|
40
33
|
|
|
34
|
+
DynamicStr = str | Callable[[FlockContext], str]
|
|
41
35
|
|
|
42
36
|
class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
|
|
43
37
|
"""Unified FlockAgent using the new component architecture.
|
|
@@ -63,23 +57,29 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
|
|
|
63
57
|
None,
|
|
64
58
|
description="The model identifier to use (e.g., 'openai/gpt-4o'). If None, uses Flock's default.",
|
|
65
59
|
)
|
|
66
|
-
|
|
67
|
-
"",
|
|
60
|
+
description_spec: DynamicStr | None = Field(
|
|
61
|
+
default="",
|
|
62
|
+
alias="description",
|
|
63
|
+
validation_alias="description",
|
|
68
64
|
description="A human-readable description or a callable returning one.",
|
|
69
65
|
)
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
input_spec: DynamicStr | None = Field(
|
|
67
|
+
default="",
|
|
68
|
+
alias="input",
|
|
69
|
+
validation_alias="input",
|
|
72
70
|
description="Signature for input keys. Supports type hints (:) and descriptions (|).",
|
|
73
71
|
)
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
output_spec: DynamicStr | None = Field(
|
|
73
|
+
default="",
|
|
74
|
+
alias="output",
|
|
75
|
+
validation_alias="output",
|
|
76
76
|
description="Signature for output keys. Supports type hints (:) and descriptions (|).",
|
|
77
77
|
)
|
|
78
78
|
tools: list[Callable[..., Any]] | None = Field(
|
|
79
79
|
default=None,
|
|
80
80
|
description="List of callable tools the agent can use. These must be registered.",
|
|
81
81
|
)
|
|
82
|
-
servers: list[str |
|
|
82
|
+
servers: list[str | FlockMCPServer] | None = Field(
|
|
83
83
|
default=None,
|
|
84
84
|
description="List of MCP Servers the agent can use to enhance its capabilities.",
|
|
85
85
|
)
|
|
@@ -91,9 +91,10 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
|
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
# --- EXPLICIT WORKFLOW STATE ---
|
|
94
|
-
|
|
94
|
+
next_agent_spec: DynamicStr | None = Field(
|
|
95
95
|
default=None,
|
|
96
|
-
|
|
96
|
+
alias="next_agent",
|
|
97
|
+
validation_alias="next_agent",
|
|
97
98
|
description="Next agent in workflow - set by user or routing components.",
|
|
98
99
|
)
|
|
99
100
|
|
|
@@ -118,14 +119,14 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
|
|
|
118
119
|
self,
|
|
119
120
|
name: str,
|
|
120
121
|
model: str | None = None,
|
|
121
|
-
description:
|
|
122
|
-
input:
|
|
123
|
-
output:
|
|
122
|
+
description: DynamicStr | None = None,
|
|
123
|
+
input: DynamicStr | None = None,
|
|
124
|
+
output: DynamicStr | None = None,
|
|
124
125
|
tools: list[Callable[..., Any]] | None = None,
|
|
125
|
-
servers: list[str |
|
|
126
|
+
servers: list[str | FlockMCPServer] | None = None,
|
|
126
127
|
components: list[AgentComponent] | None = None,
|
|
127
128
|
config: FlockAgentConfig | None = None,
|
|
128
|
-
next_agent:
|
|
129
|
+
next_agent: DynamicStr | None = None,
|
|
129
130
|
temporal_activity_config: TemporalActivityConfig | None = None,
|
|
130
131
|
):
|
|
131
132
|
"""Initialize the unified FlockAgent with components and configuration."""
|
|
@@ -155,12 +156,12 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
|
|
|
155
156
|
# These provide familiar access patterns while using the unified model
|
|
156
157
|
|
|
157
158
|
@property
|
|
158
|
-
def evaluator(self) ->
|
|
159
|
+
def evaluator(self) -> EvaluationComponent | None:
|
|
159
160
|
"""Get the primary evaluation component for this agent."""
|
|
160
161
|
return self._components.get_primary_evaluator()
|
|
161
162
|
|
|
162
163
|
@property
|
|
163
|
-
def router(self) ->
|
|
164
|
+
def router(self) -> RoutingComponent | None:
|
|
164
165
|
"""Get the primary routing component for this agent."""
|
|
165
166
|
return self._components.get_primary_router()
|
|
166
167
|
|
|
@@ -270,22 +271,51 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
|
|
|
270
271
|
f"Agent '{self.name}' has no evaluator to set model for."
|
|
271
272
|
)
|
|
272
273
|
|
|
273
|
-
def resolve_callables(self, context: FlockContext | None = None) -> None:
|
|
274
|
-
"""Resolves callable fields (description, input, output) using context."""
|
|
275
|
-
self.context = context or self.context
|
|
276
|
-
return self._integration.resolve_callables(self.context)
|
|
277
|
-
|
|
278
274
|
@property
|
|
279
|
-
def
|
|
280
|
-
"""Returns the resolved agent description.
|
|
281
|
-
If the description is a callable, it attempts to call it.
|
|
282
|
-
Returns None if the description is None or a callable that fails.
|
|
283
|
-
"""
|
|
275
|
+
def description(self) -> str | None:
|
|
276
|
+
"""Returns the resolved agent description."""
|
|
284
277
|
return self._integration.resolve_description(self.context)
|
|
285
278
|
|
|
279
|
+
@property
|
|
280
|
+
def input(self) -> str | None:
|
|
281
|
+
"""Returns the resolved agent input."""
|
|
282
|
+
return self._integration.resolve_input(self.context)
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def output(self) -> str | None:
|
|
286
|
+
"""Returns the resolved agent output."""
|
|
287
|
+
return self._integration.resolve_output(self.context)
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def next_agent(self) -> str | None:
|
|
291
|
+
"""Returns the resolved agent next agent."""
|
|
292
|
+
return self._integration.resolve_next_agent(self.context)
|
|
293
|
+
|
|
294
|
+
@description.setter
|
|
295
|
+
def description(self, value: DynamicStr) -> None:
|
|
296
|
+
self.description_spec = value
|
|
297
|
+
|
|
298
|
+
@input.setter
|
|
299
|
+
def input(self, value: DynamicStr) -> None:
|
|
300
|
+
self.input_spec = value
|
|
301
|
+
|
|
302
|
+
@output.setter
|
|
303
|
+
def output(self, value: DynamicStr) -> None:
|
|
304
|
+
self.output_spec = value
|
|
305
|
+
|
|
306
|
+
@next_agent.setter
|
|
307
|
+
def next_agent(self, value: DynamicStr) -> None:
|
|
308
|
+
self.next_agent_spec = value
|
|
309
|
+
|
|
286
310
|
def _save_output(self, agent_name: str, result: dict[str, Any]) -> None:
|
|
287
311
|
"""Save output to file if configured (delegated to serialization)."""
|
|
288
312
|
return self._serialization._save_output(agent_name, result)
|
|
289
313
|
|
|
290
314
|
# --- Pydantic v2 Configuration ---
|
|
291
|
-
model_config = {
|
|
315
|
+
model_config = {
|
|
316
|
+
"arbitrary_types_allowed": True,
|
|
317
|
+
"populate_by_name": True,
|
|
318
|
+
# "json_encoders": {
|
|
319
|
+
# Callable: lambda f: f"{f.__module__}.{f.__qualname__}",
|
|
320
|
+
# },
|
|
321
|
+
}
|
flock/core/flock_factory.py
CHANGED
|
@@ -16,13 +16,13 @@ from flock.components.utility.metrics_utility_component import (
|
|
|
16
16
|
# New unified components imported locally to avoid circular imports
|
|
17
17
|
from flock.core.config.flock_agent_config import FlockAgentConfig
|
|
18
18
|
from flock.core.config.scheduled_agent_config import ScheduledAgentConfig
|
|
19
|
-
from flock.core.flock_agent import
|
|
19
|
+
from flock.core.flock_agent import DynamicStr, FlockAgent
|
|
20
20
|
from flock.core.logging.formatters.themes import OutputTheme
|
|
21
|
-
from flock.core.mcp.flock_mcp_server import
|
|
21
|
+
from flock.core.mcp.flock_mcp_server import FlockMCPServer
|
|
22
22
|
from flock.core.mcp.mcp_config import (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
FlockMCPCachingConfiguration,
|
|
24
|
+
FlockMCPCallbackConfiguration,
|
|
25
|
+
FlockMCPFeatureConfiguration,
|
|
26
26
|
)
|
|
27
27
|
from flock.core.mcp.types.types import (
|
|
28
28
|
FlockListRootsMCPCallback,
|
|
@@ -207,7 +207,7 @@ class FlockFactory:
|
|
|
207
207
|
tool_result_cache_ttl=100,
|
|
208
208
|
description: str | Callable[..., str] | None = None,
|
|
209
209
|
alert_latency_threshold_ms: int = 30000,
|
|
210
|
-
) ->
|
|
210
|
+
) -> FlockMCPServer:
|
|
211
211
|
"""Create a default MCP Server with common modules.
|
|
212
212
|
|
|
213
213
|
Allows for creating one of the three default-implementations provided
|
|
@@ -246,19 +246,19 @@ class FlockFactory:
|
|
|
246
246
|
continue # ignore
|
|
247
247
|
|
|
248
248
|
# build generic configs
|
|
249
|
-
feature_config =
|
|
249
|
+
feature_config = FlockMCPFeatureConfiguration(
|
|
250
250
|
roots_enabled=enable_roots_feature,
|
|
251
251
|
tools_enabled=enable_tools_feature,
|
|
252
252
|
prompts_enabled=enable_prompts_feature,
|
|
253
253
|
sampling_enabled=enable_sampling_feature,
|
|
254
254
|
)
|
|
255
|
-
callback_config =
|
|
255
|
+
callback_config = FlockMCPCallbackConfiguration(
|
|
256
256
|
sampling_callback=sampling_callback,
|
|
257
257
|
list_roots_callback=list_roots_callback,
|
|
258
258
|
logging_callback=logging_callback,
|
|
259
259
|
message_handler=message_handler,
|
|
260
260
|
)
|
|
261
|
-
caching_config =
|
|
261
|
+
caching_config = FlockMCPCachingConfiguration(
|
|
262
262
|
tool_cache_max_size=tool_cache_size,
|
|
263
263
|
tool_cache_max_ttl=tool_cache_ttl,
|
|
264
264
|
resource_contents_cache_max_size=resource_contents_cache_size,
|
|
@@ -388,12 +388,12 @@ class FlockFactory:
|
|
|
388
388
|
@staticmethod
|
|
389
389
|
def create_default_agent(
|
|
390
390
|
name: str,
|
|
391
|
-
description:
|
|
391
|
+
description: DynamicStr| None = None,
|
|
392
392
|
model: str | Callable[..., str] | None = None,
|
|
393
|
-
input:
|
|
394
|
-
output:
|
|
393
|
+
input: DynamicStr = None,
|
|
394
|
+
output: DynamicStr = None,
|
|
395
395
|
tools: list[Callable[..., Any] | Any] | None = None,
|
|
396
|
-
servers: list[str |
|
|
396
|
+
servers: list[str | FlockMCPServer] | None = None,
|
|
397
397
|
use_cache: bool = True,
|
|
398
398
|
enable_rich_tables: bool = False,
|
|
399
399
|
output_theme: OutputTheme = OutputTheme.abernathy,
|
|
@@ -408,6 +408,7 @@ class FlockFactory:
|
|
|
408
408
|
write_to_file: bool = False,
|
|
409
409
|
stream: bool = False,
|
|
410
410
|
include_thought_process: bool = False,
|
|
411
|
+
next_agent: DynamicStr | None = None,
|
|
411
412
|
temporal_activity_config: TemporalActivityConfig | None = None,
|
|
412
413
|
) -> FlockAgent:
|
|
413
414
|
"""Creates a default FlockAgent using unified component architecture.
|
|
@@ -479,7 +480,7 @@ class FlockFactory:
|
|
|
479
480
|
components=[evaluator, output_component, metrics_component],
|
|
480
481
|
config=FlockAgentConfig(write_to_file=write_to_file,
|
|
481
482
|
wait_for_input=wait_for_input),
|
|
482
|
-
next_agent=
|
|
483
|
+
next_agent=next_agent,
|
|
483
484
|
temporal_activity_config=temporal_activity_config,
|
|
484
485
|
)
|
|
485
486
|
|
|
@@ -489,14 +490,15 @@ class FlockFactory:
|
|
|
489
490
|
def create_scheduled_agent(
|
|
490
491
|
name: str,
|
|
491
492
|
schedule_expression: str, # e.g., "every 1h", "0 0 * * *"
|
|
492
|
-
description:
|
|
493
|
-
model: str |
|
|
494
|
-
output:
|
|
493
|
+
description: DynamicStr | None = None,
|
|
494
|
+
model: str | None = None,
|
|
495
|
+
output: DynamicStr | None = None, # Input might be implicit or none
|
|
495
496
|
tools: list[Callable[..., Any] | Any] | None = None,
|
|
496
|
-
servers: list[str |
|
|
497
|
+
servers: list[str | FlockMCPServer] | None = None,
|
|
497
498
|
use_cache: bool = False, # Whether to cache results
|
|
498
499
|
temperature: float = 0.7, # Temperature for model responses
|
|
499
500
|
# ... other common agent params from create_default_agent ...
|
|
501
|
+
next_agent: DynamicStr | None = None,
|
|
500
502
|
temporal_activity_config: TemporalActivityConfig
|
|
501
503
|
| None = None, # If you want scheduled tasks to be Temporal activities
|
|
502
504
|
**kwargs, # Forward other standard agent params
|
|
@@ -522,6 +524,7 @@ class FlockFactory:
|
|
|
522
524
|
temporal_activity_config=temporal_activity_config,
|
|
523
525
|
use_cache=use_cache,
|
|
524
526
|
temperature=temperature,
|
|
527
|
+
next_agent=next_agent,
|
|
525
528
|
**kwargs,
|
|
526
529
|
)
|
|
527
530
|
)
|
flock/core/flock_scheduler.py
CHANGED
|
@@ -76,7 +76,7 @@ class FlockScheduler:
|
|
|
76
76
|
logger.info(f"Triggering scheduled agent '{agent.name}' at {trigger_time.isoformat()}")
|
|
77
77
|
try:
|
|
78
78
|
# Input for a scheduled agent could include the trigger time
|
|
79
|
-
await self.flock.run_async(
|
|
79
|
+
await self.flock.run_async(agent=agent.name, input={"trigger_time": trigger_time})
|
|
80
80
|
logger.info(f"Scheduled agent '{agent.name}' finished successfully.")
|
|
81
81
|
except Exception as e:
|
|
82
82
|
logger.error(f"Error running scheduled agent '{agent.name}': {e}\n{traceback.format_exc()}")
|
|
@@ -6,13 +6,13 @@ from contextlib import AsyncExitStack
|
|
|
6
6
|
from anyio import Lock
|
|
7
7
|
from pydantic import BaseModel, ConfigDict, Field
|
|
8
8
|
|
|
9
|
-
from flock.core.mcp.flock_mcp_server import
|
|
9
|
+
from flock.core.mcp.flock_mcp_server import FlockMCPServer
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class FlockServerManager(BaseModel):
|
|
13
13
|
"""Async-context-manager to start/stop a set of Flock MCP servers."""
|
|
14
14
|
|
|
15
|
-
servers: list[
|
|
15
|
+
servers: list[FlockMCPServer] | None = Field(
|
|
16
16
|
..., exclude=True, description="The servers to manage."
|
|
17
17
|
)
|
|
18
18
|
|
|
@@ -32,7 +32,7 @@ class FlockServerManager(BaseModel):
|
|
|
32
32
|
|
|
33
33
|
def __init__(
|
|
34
34
|
self,
|
|
35
|
-
servers: list[
|
|
35
|
+
servers: list[FlockMCPServer] | None = None,
|
|
36
36
|
stack: AsyncExitStack | None = None,
|
|
37
37
|
lock: asyncio.Lock | None = None,
|
|
38
38
|
) -> None:
|
|
@@ -43,7 +43,7 @@ class FlockServerManager(BaseModel):
|
|
|
43
43
|
lock=lock,
|
|
44
44
|
)
|
|
45
45
|
|
|
46
|
-
def add_server_sync(self, server:
|
|
46
|
+
def add_server_sync(self, server: FlockMCPServer) -> None:
|
|
47
47
|
"""Add a server to be managed by the ServerManager.
|
|
48
48
|
|
|
49
49
|
Note:
|
|
@@ -57,7 +57,7 @@ class FlockServerManager(BaseModel):
|
|
|
57
57
|
|
|
58
58
|
self.servers.append(server)
|
|
59
59
|
|
|
60
|
-
def remove_server_sync(self, server:
|
|
60
|
+
def remove_server_sync(self, server: FlockMCPServer) -> None:
|
|
61
61
|
"""Remove a server from the list of managed servers.
|
|
62
62
|
|
|
63
63
|
Note:
|
|
@@ -71,7 +71,7 @@ class FlockServerManager(BaseModel):
|
|
|
71
71
|
|
|
72
72
|
# -- For future use: Allow adding and removal of servers during runtime ---
|
|
73
73
|
async def add_server_during_runtime(
|
|
74
|
-
self, server:
|
|
74
|
+
self, server: FlockMCPServer
|
|
75
75
|
) -> None:
|
|
76
76
|
"""Add a server to the manager and, if already running, start it immediately."""
|
|
77
77
|
if self.lock is None:
|
|
@@ -88,13 +88,13 @@ class FlockServerManager(BaseModel):
|
|
|
88
88
|
await self.stack.enter_async_context(server)
|
|
89
89
|
|
|
90
90
|
async def remove_server_during_runtime(
|
|
91
|
-
self, server:
|
|
91
|
+
self, server: FlockMCPServer
|
|
92
92
|
) -> None:
|
|
93
93
|
"""Tear down and remove a server from the manager at runtime."""
|
|
94
94
|
if self.lock is None:
|
|
95
95
|
self.lock = asyncio.Lock()
|
|
96
96
|
|
|
97
|
-
retrieved_server:
|
|
97
|
+
retrieved_server: FlockMCPServer | None = None
|
|
98
98
|
|
|
99
99
|
async with self.lock:
|
|
100
100
|
if not self.servers or server not in self.servers:
|
|
@@ -17,9 +17,9 @@ from pydantic import (
|
|
|
17
17
|
|
|
18
18
|
from flock.core.component.agent_component_base import AgentComponent
|
|
19
19
|
from flock.core.logging.logging import get_logger
|
|
20
|
-
from flock.core.mcp.
|
|
21
|
-
from flock.core.mcp.mcp_client_manager import
|
|
22
|
-
from flock.core.mcp.mcp_config import
|
|
20
|
+
from flock.core.mcp.flock_mcp_tool import FlockMCPTool
|
|
21
|
+
from flock.core.mcp.mcp_client_manager import FlockMCPClientManager
|
|
22
|
+
from flock.core.mcp.mcp_config import FlockMCPConfiguration
|
|
23
23
|
from flock.core.serialization.serializable import Serializable
|
|
24
24
|
from flock.core.serialization.serialization_utils import (
|
|
25
25
|
deserialize_component,
|
|
@@ -28,7 +28,7 @@ from flock.core.serialization.serialization_utils import (
|
|
|
28
28
|
|
|
29
29
|
logger = get_logger("mcp.server")
|
|
30
30
|
tracer = trace.get_tracer(__name__)
|
|
31
|
-
T = TypeVar("T", bound="
|
|
31
|
+
T = TypeVar("T", bound="FlockMCPServer")
|
|
32
32
|
|
|
33
33
|
LoggingLevel = Literal[
|
|
34
34
|
"debug",
|
|
@@ -42,7 +42,7 @@ LoggingLevel = Literal[
|
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
class
|
|
45
|
+
class FlockMCPServer(BaseModel, Serializable, ABC):
|
|
46
46
|
"""Base class for all Flock MCP Server Types.
|
|
47
47
|
|
|
48
48
|
Servers serve as an abstraction-layer between the underlying MCPClientSession
|
|
@@ -64,7 +64,7 @@ class FlockMCPServerBase(BaseModel, Serializable, ABC):
|
|
|
64
64
|
2. Using FlockMCPServerConfig.with_fields() to create a config class.
|
|
65
65
|
"""
|
|
66
66
|
|
|
67
|
-
config:
|
|
67
|
+
config: FlockMCPConfiguration = Field(
|
|
68
68
|
..., description="Config for clients connecting to the server."
|
|
69
69
|
)
|
|
70
70
|
|
|
@@ -82,7 +82,7 @@ class FlockMCPServerBase(BaseModel, Serializable, ABC):
|
|
|
82
82
|
# --- Underlying ConnectionManager ---
|
|
83
83
|
# (Manages a pool of ClientConnections and does the actual talking to the MCP Server)
|
|
84
84
|
# (Excluded from Serialization)
|
|
85
|
-
client_manager:
|
|
85
|
+
client_manager: FlockMCPClientManager | None = Field(
|
|
86
86
|
default=None,
|
|
87
87
|
exclude=True,
|
|
88
88
|
description="Underlying Connection Manager. Handles the actual underlying connections to the server.",
|
|
@@ -134,7 +134,7 @@ class FlockMCPServerBase(BaseModel, Serializable, ABC):
|
|
|
134
134
|
return [c for c in self.components.values() if c.config.enabled]
|
|
135
135
|
|
|
136
136
|
@abstractmethod
|
|
137
|
-
async def initialize(self) ->
|
|
137
|
+
async def initialize(self) -> FlockMCPClientManager:
|
|
138
138
|
"""Called when initializing the server."""
|
|
139
139
|
pass
|
|
140
140
|
|
|
@@ -211,7 +211,7 @@ class FlockMCPServerBase(BaseModel, Serializable, ABC):
|
|
|
211
211
|
additional_params=additional_params
|
|
212
212
|
)
|
|
213
213
|
result: list[
|
|
214
|
-
|
|
214
|
+
FlockMCPTool
|
|
215
215
|
] = await self.client_manager.get_tools(
|
|
216
216
|
agent_id=agent_id,
|
|
217
217
|
run_id=run_id,
|
|
@@ -385,7 +385,7 @@ class FlockMCPServerBase(BaseModel, Serializable, ABC):
|
|
|
385
385
|
span.record_exception(module_error)
|
|
386
386
|
|
|
387
387
|
# --- Async Methods ---
|
|
388
|
-
async def __aenter__(self) -> "
|
|
388
|
+
async def __aenter__(self) -> "FlockMCPServer":
|
|
389
389
|
"""Enter the asynchronous context for the server."""
|
|
390
390
|
# Spin up the client-manager
|
|
391
391
|
with tracer.start_as_current_span("server.__aenter__") as span:
|
|
@@ -614,7 +614,7 @@ class FlockMCPServerBase(BaseModel, Serializable, ABC):
|
|
|
614
614
|
config_cls = config_field.annotation
|
|
615
615
|
except (AttributeError, KeyError):
|
|
616
616
|
# fallback if Pydantic v1 or missing
|
|
617
|
-
config_cls =
|
|
617
|
+
config_cls = FlockMCPConfiguration
|
|
618
618
|
config_object = config_cls.from_dict(config_data)
|
|
619
619
|
data["config"] = config_object
|
|
620
620
|
|
|
@@ -13,7 +13,7 @@ from flock.core.logging.logging import get_logger
|
|
|
13
13
|
logger = get_logger("mcp.tool")
|
|
14
14
|
tracer = trace.get_tracer(__name__)
|
|
15
15
|
|
|
16
|
-
T = TypeVar("T", bound="
|
|
16
|
+
T = TypeVar("T", bound="FlockMCPTool")
|
|
17
17
|
|
|
18
18
|
TYPE_MAPPING = {
|
|
19
19
|
"string": str,
|
|
@@ -25,7 +25,7 @@ TYPE_MAPPING = {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class
|
|
28
|
+
class FlockMCPTool(BaseModel):
|
|
29
29
|
"""Base Class for MCP Tools for Flock."""
|
|
30
30
|
|
|
31
31
|
name: str = Field(..., description="Name of the tool")
|
flock/core/mcp/mcp_client.py
CHANGED
|
@@ -37,8 +37,8 @@ from pydantic import (
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
from flock.core.logging.logging import get_logger
|
|
40
|
-
from flock.core.mcp.
|
|
41
|
-
from flock.core.mcp.mcp_config import
|
|
40
|
+
from flock.core.mcp.flock_mcp_tool import FlockMCPTool
|
|
41
|
+
from flock.core.mcp.mcp_config import FlockMCPConfiguration
|
|
42
42
|
from flock.core.mcp.types.factories import (
|
|
43
43
|
default_flock_mcp_list_roots_callback_factory,
|
|
44
44
|
default_flock_mcp_logging_callback_factory,
|
|
@@ -61,7 +61,7 @@ tracer = trace.get_tracer(__name__)
|
|
|
61
61
|
GetSessionIdCallback = Callable[[], str | None]
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
class
|
|
64
|
+
class FlockMCPClient(BaseModel, ABC):
|
|
65
65
|
"""Wrapper for mcp ClientSession.
|
|
66
66
|
|
|
67
67
|
Class will attempt to re-establish connection if possible.
|
|
@@ -71,7 +71,7 @@ class FlockMCPClientBase(BaseModel, ABC):
|
|
|
71
71
|
"""
|
|
72
72
|
|
|
73
73
|
# --- Properties ---
|
|
74
|
-
config:
|
|
74
|
+
config: FlockMCPConfiguration = Field(
|
|
75
75
|
..., description="The config for this client instance."
|
|
76
76
|
)
|
|
77
77
|
|
|
@@ -225,7 +225,7 @@ class FlockMCPClientBase(BaseModel, ABC):
|
|
|
225
225
|
|
|
226
226
|
def __init__(
|
|
227
227
|
self,
|
|
228
|
-
config:
|
|
228
|
+
config: FlockMCPConfiguration,
|
|
229
229
|
lock: Lock | None = None,
|
|
230
230
|
tool_cache: TTLCache | None = None,
|
|
231
231
|
tool_result_cache: TTLCache | None = None,
|
|
@@ -374,24 +374,24 @@ class FlockMCPClientBase(BaseModel, ABC):
|
|
|
374
374
|
self,
|
|
375
375
|
agent_id: str,
|
|
376
376
|
run_id: str,
|
|
377
|
-
) -> list[
|
|
377
|
+
) -> list[FlockMCPTool]:
|
|
378
378
|
"""Gets a list of available tools from the server."""
|
|
379
379
|
|
|
380
380
|
@cached(cache=self.tool_cache, key=cache_key_generator)
|
|
381
381
|
async def _get_tools_cached(
|
|
382
382
|
agent_id: str,
|
|
383
383
|
run_id: str,
|
|
384
|
-
) -> list[
|
|
384
|
+
) -> list[FlockMCPTool]:
|
|
385
385
|
if not self.config.feature_config.tools_enabled:
|
|
386
386
|
return []
|
|
387
387
|
|
|
388
|
-
async def _get_tools_internal() -> list[
|
|
388
|
+
async def _get_tools_internal() -> list[FlockMCPTool]:
|
|
389
389
|
# TODO: Crash
|
|
390
390
|
response: ListToolsResult = await self.session.list_tools()
|
|
391
391
|
flock_tools = []
|
|
392
392
|
|
|
393
393
|
for tool in response.tools:
|
|
394
|
-
converted_tool =
|
|
394
|
+
converted_tool = FlockMCPTool.from_mcp_tool(
|
|
395
395
|
tool,
|
|
396
396
|
agent_id=agent_id,
|
|
397
397
|
run_id=run_id,
|
|
@@ -13,22 +13,22 @@ from pydantic import (
|
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
from flock.core.logging.logging import get_logger
|
|
16
|
-
from flock.core.mcp.
|
|
16
|
+
from flock.core.mcp.flock_mcp_tool import FlockMCPTool
|
|
17
17
|
from flock.core.mcp.mcp_client import (
|
|
18
|
-
|
|
18
|
+
FlockMCPClient,
|
|
19
19
|
)
|
|
20
|
-
from flock.core.mcp.mcp_config import
|
|
20
|
+
from flock.core.mcp.mcp_config import FlockMCPConfiguration
|
|
21
21
|
|
|
22
22
|
logger = get_logger("mcp.client_manager")
|
|
23
23
|
tracer = trace.get_tracer(__name__)
|
|
24
24
|
|
|
25
|
-
TClient = TypeVar("TClient", bound="
|
|
25
|
+
TClient = TypeVar("TClient", bound="FlockMCPClient")
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class
|
|
28
|
+
class FlockMCPClientManager(BaseModel, ABC, Generic[TClient]):
|
|
29
29
|
"""Handles a Pool of MCPClients of type TClient."""
|
|
30
30
|
|
|
31
|
-
client_config:
|
|
31
|
+
client_config: FlockMCPConfiguration = Field(
|
|
32
32
|
..., description="Configuration for clients."
|
|
33
33
|
)
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ class FlockMCPClientManagerBase(BaseModel, ABC, Generic[TClient]):
|
|
|
38
38
|
exclude=True,
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
-
clients: dict[str, dict[str,
|
|
41
|
+
clients: dict[str, dict[str, FlockMCPClient]] = Field(
|
|
42
42
|
default_factory=dict,
|
|
43
43
|
exclude=True,
|
|
44
44
|
description="Internal Store for the clients.",
|
|
@@ -154,7 +154,7 @@ class FlockMCPClientManagerBase(BaseModel, ABC, Generic[TClient]):
|
|
|
154
154
|
agent_id: str,
|
|
155
155
|
run_id: str,
|
|
156
156
|
additional_params: dict[str, Any] | None = None,
|
|
157
|
-
) -> list[
|
|
157
|
+
) -> list[FlockMCPTool]:
|
|
158
158
|
"""Retrieves a list of tools for the agents to act on."""
|
|
159
159
|
with tracer.start_as_current_span("client_manager.get_tools") as span:
|
|
160
160
|
span.set_attribute("agent_id", agent_id)
|
|
@@ -165,7 +165,7 @@ class FlockMCPClientManagerBase(BaseModel, ABC, Generic[TClient]):
|
|
|
165
165
|
run_id=run_id,
|
|
166
166
|
additional_params=additional_params,
|
|
167
167
|
)
|
|
168
|
-
tools: list[
|
|
168
|
+
tools: list[FlockMCPTool] = await client.get_tools(
|
|
169
169
|
agent_id=agent_id, run_id=run_id
|
|
170
170
|
)
|
|
171
171
|
return tools
|