flock-core 0.4.520__py3-none-any.whl → 0.5.0b1__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/manage_agents.py +3 -3
- flock/components/__init__.py +28 -0
- flock/components/evaluation/__init__.py +9 -0
- flock/components/evaluation/declarative_evaluation_component.py +198 -0
- flock/components/routing/__init__.py +15 -0
- flock/{routers/conditional/conditional_router.py → components/routing/conditional_routing_component.py} +60 -49
- flock/components/routing/default_routing_component.py +103 -0
- flock/components/routing/llm_routing_component.py +208 -0
- flock/components/utility/__init__.py +15 -0
- flock/{modules/enterprise_memory/enterprise_memory_module.py → components/utility/memory_utility_component.py} +195 -173
- flock/{modules/performance/metrics_module.py → components/utility/metrics_utility_component.py} +101 -86
- flock/{modules/output/output_module.py → components/utility/output_utility_component.py} +49 -49
- flock/core/__init__.py +2 -8
- flock/core/agent/__init__.py +16 -0
- flock/core/agent/flock_agent_components.py +104 -0
- flock/core/agent/flock_agent_execution.py +101 -0
- flock/core/agent/flock_agent_integration.py +147 -0
- flock/core/agent/flock_agent_lifecycle.py +177 -0
- flock/core/agent/flock_agent_serialization.py +378 -0
- flock/core/component/__init__.py +15 -0
- flock/core/{flock_module.py → component/agent_component_base.py} +136 -35
- flock/core/component/evaluation_component_base.py +56 -0
- flock/core/component/routing_component_base.py +75 -0
- flock/core/component/utility_component_base.py +69 -0
- flock/core/config/flock_agent_config.py +49 -2
- flock/core/evaluation/utils.py +1 -1
- flock/core/execution/evaluation_executor.py +1 -1
- flock/core/flock.py +137 -483
- flock/core/flock_agent.py +151 -1018
- flock/core/flock_factory.py +94 -73
- flock/core/{flock_registry.py → flock_registry.py.backup} +3 -17
- flock/core/logging/logging.py +1 -0
- flock/core/mcp/flock_mcp_server.py +42 -37
- flock/core/mixin/dspy_integration.py +5 -5
- flock/core/orchestration/__init__.py +18 -0
- flock/core/orchestration/flock_batch_processor.py +94 -0
- flock/core/orchestration/flock_evaluator.py +113 -0
- flock/core/orchestration/flock_execution.py +288 -0
- flock/core/orchestration/flock_initialization.py +125 -0
- flock/core/orchestration/flock_server_manager.py +65 -0
- flock/core/orchestration/flock_web_server.py +117 -0
- flock/core/registry/__init__.py +39 -0
- flock/core/registry/agent_registry.py +69 -0
- flock/core/registry/callable_registry.py +139 -0
- flock/core/registry/component_discovery.py +142 -0
- flock/core/registry/component_registry.py +64 -0
- flock/core/registry/config_mapping.py +64 -0
- flock/core/registry/decorators.py +137 -0
- flock/core/registry/registry_hub.py +202 -0
- flock/core/registry/server_registry.py +57 -0
- flock/core/registry/type_registry.py +86 -0
- flock/core/serialization/flock_serializer.py +33 -30
- flock/core/serialization/serialization_utils.py +28 -25
- flock/core/util/input_resolver.py +29 -2
- flock/platform/docker_tools.py +3 -3
- flock/tools/markdown_tools.py +1 -2
- flock/tools/text_tools.py +1 -2
- flock/webapp/app/main.py +9 -5
- flock/workflow/activities.py +59 -84
- flock/workflow/activities_unified.py +230 -0
- flock/workflow/agent_execution_activity.py +6 -6
- flock/workflow/flock_workflow.py +1 -1
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b1.dist-info}/METADATA +2 -2
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b1.dist-info}/RECORD +67 -68
- flock/core/flock_evaluator.py +0 -60
- flock/core/flock_router.py +0 -83
- flock/evaluators/__init__.py +0 -1
- flock/evaluators/declarative/__init__.py +0 -1
- flock/evaluators/declarative/declarative_evaluator.py +0 -194
- flock/evaluators/memory/memory_evaluator.py +0 -90
- flock/evaluators/test/test_case_evaluator.py +0 -38
- flock/evaluators/zep/zep_evaluator.py +0 -59
- flock/modules/__init__.py +0 -1
- flock/modules/assertion/__init__.py +0 -1
- flock/modules/assertion/assertion_module.py +0 -286
- flock/modules/callback/__init__.py +0 -1
- flock/modules/callback/callback_module.py +0 -91
- flock/modules/enterprise_memory/README.md +0 -99
- flock/modules/mem0/__init__.py +0 -1
- flock/modules/mem0/mem0_module.py +0 -126
- flock/modules/mem0_async/__init__.py +0 -1
- flock/modules/mem0_async/async_mem0_module.py +0 -126
- flock/modules/memory/__init__.py +0 -1
- flock/modules/memory/memory_module.py +0 -429
- flock/modules/memory/memory_parser.py +0 -125
- flock/modules/memory/memory_storage.py +0 -736
- flock/modules/output/__init__.py +0 -1
- flock/modules/performance/__init__.py +0 -1
- flock/modules/zep/__init__.py +0 -1
- flock/modules/zep/zep_module.py +0 -192
- flock/routers/__init__.py +0 -1
- flock/routers/agent/__init__.py +0 -1
- flock/routers/agent/agent_router.py +0 -236
- flock/routers/agent/handoff_agent.py +0 -58
- flock/routers/default/__init__.py +0 -1
- flock/routers/default/default_router.py +0 -80
- flock/routers/feedback/feedback_router.py +0 -114
- flock/routers/list_generator/list_generator_router.py +0 -166
- flock/routers/llm/__init__.py +0 -1
- flock/routers/llm/llm_router.py +0 -365
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b1.dist-info}/WHEEL +0 -0
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b1.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# src/flock/core/registry/decorators.py
|
|
2
|
+
"""Registry decorators for component, tool, and type registration."""
|
|
3
|
+
|
|
4
|
+
import inspect
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any, TypeVar, overload
|
|
7
|
+
|
|
8
|
+
from flock.core.registry.registry_hub import get_registry
|
|
9
|
+
|
|
10
|
+
ClassType = TypeVar("ClassType", bound=type)
|
|
11
|
+
FuncType = TypeVar("FuncType", bound=Callable)
|
|
12
|
+
ConfigType = TypeVar("ConfigType")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# --- Component Registration Decorator ---
|
|
16
|
+
|
|
17
|
+
@overload
|
|
18
|
+
def flock_component(cls: ClassType) -> ClassType: ... # Basic registration
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@overload
|
|
22
|
+
def flock_component(
|
|
23
|
+
*, name: str | None = None, config_class: type[ConfigType] | None = None
|
|
24
|
+
) -> Callable[[ClassType], ClassType]: ... # With options
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def flock_component(
|
|
28
|
+
cls: ClassType | None = None,
|
|
29
|
+
*,
|
|
30
|
+
name: str | None = None,
|
|
31
|
+
config_class: type[ConfigType] | None = None,
|
|
32
|
+
) -> Any:
|
|
33
|
+
"""Decorator to register a Flock Component class and optionally link its config class."""
|
|
34
|
+
registry = get_registry()
|
|
35
|
+
|
|
36
|
+
def decorator(inner_cls: ClassType) -> ClassType:
|
|
37
|
+
if not inspect.isclass(inner_cls):
|
|
38
|
+
raise TypeError("@flock_component can only decorate classes.")
|
|
39
|
+
|
|
40
|
+
component_name = name or inner_cls.__name__
|
|
41
|
+
registry.register_component(inner_cls, name=component_name)
|
|
42
|
+
|
|
43
|
+
# If config_class is provided, register the mapping
|
|
44
|
+
if config_class:
|
|
45
|
+
registry.register_config_component_pair(config_class, inner_cls)
|
|
46
|
+
|
|
47
|
+
return inner_cls
|
|
48
|
+
|
|
49
|
+
if cls is None:
|
|
50
|
+
# Called as @flock_component(name="...", config_class=...)
|
|
51
|
+
return decorator
|
|
52
|
+
else:
|
|
53
|
+
# Called as @flock_component
|
|
54
|
+
return decorator(cls)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# --- Tool/Callable Registration Decorator ---
|
|
58
|
+
|
|
59
|
+
@overload
|
|
60
|
+
def flock_tool(func: FuncType) -> FuncType: ...
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@overload
|
|
64
|
+
def flock_tool(
|
|
65
|
+
*, name: str | None = None
|
|
66
|
+
) -> Callable[[FuncType], FuncType]: ...
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def flock_tool(func: FuncType | None = None, *, name: str | None = None) -> Any:
|
|
70
|
+
"""Decorator to register a callable function/method as a Tool (or general callable).
|
|
71
|
+
|
|
72
|
+
Usage:
|
|
73
|
+
@flock_tool
|
|
74
|
+
def my_web_search(query: str): ...
|
|
75
|
+
|
|
76
|
+
@flock_tool(name="utils.calculate_pi")
|
|
77
|
+
def compute_pi(): ...
|
|
78
|
+
"""
|
|
79
|
+
registry = get_registry()
|
|
80
|
+
|
|
81
|
+
def decorator(inner_func: FuncType) -> FuncType:
|
|
82
|
+
if not callable(inner_func):
|
|
83
|
+
raise TypeError("@flock_tool can only decorate callables.")
|
|
84
|
+
# Let registry handle default name generation if None
|
|
85
|
+
registry.register_callable(inner_func, name=name)
|
|
86
|
+
return inner_func
|
|
87
|
+
|
|
88
|
+
if func is None:
|
|
89
|
+
# Called as @flock_tool(name="...")
|
|
90
|
+
return decorator
|
|
91
|
+
else:
|
|
92
|
+
# Called as @flock_tool
|
|
93
|
+
return decorator(func)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Alias for clarity
|
|
97
|
+
flock_callable = flock_tool
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# --- Type Registration Decorator ---
|
|
101
|
+
|
|
102
|
+
@overload
|
|
103
|
+
def flock_type(cls: ClassType) -> ClassType: ...
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@overload
|
|
107
|
+
def flock_type(
|
|
108
|
+
*, name: str | None = None
|
|
109
|
+
) -> Callable[[ClassType], ClassType]: ...
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def flock_type(cls: ClassType | None = None, *, name: str | None = None) -> Any:
|
|
113
|
+
"""Decorator to register a Type (Pydantic Model, Dataclass) used in signatures.
|
|
114
|
+
|
|
115
|
+
Usage:
|
|
116
|
+
@flock_type
|
|
117
|
+
class MyDataModel(BaseModel): ...
|
|
118
|
+
|
|
119
|
+
@flock_type(name="UserInput")
|
|
120
|
+
@dataclass
|
|
121
|
+
class UserQuery: ...
|
|
122
|
+
"""
|
|
123
|
+
registry = get_registry()
|
|
124
|
+
|
|
125
|
+
def decorator(inner_cls: ClassType) -> ClassType:
|
|
126
|
+
if not inspect.isclass(inner_cls):
|
|
127
|
+
raise TypeError("@flock_type can only decorate classes.")
|
|
128
|
+
type_name = name or inner_cls.__name__
|
|
129
|
+
registry.register_type(inner_cls, name=type_name)
|
|
130
|
+
return inner_cls
|
|
131
|
+
|
|
132
|
+
if cls is None:
|
|
133
|
+
# Called as @flock_type(name="...")
|
|
134
|
+
return decorator
|
|
135
|
+
else:
|
|
136
|
+
# Called as @flock_type
|
|
137
|
+
return decorator(cls)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# src/flock/core/registry/registry_hub.py
|
|
2
|
+
"""Main registry hub using composition pattern with thread safety."""
|
|
3
|
+
|
|
4
|
+
import threading
|
|
5
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
6
|
+
|
|
7
|
+
from flock.core.logging.logging import get_logger
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from flock.core.flock_agent import FlockAgent
|
|
12
|
+
from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
|
|
13
|
+
|
|
14
|
+
logger = get_logger("registry.hub")
|
|
15
|
+
|
|
16
|
+
T = TypeVar("T")
|
|
17
|
+
ConfigType = TypeVar("ConfigType")
|
|
18
|
+
ClassType = TypeVar("ClassType")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RegistryHub:
|
|
22
|
+
"""Thread-safe registry hub using composition pattern.
|
|
23
|
+
|
|
24
|
+
Main coordinator for all registry types following the successful
|
|
25
|
+
pattern from Flock and FlockAgent refactoring.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self._lock = threading.RLock()
|
|
30
|
+
logger.debug("RegistryHub initialized with thread safety")
|
|
31
|
+
|
|
32
|
+
# --- Lazy-loaded composition helpers (following Flock pattern) ---
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def components(self):
|
|
36
|
+
"""Component class registry helper."""
|
|
37
|
+
if not hasattr(self, '_components_helper'):
|
|
38
|
+
from flock.core.registry.component_registry import ComponentRegistry
|
|
39
|
+
self._components_helper = ComponentRegistry(self._lock)
|
|
40
|
+
return self._components_helper
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def callables(self):
|
|
44
|
+
"""Callable registry helper."""
|
|
45
|
+
if not hasattr(self, '_callables_helper'):
|
|
46
|
+
from flock.core.registry.callable_registry import CallableRegistry
|
|
47
|
+
self._callables_helper = CallableRegistry(self._lock)
|
|
48
|
+
return self._callables_helper
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def agents(self):
|
|
52
|
+
"""Agent registry helper."""
|
|
53
|
+
if not hasattr(self, '_agents_helper'):
|
|
54
|
+
from flock.core.registry.agent_registry import AgentRegistry
|
|
55
|
+
self._agents_helper = AgentRegistry(self._lock)
|
|
56
|
+
return self._agents_helper
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def servers(self):
|
|
60
|
+
"""Server registry helper."""
|
|
61
|
+
if not hasattr(self, '_servers_helper'):
|
|
62
|
+
from flock.core.registry.server_registry import ServerRegistry
|
|
63
|
+
self._servers_helper = ServerRegistry(self._lock)
|
|
64
|
+
return self._servers_helper
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def types(self):
|
|
68
|
+
"""Type registry helper."""
|
|
69
|
+
if not hasattr(self, '_types_helper'):
|
|
70
|
+
from flock.core.registry.type_registry import TypeRegistry
|
|
71
|
+
self._types_helper = TypeRegistry(self._lock)
|
|
72
|
+
return self._types_helper
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def config_mapping(self):
|
|
76
|
+
"""Config mapping helper."""
|
|
77
|
+
if not hasattr(self, '_config_mapping_helper'):
|
|
78
|
+
from flock.core.registry.config_mapping import ConfigMapping
|
|
79
|
+
self._config_mapping_helper = ConfigMapping(self._lock)
|
|
80
|
+
return self._config_mapping_helper
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def discovery(self):
|
|
84
|
+
"""Component discovery helper."""
|
|
85
|
+
if not hasattr(self, '_discovery_helper'):
|
|
86
|
+
from flock.core.registry.component_discovery import ComponentDiscovery
|
|
87
|
+
self._discovery_helper = ComponentDiscovery(self)
|
|
88
|
+
return self._discovery_helper
|
|
89
|
+
|
|
90
|
+
# --- High-level registry operations (delegate to helpers) ---
|
|
91
|
+
|
|
92
|
+
def register_agent(self, agent: "FlockAgent", *, force: bool = False) -> None:
|
|
93
|
+
"""Register a FlockAgent instance."""
|
|
94
|
+
self.agents.register_agent(agent, force=force)
|
|
95
|
+
|
|
96
|
+
def get_agent(self, name: str) -> "FlockAgent | None":
|
|
97
|
+
"""Get a registered FlockAgent instance by name."""
|
|
98
|
+
return self.agents.get_agent(name)
|
|
99
|
+
|
|
100
|
+
def get_all_agent_names(self) -> list[str]:
|
|
101
|
+
"""Get all registered agent names."""
|
|
102
|
+
return self.agents.get_all_agent_names()
|
|
103
|
+
|
|
104
|
+
def register_server(self, server: "FlockMCPServerBase") -> None:
|
|
105
|
+
"""Register a FlockMCPServer instance."""
|
|
106
|
+
self.servers.register_server(server)
|
|
107
|
+
|
|
108
|
+
def get_server(self, name: str) -> "FlockMCPServerBase | None":
|
|
109
|
+
"""Get a registered FlockMCPServer instance by name."""
|
|
110
|
+
return self.servers.get_server(name)
|
|
111
|
+
|
|
112
|
+
def get_all_server_names(self) -> list[str]:
|
|
113
|
+
"""Get all registered server names."""
|
|
114
|
+
return self.servers.get_all_server_names()
|
|
115
|
+
|
|
116
|
+
def register_callable(self, func: "Callable", name: str | None = None) -> str | None:
|
|
117
|
+
"""Register a callable function/method."""
|
|
118
|
+
return self.callables.register_callable(func, name)
|
|
119
|
+
|
|
120
|
+
def get_callable(self, name_or_path: str) -> "Callable":
|
|
121
|
+
"""Get a registered callable by name or path."""
|
|
122
|
+
return self.callables.get_callable(name_or_path)
|
|
123
|
+
|
|
124
|
+
def get_callable_path_string(self, func: "Callable") -> str | None:
|
|
125
|
+
"""Get the path string for a callable."""
|
|
126
|
+
return self.callables.get_callable_path_string(func)
|
|
127
|
+
|
|
128
|
+
def register_type(self, type_obj: type, name: str | None = None) -> str | None:
|
|
129
|
+
"""Register a type (Pydantic Model, Dataclass, etc.)."""
|
|
130
|
+
return self.types.register_type(type_obj, name)
|
|
131
|
+
|
|
132
|
+
def get_type(self, type_name: str) -> type:
|
|
133
|
+
"""Get a registered type by name."""
|
|
134
|
+
return self.types.get_type(type_name)
|
|
135
|
+
|
|
136
|
+
def register_component(self, component_class: type, name: str | None = None) -> str | None:
|
|
137
|
+
"""Register a component class."""
|
|
138
|
+
return self.components.register_component(component_class, name)
|
|
139
|
+
|
|
140
|
+
def get_component(self, type_name: str) -> type:
|
|
141
|
+
"""Get a registered component class by name."""
|
|
142
|
+
return self.components.get_component(type_name)
|
|
143
|
+
|
|
144
|
+
def get_component_type_name(self, component_class: type) -> str | None:
|
|
145
|
+
"""Get the type name for a component class."""
|
|
146
|
+
return self.components.get_component_type_name(component_class)
|
|
147
|
+
|
|
148
|
+
def register_config_component_pair(self, config_cls: type[ConfigType], component_cls: type[ClassType]) -> None:
|
|
149
|
+
"""Register a config-to-component mapping."""
|
|
150
|
+
self.config_mapping.register_config_component_pair(config_cls, component_cls)
|
|
151
|
+
|
|
152
|
+
def get_component_class_for_config(self, config_cls: type[ConfigType]) -> type[ClassType] | None:
|
|
153
|
+
"""Get the component class for a config class."""
|
|
154
|
+
return self.config_mapping.get_component_class_for_config(config_cls)
|
|
155
|
+
|
|
156
|
+
def discover_and_register_components(self) -> None:
|
|
157
|
+
"""Auto-discover and register components from known packages."""
|
|
158
|
+
self.discovery.discover_and_register_components()
|
|
159
|
+
|
|
160
|
+
def register_module_components(self, module_or_path: Any) -> None:
|
|
161
|
+
"""Register components from a specific module."""
|
|
162
|
+
self.discovery.register_module_components(module_or_path)
|
|
163
|
+
|
|
164
|
+
# --- Utility methods ---
|
|
165
|
+
|
|
166
|
+
def clear_all(self) -> None:
|
|
167
|
+
"""Clear all registries (useful for testing)."""
|
|
168
|
+
with self._lock:
|
|
169
|
+
if hasattr(self, '_agents_helper'):
|
|
170
|
+
self.agents.clear()
|
|
171
|
+
if hasattr(self, '_servers_helper'):
|
|
172
|
+
self.servers.clear()
|
|
173
|
+
if hasattr(self, '_callables_helper'):
|
|
174
|
+
self.callables.clear()
|
|
175
|
+
if hasattr(self, '_types_helper'):
|
|
176
|
+
self.types.clear()
|
|
177
|
+
if hasattr(self, '_components_helper'):
|
|
178
|
+
self.components.clear()
|
|
179
|
+
if hasattr(self, '_config_mapping_helper'):
|
|
180
|
+
self.config_mapping.clear_config_mappings()
|
|
181
|
+
logger.debug("Cleared all registries")
|
|
182
|
+
|
|
183
|
+
def get_registry_summary(self) -> dict[str, int]:
|
|
184
|
+
"""Get a summary of all registries."""
|
|
185
|
+
with self._lock:
|
|
186
|
+
return {
|
|
187
|
+
"agents": len(self.agents.get_all_agents()),
|
|
188
|
+
"servers": len(self.servers.get_all_servers()),
|
|
189
|
+
"callables": len(self.callables.get_all_callables()),
|
|
190
|
+
"types": len(self.types.get_all_types()),
|
|
191
|
+
"components": len(self.components.get_all_components()),
|
|
192
|
+
"config_mappings": len(self.config_mapping.get_all_config_mappings()),
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# --- Global singleton instance ---
|
|
197
|
+
_default_registry_hub = RegistryHub()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_registry() -> RegistryHub:
|
|
201
|
+
"""Get the default thread-safe registry hub instance."""
|
|
202
|
+
return _default_registry_hub
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# src/flock/core/registry/server_registry.py
|
|
2
|
+
"""MCP Server registration and lookup functionality."""
|
|
3
|
+
|
|
4
|
+
import threading
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from flock.core.logging.logging import get_logger
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
|
|
11
|
+
|
|
12
|
+
logger = get_logger("registry.servers")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ServerRegistry:
|
|
16
|
+
"""Manages FlockMCPServerBase registration and lookup with thread safety."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, lock: threading.RLock):
|
|
19
|
+
self._lock = lock
|
|
20
|
+
self._servers: dict[str, "FlockMCPServerBase"] = {}
|
|
21
|
+
|
|
22
|
+
def register_server(self, server: "FlockMCPServerBase") -> None:
|
|
23
|
+
"""Register a flock mcp server by its name."""
|
|
24
|
+
if not hasattr(server.config, "name") or not server.config.name:
|
|
25
|
+
logger.error("Attempted to register a server without a valid 'name' attribute.")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
with self._lock:
|
|
29
|
+
if server.config.name in self._servers and self._servers[server.config.name] != server:
|
|
30
|
+
logger.warning(f"Server '{server.config.name}' already registered. Overwriting.")
|
|
31
|
+
|
|
32
|
+
self._servers[server.config.name] = server
|
|
33
|
+
logger.debug(f"Registered server: {server.config.name}")
|
|
34
|
+
|
|
35
|
+
def get_server(self, name: str) -> "FlockMCPServerBase | None":
|
|
36
|
+
"""Retrieve a registered FlockMCPServer instance by name."""
|
|
37
|
+
with self._lock:
|
|
38
|
+
server = self._servers.get(name)
|
|
39
|
+
if not server:
|
|
40
|
+
logger.warning(f"Server '{name}' not found in registry.")
|
|
41
|
+
return server
|
|
42
|
+
|
|
43
|
+
def get_all_server_names(self) -> list[str]:
|
|
44
|
+
"""Return a list of names for all registered servers."""
|
|
45
|
+
with self._lock:
|
|
46
|
+
return list(self._servers.keys())
|
|
47
|
+
|
|
48
|
+
def get_all_servers(self) -> dict[str, "FlockMCPServerBase"]:
|
|
49
|
+
"""Get all registered servers."""
|
|
50
|
+
with self._lock:
|
|
51
|
+
return self._servers.copy()
|
|
52
|
+
|
|
53
|
+
def clear(self) -> None:
|
|
54
|
+
"""Clear all registered servers."""
|
|
55
|
+
with self._lock:
|
|
56
|
+
self._servers.clear()
|
|
57
|
+
logger.debug("Cleared all registered servers")
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# src/flock/core/registry/type_registry.py
|
|
2
|
+
"""Type registration and lookup functionality."""
|
|
3
|
+
|
|
4
|
+
import threading
|
|
5
|
+
from typing import Any, Literal, Mapping, Optional, Sequence, TypeVar, Union
|
|
6
|
+
|
|
7
|
+
from flock.core.logging.logging import get_logger
|
|
8
|
+
|
|
9
|
+
logger = get_logger("registry.types")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TypeRegistry:
|
|
13
|
+
"""Manages type registration and lookup with thread safety."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, lock: threading.RLock):
|
|
16
|
+
self._lock = lock
|
|
17
|
+
self._types: dict[str, type] = {}
|
|
18
|
+
self._register_core_types()
|
|
19
|
+
|
|
20
|
+
def register_type(self, type_obj: type, name: str | None = None) -> str | None:
|
|
21
|
+
"""Register a class/type (Pydantic, Dataclass, etc.) used in signatures."""
|
|
22
|
+
type_name = name or type_obj.__name__
|
|
23
|
+
if not type_name:
|
|
24
|
+
logger.error(f"Could not determine name for type: {type_obj}")
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
with self._lock:
|
|
28
|
+
if type_name in self._types and self._types[type_name] != type_obj:
|
|
29
|
+
logger.warning(f"Type '{type_name}' already registered. Overwriting.")
|
|
30
|
+
|
|
31
|
+
self._types[type_name] = type_obj
|
|
32
|
+
logger.debug(f"Registered type: {type_name}")
|
|
33
|
+
return type_name
|
|
34
|
+
|
|
35
|
+
def get_type(self, type_name: str) -> type:
|
|
36
|
+
"""Retrieve a registered type by its name."""
|
|
37
|
+
with self._lock:
|
|
38
|
+
if type_name in self._types:
|
|
39
|
+
return self._types[type_name]
|
|
40
|
+
|
|
41
|
+
# Consider adding dynamic import attempts for types if needed,
|
|
42
|
+
# but explicit registration is generally safer for types.
|
|
43
|
+
logger.warning(f"Type '{type_name}' not found in registry. Will attempt to build it from builtins.")
|
|
44
|
+
raise KeyError(f"Type '{type_name}' not found. Ensure it is registered.")
|
|
45
|
+
|
|
46
|
+
def get_all_types(self) -> dict[str, type]:
|
|
47
|
+
"""Get all registered types."""
|
|
48
|
+
with self._lock:
|
|
49
|
+
return self._types.copy()
|
|
50
|
+
|
|
51
|
+
def clear(self) -> None:
|
|
52
|
+
"""Clear all registered types (except core types)."""
|
|
53
|
+
with self._lock:
|
|
54
|
+
# Save core types
|
|
55
|
+
core_types = {
|
|
56
|
+
name: type_obj for name, type_obj in self._types.items()
|
|
57
|
+
if type_obj in [str, int, float, bool, list, dict, tuple, set, Any, Mapping, Sequence, TypeVar, Literal, Optional, Union]
|
|
58
|
+
}
|
|
59
|
+
self._types.clear()
|
|
60
|
+
self._types.update(core_types)
|
|
61
|
+
logger.debug("Cleared all registered types (keeping core types)")
|
|
62
|
+
|
|
63
|
+
def _register_core_types(self):
|
|
64
|
+
"""Register common built-in and typing types."""
|
|
65
|
+
core_types = [
|
|
66
|
+
str,
|
|
67
|
+
int,
|
|
68
|
+
float,
|
|
69
|
+
bool,
|
|
70
|
+
list,
|
|
71
|
+
dict,
|
|
72
|
+
tuple,
|
|
73
|
+
set,
|
|
74
|
+
Any,
|
|
75
|
+
Mapping,
|
|
76
|
+
Sequence,
|
|
77
|
+
TypeVar,
|
|
78
|
+
Literal,
|
|
79
|
+
Optional,
|
|
80
|
+
Union, # Common typing generics
|
|
81
|
+
]
|
|
82
|
+
for t in core_types:
|
|
83
|
+
try:
|
|
84
|
+
self.register_type(t)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logger.error(f"Failed to auto-register core type {t}: {e}")
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# src/flock/core/serialization/flock_serializer.py
|
|
2
2
|
"""Handles serialization and deserialization logic for Flock instances."""
|
|
3
3
|
|
|
4
|
-
import builtins
|
|
5
4
|
import importlib
|
|
6
5
|
import importlib.util
|
|
7
6
|
import inspect
|
|
@@ -14,7 +13,7 @@ from typing import TYPE_CHECKING, Any, Literal
|
|
|
14
13
|
from pydantic import BaseModel, create_model
|
|
15
14
|
|
|
16
15
|
# Need registry access
|
|
17
|
-
from flock.core.
|
|
16
|
+
from flock.core.registry import get_registry
|
|
18
17
|
from flock.core.logging.logging import get_logger
|
|
19
18
|
from flock.core.serialization.serialization_utils import (
|
|
20
19
|
# Assuming this handles basic serialization needs
|
|
@@ -26,7 +25,7 @@ if TYPE_CHECKING:
|
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
logger = get_logger("serialization.flock")
|
|
29
|
-
|
|
28
|
+
registry = get_registry()
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class FlockSerializer:
|
|
@@ -186,7 +185,7 @@ class FlockSerializer:
|
|
|
186
185
|
"description_callable"
|
|
187
186
|
]
|
|
188
187
|
description_callable = agent_instance.description
|
|
189
|
-
path_str =
|
|
188
|
+
path_str = registry.get_callable_path_string(
|
|
190
189
|
description_callable
|
|
191
190
|
)
|
|
192
191
|
if path_str:
|
|
@@ -205,7 +204,7 @@ class FlockSerializer:
|
|
|
205
204
|
)
|
|
206
205
|
input_callable_name = agent_data["input_callable"]
|
|
207
206
|
input_callable = agent_instance.input
|
|
208
|
-
path_str =
|
|
207
|
+
path_str = registry.get_callable_path_string(
|
|
209
208
|
input_callable
|
|
210
209
|
)
|
|
211
210
|
if path_str:
|
|
@@ -224,7 +223,7 @@ class FlockSerializer:
|
|
|
224
223
|
)
|
|
225
224
|
output_callable_name = agent_data["output_callable"]
|
|
226
225
|
output_callable = agent_instance.output
|
|
227
|
-
path_str =
|
|
226
|
+
path_str = registry.get_callable_path_string(
|
|
228
227
|
output_callable
|
|
229
228
|
)
|
|
230
229
|
if path_str:
|
|
@@ -250,7 +249,7 @@ class FlockSerializer:
|
|
|
250
249
|
tool = tool_objs[i]
|
|
251
250
|
if callable(tool) and not isinstance(tool, type):
|
|
252
251
|
path_str = (
|
|
253
|
-
|
|
252
|
+
registry.get_callable_path_string(tool)
|
|
254
253
|
)
|
|
255
254
|
if path_str:
|
|
256
255
|
logger.debug(
|
|
@@ -415,7 +414,7 @@ class FlockSerializer:
|
|
|
415
414
|
type_definitions = {}
|
|
416
415
|
for type_name in type_names:
|
|
417
416
|
try:
|
|
418
|
-
type_obj =
|
|
417
|
+
type_obj = registry.get_type(
|
|
419
418
|
type_name
|
|
420
419
|
) # Throws KeyError if not found
|
|
421
420
|
type_def = FlockSerializer._extract_type_definition(
|
|
@@ -490,7 +489,7 @@ class FlockSerializer:
|
|
|
490
489
|
"file_path": None,
|
|
491
490
|
}
|
|
492
491
|
try:
|
|
493
|
-
component_class =
|
|
492
|
+
component_class = registry.get_component(
|
|
494
493
|
component_type_name
|
|
495
494
|
) # Raises KeyError if not found
|
|
496
495
|
component_def["module_path"] = getattr(
|
|
@@ -540,7 +539,7 @@ class FlockSerializer:
|
|
|
540
539
|
"file_path": None,
|
|
541
540
|
}
|
|
542
541
|
try:
|
|
543
|
-
func =
|
|
542
|
+
func = registry.get_callable(
|
|
544
543
|
callable_path
|
|
545
544
|
) # Raises KeyError if not found
|
|
546
545
|
callable_def["module_path"] = getattr(func, "__module__", "unknown")
|
|
@@ -596,7 +595,7 @@ class FlockSerializer:
|
|
|
596
595
|
module = importlib.import_module(module_path)
|
|
597
596
|
if hasattr(module, type_name):
|
|
598
597
|
type_obj = getattr(module, type_name)
|
|
599
|
-
|
|
598
|
+
registry.register_type(type_obj, type_name)
|
|
600
599
|
logger.info(
|
|
601
600
|
f"Registered type '{type_name}' from module '{module_path}'"
|
|
602
601
|
)
|
|
@@ -629,7 +628,7 @@ class FlockSerializer:
|
|
|
629
628
|
type_name: str, type_def: dict[str, Any]
|
|
630
629
|
) -> None:
|
|
631
630
|
"""Dynamically create and register a Pydantic model from schema."""
|
|
632
|
-
# (Logic remains the same, ensure it uses
|
|
631
|
+
# (Logic remains the same, ensure it uses registry.register_type)
|
|
633
632
|
schema = type_def.get("schema", {})
|
|
634
633
|
try:
|
|
635
634
|
fields = {}
|
|
@@ -641,7 +640,7 @@ class FlockSerializer:
|
|
|
641
640
|
fields[field_name] = (field_type, default)
|
|
642
641
|
|
|
643
642
|
DynamicModel = create_model(type_name, **fields)
|
|
644
|
-
|
|
643
|
+
registry.register_type(DynamicModel, type_name)
|
|
645
644
|
logger.info(
|
|
646
645
|
f"Dynamically created and registered Pydantic model: {type_name}"
|
|
647
646
|
)
|
|
@@ -672,27 +671,31 @@ class FlockSerializer:
|
|
|
672
671
|
@staticmethod
|
|
673
672
|
def _create_dataclass(type_name: str, type_def: dict[str, Any]) -> None:
|
|
674
673
|
"""Dynamically create and register a dataclass."""
|
|
675
|
-
# (Logic remains the same, ensure it uses
|
|
674
|
+
# (Logic remains the same, ensure it uses registry.register_type)
|
|
676
675
|
from dataclasses import make_dataclass
|
|
677
676
|
|
|
678
677
|
fields_def = type_def.get("fields", {})
|
|
679
678
|
try:
|
|
680
679
|
fields = []
|
|
681
680
|
for field_name, field_props in fields_def.items():
|
|
682
|
-
# Safely
|
|
681
|
+
# Safely map type strings to actual types
|
|
683
682
|
field_type_str = field_props.get("type", "str")
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
683
|
+
type_mapping = {
|
|
684
|
+
"str": str,
|
|
685
|
+
"int": int,
|
|
686
|
+
"float": float,
|
|
687
|
+
"bool": bool,
|
|
688
|
+
"list": list,
|
|
689
|
+
"dict": dict,
|
|
690
|
+
"List": list,
|
|
691
|
+
"Dict": dict,
|
|
692
|
+
"Any": Any,
|
|
693
|
+
}
|
|
694
|
+
field_type = type_mapping.get(field_type_str, Any)
|
|
692
695
|
fields.append((field_name, field_type))
|
|
693
696
|
|
|
694
697
|
DynamicDataclass = make_dataclass(type_name, fields)
|
|
695
|
-
|
|
698
|
+
registry.register_type(DynamicDataclass, type_name)
|
|
696
699
|
logger.info(
|
|
697
700
|
f"Dynamically created and registered dataclass: {type_name}"
|
|
698
701
|
)
|
|
@@ -705,7 +708,7 @@ class FlockSerializer:
|
|
|
705
708
|
path_type: Literal["absolute", "relative"],
|
|
706
709
|
) -> None:
|
|
707
710
|
"""Register component/callable definitions from serialized data."""
|
|
708
|
-
# (Logic remains the same, ensure it uses
|
|
711
|
+
# (Logic remains the same, ensure it uses registry.register_component/register_callable)
|
|
709
712
|
# Key change: Ensure file_path is handled correctly based on path_type from metadata
|
|
710
713
|
for name, comp_def in component_defs.items():
|
|
711
714
|
logger.debug(
|
|
@@ -735,13 +738,13 @@ class FlockSerializer:
|
|
|
735
738
|
if hasattr(module, name):
|
|
736
739
|
obj = getattr(module, name)
|
|
737
740
|
if kind == "flock_callable" and callable(obj):
|
|
738
|
-
|
|
741
|
+
registry.register_callable(
|
|
739
742
|
obj, name
|
|
740
743
|
) # Register by simple name
|
|
741
744
|
# Also register by full path if possible
|
|
742
745
|
full_path = f"{module_path}.{name}"
|
|
743
746
|
if full_path != name:
|
|
744
|
-
|
|
747
|
+
registry.register_callable(obj, full_path)
|
|
745
748
|
logger.info(
|
|
746
749
|
f"Registered callable '{name}' from module '{module_path}'"
|
|
747
750
|
)
|
|
@@ -749,7 +752,7 @@ class FlockSerializer:
|
|
|
749
752
|
elif kind == "flock_component" and isinstance(
|
|
750
753
|
obj, type
|
|
751
754
|
):
|
|
752
|
-
|
|
755
|
+
registry.register_component(obj, name)
|
|
753
756
|
logger.info(
|
|
754
757
|
f"Registered component '{name}' from module '{module_path}'"
|
|
755
758
|
)
|
|
@@ -785,14 +788,14 @@ class FlockSerializer:
|
|
|
785
788
|
if hasattr(module, name):
|
|
786
789
|
obj = getattr(module, name)
|
|
787
790
|
if kind == "flock_callable" and callable(obj):
|
|
788
|
-
|
|
791
|
+
registry.register_callable(obj, name)
|
|
789
792
|
logger.info(
|
|
790
793
|
f"Registered callable '{name}' from file '{file_path}'"
|
|
791
794
|
)
|
|
792
795
|
elif kind == "flock_component" and isinstance(
|
|
793
796
|
obj, type
|
|
794
797
|
):
|
|
795
|
-
|
|
798
|
+
registry.register_component(obj, name)
|
|
796
799
|
logger.info(
|
|
797
800
|
f"Registered component '{name}' from file '{file_path}'"
|
|
798
801
|
)
|