flock-core 0.5.0b28__py3-none-any.whl → 0.5.56b0__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 +12 -217
- flock/agent.py +678 -0
- flock/api/themes.py +71 -0
- flock/artifacts.py +79 -0
- flock/cli.py +75 -0
- flock/components.py +173 -0
- flock/dashboard/__init__.py +28 -0
- flock/dashboard/collector.py +283 -0
- flock/dashboard/events.py +182 -0
- flock/dashboard/launcher.py +230 -0
- flock/dashboard/service.py +537 -0
- flock/dashboard/websocket.py +235 -0
- flock/engines/__init__.py +6 -0
- flock/engines/dspy_engine.py +856 -0
- flock/examples.py +128 -0
- flock/{core/util → helper}/cli_helper.py +4 -3
- flock/{core/logging → logging}/__init__.py +2 -3
- flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
- flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
- flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
- flock/{core/logging → logging}/logging.py +77 -61
- flock/{core/logging → logging}/telemetry.py +20 -26
- flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
- flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
- flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
- flock/{core/logging → logging}/trace_and_logged.py +20 -24
- flock/mcp/__init__.py +91 -0
- flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
- flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
- flock/mcp/manager.py +255 -0
- flock/mcp/servers/sse/__init__.py +1 -1
- flock/mcp/servers/sse/flock_sse_server.py +11 -53
- flock/mcp/servers/stdio/__init__.py +1 -1
- flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
- flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
- flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
- flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
- flock/mcp/types/__init__.py +42 -0
- flock/{core/mcp → mcp}/types/callbacks.py +9 -15
- flock/{core/mcp → mcp}/types/factories.py +7 -6
- flock/{core/mcp → mcp}/types/handlers.py +13 -18
- flock/{core/mcp → mcp}/types/types.py +70 -74
- flock/{core/mcp → mcp}/util/helpers.py +1 -1
- flock/orchestrator.py +645 -0
- flock/registry.py +148 -0
- flock/runtime.py +262 -0
- flock/service.py +140 -0
- flock/store.py +69 -0
- flock/subscription.py +111 -0
- flock/themes/andromeda.toml +1 -1
- flock/themes/apple-system-colors.toml +1 -1
- flock/themes/arcoiris.toml +1 -1
- flock/themes/atomonelight.toml +1 -1
- flock/themes/ayu copy.toml +1 -1
- flock/themes/ayu-light.toml +1 -1
- flock/themes/belafonte-day.toml +1 -1
- flock/themes/belafonte-night.toml +1 -1
- flock/themes/blulocodark.toml +1 -1
- flock/themes/breeze.toml +1 -1
- flock/themes/broadcast.toml +1 -1
- flock/themes/brogrammer.toml +1 -1
- flock/themes/builtin-dark.toml +1 -1
- flock/themes/builtin-pastel-dark.toml +1 -1
- flock/themes/catppuccin-latte.toml +1 -1
- flock/themes/catppuccin-macchiato.toml +1 -1
- flock/themes/catppuccin-mocha.toml +1 -1
- flock/themes/cga.toml +1 -1
- flock/themes/chalk.toml +1 -1
- flock/themes/ciapre.toml +1 -1
- flock/themes/coffee-theme.toml +1 -1
- flock/themes/cyberpunkscarletprotocol.toml +1 -1
- flock/themes/dark+.toml +1 -1
- flock/themes/darkermatrix.toml +1 -1
- flock/themes/darkside.toml +1 -1
- flock/themes/desert.toml +1 -1
- flock/themes/django.toml +1 -1
- flock/themes/djangosmooth.toml +1 -1
- flock/themes/doomone.toml +1 -1
- flock/themes/dotgov.toml +1 -1
- flock/themes/dracula+.toml +1 -1
- flock/themes/duckbones.toml +1 -1
- flock/themes/encom.toml +1 -1
- flock/themes/espresso.toml +1 -1
- flock/themes/everblush.toml +1 -1
- flock/themes/fairyfloss.toml +1 -1
- flock/themes/fideloper.toml +1 -1
- flock/themes/fishtank.toml +1 -1
- flock/themes/flexoki-light.toml +1 -1
- flock/themes/floraverse.toml +1 -1
- flock/themes/framer.toml +1 -1
- flock/themes/galizur.toml +1 -1
- flock/themes/github.toml +1 -1
- flock/themes/grass.toml +1 -1
- flock/themes/grey-green.toml +1 -1
- flock/themes/gruvboxlight.toml +1 -1
- flock/themes/guezwhoz.toml +1 -1
- flock/themes/harper.toml +1 -1
- flock/themes/hax0r-blue.toml +1 -1
- flock/themes/hopscotch.256.toml +1 -1
- flock/themes/ic-green-ppl.toml +1 -1
- flock/themes/iceberg-dark.toml +1 -1
- flock/themes/japanesque.toml +1 -1
- flock/themes/jubi.toml +1 -1
- flock/themes/kibble.toml +1 -1
- flock/themes/kolorit.toml +1 -1
- flock/themes/kurokula.toml +1 -1
- flock/themes/materialdesigncolors.toml +1 -1
- flock/themes/matrix.toml +1 -1
- flock/themes/mellifluous.toml +1 -1
- flock/themes/midnight-in-mojave.toml +1 -1
- flock/themes/monokai-remastered.toml +1 -1
- flock/themes/monokai-soda.toml +1 -1
- flock/themes/neon.toml +1 -1
- flock/themes/neopolitan.toml +1 -1
- flock/themes/nord-light.toml +1 -1
- flock/themes/ocean.toml +1 -1
- flock/themes/onehalfdark.toml +1 -1
- flock/themes/onehalflight.toml +1 -1
- flock/themes/palenighthc.toml +1 -1
- flock/themes/paulmillr.toml +1 -1
- flock/themes/pencildark.toml +1 -1
- flock/themes/pnevma.toml +1 -1
- flock/themes/purple-rain.toml +1 -1
- flock/themes/purplepeter.toml +1 -1
- flock/themes/raycast-dark.toml +1 -1
- flock/themes/red-sands.toml +1 -1
- flock/themes/relaxed.toml +1 -1
- flock/themes/retro.toml +1 -1
- flock/themes/rose-pine.toml +1 -1
- flock/themes/royal.toml +1 -1
- flock/themes/ryuuko.toml +1 -1
- flock/themes/sakura.toml +1 -1
- flock/themes/scarlet-protocol.toml +1 -1
- flock/themes/seoulbones-dark.toml +1 -1
- flock/themes/shades-of-purple.toml +1 -1
- flock/themes/smyck.toml +1 -1
- flock/themes/softserver.toml +1 -1
- flock/themes/solarized-darcula.toml +1 -1
- flock/themes/square.toml +1 -1
- flock/themes/sugarplum.toml +1 -1
- flock/themes/thayer-bright.toml +1 -1
- flock/themes/tokyonight.toml +1 -1
- flock/themes/tomorrow.toml +1 -1
- flock/themes/ubuntu.toml +1 -1
- flock/themes/ultradark.toml +1 -1
- flock/themes/ultraviolent.toml +1 -1
- flock/themes/unikitty.toml +1 -1
- flock/themes/urple.toml +1 -1
- flock/themes/vesper.toml +1 -1
- flock/themes/vimbones.toml +1 -1
- flock/themes/wildcherry.toml +1 -1
- flock/themes/wilmersdorf.toml +1 -1
- flock/themes/wryan.toml +1 -1
- flock/themes/xcodedarkhc.toml +1 -1
- flock/themes/xcodelight.toml +1 -1
- flock/themes/zenbones-light.toml +1 -1
- flock/themes/zenwritten-dark.toml +1 -1
- flock/utilities.py +301 -0
- flock/{components/utility → utility}/output_utility_component.py +68 -53
- flock/visibility.py +107 -0
- flock_core-0.5.56b0.dist-info/METADATA +747 -0
- flock_core-0.5.56b0.dist-info/RECORD +398 -0
- flock_core-0.5.56b0.dist-info/entry_points.txt +2 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/licenses/LICENSE +1 -1
- flock/adapter/__init__.py +0 -14
- flock/adapter/azure_adapter.py +0 -68
- flock/adapter/chroma_adapter.py +0 -73
- flock/adapter/faiss_adapter.py +0 -97
- flock/adapter/pinecone_adapter.py +0 -51
- flock/adapter/vector_base.py +0 -47
- flock/cli/assets/release_notes.md +0 -140
- flock/cli/config.py +0 -8
- flock/cli/constants.py +0 -36
- flock/cli/create_agent.py +0 -1
- flock/cli/create_flock.py +0 -280
- flock/cli/execute_flock.py +0 -620
- flock/cli/load_agent.py +0 -1
- flock/cli/load_examples.py +0 -1
- flock/cli/load_flock.py +0 -192
- flock/cli/load_release_notes.py +0 -20
- flock/cli/loaded_flock_cli.py +0 -254
- flock/cli/manage_agents.py +0 -459
- flock/cli/registry_management.py +0 -889
- flock/cli/runner.py +0 -41
- flock/cli/settings.py +0 -857
- flock/cli/utils.py +0 -135
- flock/cli/view_results.py +0 -29
- flock/cli/yaml_editor.py +0 -396
- flock/components/__init__.py +0 -30
- flock/components/evaluation/__init__.py +0 -9
- flock/components/evaluation/declarative_evaluation_component.py +0 -606
- flock/components/routing/__init__.py +0 -15
- flock/components/routing/conditional_routing_component.py +0 -494
- flock/components/routing/default_routing_component.py +0 -103
- flock/components/routing/llm_routing_component.py +0 -206
- flock/components/utility/__init__.py +0 -22
- flock/components/utility/example_utility_component.py +0 -250
- flock/components/utility/feedback_utility_component.py +0 -206
- flock/components/utility/memory_utility_component.py +0 -550
- flock/components/utility/metrics_utility_component.py +0 -700
- flock/config.py +0 -61
- flock/core/__init__.py +0 -110
- flock/core/agent/__init__.py +0 -16
- flock/core/agent/default_agent.py +0 -216
- flock/core/agent/flock_agent_components.py +0 -104
- flock/core/agent/flock_agent_execution.py +0 -101
- flock/core/agent/flock_agent_integration.py +0 -260
- flock/core/agent/flock_agent_lifecycle.py +0 -186
- flock/core/agent/flock_agent_serialization.py +0 -381
- flock/core/api/__init__.py +0 -10
- flock/core/api/custom_endpoint.py +0 -45
- flock/core/api/endpoints.py +0 -254
- flock/core/api/main.py +0 -162
- flock/core/api/models.py +0 -97
- flock/core/api/run_store.py +0 -224
- flock/core/api/runner.py +0 -44
- flock/core/api/service.py +0 -214
- flock/core/component/__init__.py +0 -15
- flock/core/component/agent_component_base.py +0 -309
- flock/core/component/evaluation_component.py +0 -62
- flock/core/component/routing_component.py +0 -74
- flock/core/component/utility_component.py +0 -69
- flock/core/config/flock_agent_config.py +0 -58
- flock/core/config/scheduled_agent_config.py +0 -40
- flock/core/context/context.py +0 -213
- flock/core/context/context_manager.py +0 -37
- flock/core/context/context_vars.py +0 -10
- flock/core/evaluation/utils.py +0 -396
- flock/core/execution/batch_executor.py +0 -369
- flock/core/execution/evaluation_executor.py +0 -438
- flock/core/execution/local_executor.py +0 -31
- flock/core/execution/opik_executor.py +0 -103
- flock/core/execution/temporal_executor.py +0 -164
- flock/core/flock.py +0 -634
- flock/core/flock_agent.py +0 -336
- flock/core/flock_factory.py +0 -613
- flock/core/flock_scheduler.py +0 -166
- flock/core/flock_server_manager.py +0 -136
- flock/core/interpreter/python_interpreter.py +0 -689
- flock/core/mcp/__init__.py +0 -1
- flock/core/mcp/flock_mcp_server.py +0 -680
- flock/core/mcp/mcp_client_manager.py +0 -201
- flock/core/mcp/types/__init__.py +0 -1
- flock/core/mixin/dspy_integration.py +0 -403
- flock/core/mixin/prompt_parser.py +0 -125
- flock/core/orchestration/__init__.py +0 -15
- flock/core/orchestration/flock_batch_processor.py +0 -94
- flock/core/orchestration/flock_evaluator.py +0 -113
- flock/core/orchestration/flock_execution.py +0 -295
- flock/core/orchestration/flock_initialization.py +0 -149
- flock/core/orchestration/flock_server_manager.py +0 -67
- flock/core/orchestration/flock_web_server.py +0 -117
- flock/core/registry/__init__.py +0 -45
- flock/core/registry/agent_registry.py +0 -69
- flock/core/registry/callable_registry.py +0 -139
- flock/core/registry/component_discovery.py +0 -142
- flock/core/registry/component_registry.py +0 -64
- flock/core/registry/config_mapping.py +0 -64
- flock/core/registry/decorators.py +0 -137
- flock/core/registry/registry_hub.py +0 -205
- flock/core/registry/server_registry.py +0 -57
- flock/core/registry/type_registry.py +0 -86
- flock/core/serialization/__init__.py +0 -13
- flock/core/serialization/callable_registry.py +0 -52
- flock/core/serialization/flock_serializer.py +0 -832
- flock/core/serialization/json_encoder.py +0 -41
- flock/core/serialization/secure_serializer.py +0 -175
- flock/core/serialization/serializable.py +0 -342
- flock/core/serialization/serialization_utils.py +0 -412
- flock/core/util/file_path_utils.py +0 -223
- flock/core/util/hydrator.py +0 -309
- flock/core/util/input_resolver.py +0 -164
- flock/core/util/loader.py +0 -59
- flock/core/util/splitter.py +0 -219
- flock/di.py +0 -27
- flock/platform/docker_tools.py +0 -49
- flock/platform/jaeger_install.py +0 -86
- flock/webapp/__init__.py +0 -1
- flock/webapp/app/__init__.py +0 -0
- flock/webapp/app/api/__init__.py +0 -0
- flock/webapp/app/api/agent_management.py +0 -241
- flock/webapp/app/api/execution.py +0 -709
- flock/webapp/app/api/flock_management.py +0 -129
- flock/webapp/app/api/registry_viewer.py +0 -30
- flock/webapp/app/chat.py +0 -665
- flock/webapp/app/config.py +0 -104
- flock/webapp/app/dependencies.py +0 -117
- flock/webapp/app/main.py +0 -1070
- flock/webapp/app/middleware.py +0 -113
- flock/webapp/app/models_ui.py +0 -7
- flock/webapp/app/services/__init__.py +0 -0
- flock/webapp/app/services/feedback_file_service.py +0 -363
- flock/webapp/app/services/flock_service.py +0 -337
- flock/webapp/app/services/sharing_models.py +0 -81
- flock/webapp/app/services/sharing_store.py +0 -762
- flock/webapp/app/templates/theme_mapper.html +0 -326
- flock/webapp/app/theme_mapper.py +0 -812
- flock/webapp/app/utils.py +0 -85
- flock/webapp/run.py +0 -215
- flock/webapp/static/css/chat.css +0 -301
- flock/webapp/static/css/components.css +0 -167
- flock/webapp/static/css/header.css +0 -39
- flock/webapp/static/css/layout.css +0 -46
- flock/webapp/static/css/sidebar.css +0 -127
- flock/webapp/static/css/two-pane.css +0 -48
- flock/webapp/templates/base.html +0 -200
- flock/webapp/templates/chat.html +0 -152
- flock/webapp/templates/chat_settings.html +0 -19
- flock/webapp/templates/flock_editor.html +0 -16
- flock/webapp/templates/index.html +0 -12
- flock/webapp/templates/partials/_agent_detail_form.html +0 -93
- flock/webapp/templates/partials/_agent_list.html +0 -18
- flock/webapp/templates/partials/_agent_manager_view.html +0 -51
- flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
- flock/webapp/templates/partials/_chat_container.html +0 -15
- flock/webapp/templates/partials/_chat_messages.html +0 -57
- flock/webapp/templates/partials/_chat_settings_form.html +0 -85
- flock/webapp/templates/partials/_create_flock_form.html +0 -50
- flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
- flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
- flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
- flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
- flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
- flock/webapp/templates/partials/_env_vars_table.html +0 -23
- flock/webapp/templates/partials/_execution_form.html +0 -118
- flock/webapp/templates/partials/_execution_view_container.html +0 -28
- flock/webapp/templates/partials/_flock_file_list.html +0 -23
- flock/webapp/templates/partials/_flock_properties_form.html +0 -52
- flock/webapp/templates/partials/_flock_upload_form.html +0 -16
- flock/webapp/templates/partials/_header_flock_status.html +0 -5
- flock/webapp/templates/partials/_load_manager_view.html +0 -49
- flock/webapp/templates/partials/_registry_table.html +0 -25
- flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
- flock/webapp/templates/partials/_results_display.html +0 -78
- flock/webapp/templates/partials/_settings_env_content.html +0 -9
- flock/webapp/templates/partials/_settings_theme_content.html +0 -14
- flock/webapp/templates/partials/_settings_view.html +0 -36
- flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
- flock/webapp/templates/partials/_share_link_snippet.html +0 -35
- flock/webapp/templates/partials/_sidebar.html +0 -74
- flock/webapp/templates/partials/_streaming_results_container.html +0 -195
- flock/webapp/templates/partials/_structured_data_view.html +0 -40
- flock/webapp/templates/partials/_theme_preview.html +0 -36
- flock/webapp/templates/registry_viewer.html +0 -84
- flock/webapp/templates/shared_run_page.html +0 -140
- flock/workflow/__init__.py +0 -0
- flock/workflow/activities.py +0 -196
- flock/workflow/agent_activities.py +0 -24
- flock/workflow/agent_execution_activity.py +0 -202
- flock/workflow/flock_workflow.py +0 -214
- flock/workflow/temporal_config.py +0 -96
- flock/workflow/temporal_setup.py +0 -68
- flock_core-0.5.0b28.dist-info/METADATA +0 -274
- flock_core-0.5.0b28.dist-info/RECORD +0 -561
- flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
- /flock/{core/logging → logging}/formatters/themes.py +0 -0
- /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
- /flock/{core/mcp → mcp}/util/__init__.py +0 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/WHEEL +0 -0
|
@@ -1,832 +0,0 @@
|
|
|
1
|
-
# src/flock/core/serialization/flock_serializer.py
|
|
2
|
-
"""Handles serialization and deserialization logic for Flock instances."""
|
|
3
|
-
|
|
4
|
-
import importlib
|
|
5
|
-
import importlib.util
|
|
6
|
-
import inspect
|
|
7
|
-
import os
|
|
8
|
-
import re
|
|
9
|
-
import sys
|
|
10
|
-
from dataclasses import is_dataclass
|
|
11
|
-
from typing import TYPE_CHECKING, Any, Literal
|
|
12
|
-
|
|
13
|
-
from pydantic import BaseModel, create_model
|
|
14
|
-
|
|
15
|
-
from flock.core.logging.logging import get_logger
|
|
16
|
-
|
|
17
|
-
# Need registry access
|
|
18
|
-
from flock.core.registry import get_registry
|
|
19
|
-
from flock.core.serialization.serialization_utils import (
|
|
20
|
-
# Assuming this handles basic serialization needs
|
|
21
|
-
extract_pydantic_models_from_type_string,
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
if TYPE_CHECKING:
|
|
25
|
-
from flock.core.flock import Flock
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
logger = get_logger("serialization.flock")
|
|
29
|
-
registry = get_registry()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class FlockSerializer:
|
|
33
|
-
"""Provides static methods for serializing and deserializing Flock instances."""
|
|
34
|
-
|
|
35
|
-
@staticmethod
|
|
36
|
-
def serialize(
|
|
37
|
-
flock_instance: "Flock",
|
|
38
|
-
path_type: Literal["absolute", "relative"] = "relative",
|
|
39
|
-
) -> dict[str, Any]:
|
|
40
|
-
"""Convert Flock instance to dictionary representation.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
flock_instance: The Flock instance to serialize.
|
|
44
|
-
path_type: How file paths should be formatted ('absolute' or 'relative').
|
|
45
|
-
"""
|
|
46
|
-
logger.debug(
|
|
47
|
-
f"Serializing Flock instance '{flock_instance.name}' to dict."
|
|
48
|
-
)
|
|
49
|
-
# Use Pydantic's dump for base fields defined in Flock's model
|
|
50
|
-
data = flock_instance.model_dump(mode="json", exclude_none=True)
|
|
51
|
-
logger.info(
|
|
52
|
-
f"Serializing Flock '{flock_instance.name}' with {len(flock_instance._agents)} agents"
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
data["agents"] = {}
|
|
56
|
-
data["mcp_servers"] = {}
|
|
57
|
-
custom_types = {}
|
|
58
|
-
components = {}
|
|
59
|
-
|
|
60
|
-
for name, server_instance in flock_instance._servers.items():
|
|
61
|
-
try:
|
|
62
|
-
# Servers handle their own serialization via their to_dict method
|
|
63
|
-
server_data = server_instance.to_dict(path_type=path_type)
|
|
64
|
-
data["mcp_servers"][name] = server_data
|
|
65
|
-
|
|
66
|
-
# --- Extract Component Information ---
|
|
67
|
-
|
|
68
|
-
# Modules
|
|
69
|
-
if "modules" in server_data:
|
|
70
|
-
for module_name, module_data in server_data[
|
|
71
|
-
"modules"
|
|
72
|
-
].items():
|
|
73
|
-
if module_data and "type" in module_data:
|
|
74
|
-
component_type = module_data["type"]
|
|
75
|
-
if component_type not in components:
|
|
76
|
-
logger.debug(
|
|
77
|
-
f"Adding module component '{component_type}' from module '{module_name}' in server '{name}'"
|
|
78
|
-
)
|
|
79
|
-
components[component_type] = (
|
|
80
|
-
FlockSerializer._get_component_definition(
|
|
81
|
-
component_type, path_type
|
|
82
|
-
)
|
|
83
|
-
)
|
|
84
|
-
except Exception as e:
|
|
85
|
-
logger.error(
|
|
86
|
-
f"Failed to serialize server '{name}' within Flock: {e}",
|
|
87
|
-
exc_info=True,
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
for name, agent_instance in flock_instance._agents.items():
|
|
91
|
-
try:
|
|
92
|
-
logger.debug(f"Serializing agent '{name}'")
|
|
93
|
-
# Agents handle their own serialization via their to_dict
|
|
94
|
-
agent_data = (
|
|
95
|
-
agent_instance.to_dict()
|
|
96
|
-
) # This now uses the agent's refined to_dict
|
|
97
|
-
data["agents"][name] = agent_data
|
|
98
|
-
|
|
99
|
-
# --- Extract Types from Agent Signatures ---
|
|
100
|
-
input_types = []
|
|
101
|
-
if agent_instance.input:
|
|
102
|
-
input_types = FlockSerializer._extract_types_from_signature(
|
|
103
|
-
agent_instance.input
|
|
104
|
-
)
|
|
105
|
-
if input_types:
|
|
106
|
-
logger.debug(
|
|
107
|
-
f"Found input types in agent '{name}': {input_types}"
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
output_types = []
|
|
111
|
-
if agent_instance.output:
|
|
112
|
-
output_types = (
|
|
113
|
-
FlockSerializer._extract_types_from_signature(
|
|
114
|
-
agent_instance.output
|
|
115
|
-
)
|
|
116
|
-
)
|
|
117
|
-
if output_types:
|
|
118
|
-
logger.debug(
|
|
119
|
-
f"Found output types in agent '{name}': {output_types}"
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
all_types = set(input_types + output_types)
|
|
123
|
-
if all_types:
|
|
124
|
-
custom_types.update(
|
|
125
|
-
FlockSerializer._get_type_definitions(list(all_types))
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
# --- Extract Component Information ---
|
|
129
|
-
# Evaluator
|
|
130
|
-
if (
|
|
131
|
-
"evaluator" in agent_data
|
|
132
|
-
and agent_data["evaluator"]
|
|
133
|
-
and "type" in agent_data["evaluator"]
|
|
134
|
-
):
|
|
135
|
-
component_type = agent_data["evaluator"]["type"]
|
|
136
|
-
if component_type not in components:
|
|
137
|
-
logger.debug(
|
|
138
|
-
f"Adding evaluator component '{component_type}' from agent '{name}'"
|
|
139
|
-
)
|
|
140
|
-
components[component_type] = (
|
|
141
|
-
FlockSerializer._get_component_definition(
|
|
142
|
-
component_type, path_type
|
|
143
|
-
)
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
# Modules
|
|
147
|
-
if "modules" in agent_data:
|
|
148
|
-
for module_name, module_data in agent_data[
|
|
149
|
-
"modules"
|
|
150
|
-
].items():
|
|
151
|
-
if module_data and "type" in module_data:
|
|
152
|
-
component_type = module_data["type"]
|
|
153
|
-
if component_type not in components:
|
|
154
|
-
logger.debug(
|
|
155
|
-
f"Adding module component '{component_type}' from module '{module_name}' in agent '{name}'"
|
|
156
|
-
)
|
|
157
|
-
components[component_type] = (
|
|
158
|
-
FlockSerializer._get_component_definition(
|
|
159
|
-
component_type, path_type
|
|
160
|
-
)
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
# Router
|
|
164
|
-
if (
|
|
165
|
-
"handoff_router" in agent_data
|
|
166
|
-
and agent_data["handoff_router"]
|
|
167
|
-
and "type" in agent_data["handoff_router"]
|
|
168
|
-
):
|
|
169
|
-
component_type = agent_data["handoff_router"]["type"]
|
|
170
|
-
if component_type not in components:
|
|
171
|
-
logger.debug(
|
|
172
|
-
f"Adding router component '{component_type}' from agent '{name}'"
|
|
173
|
-
)
|
|
174
|
-
components[component_type] = (
|
|
175
|
-
FlockSerializer._get_component_definition(
|
|
176
|
-
component_type, path_type
|
|
177
|
-
)
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# Description (Callables)
|
|
181
|
-
if agent_data.get("description_callable"):
|
|
182
|
-
logger.debug(
|
|
183
|
-
f"Adding description callable '{agent_data['description_callable']}' from agent '{name}'"
|
|
184
|
-
)
|
|
185
|
-
description_callable_name = agent_data[
|
|
186
|
-
"description_callable"
|
|
187
|
-
]
|
|
188
|
-
description_callable = agent_instance.description
|
|
189
|
-
path_str = registry.get_callable_path_string(
|
|
190
|
-
description_callable
|
|
191
|
-
)
|
|
192
|
-
if path_str:
|
|
193
|
-
logger.debug(
|
|
194
|
-
f"Adding description callable '{description_callable_name}' (from path '{path_str}') to components"
|
|
195
|
-
)
|
|
196
|
-
components[description_callable_name] = (
|
|
197
|
-
FlockSerializer._get_callable_definition(
|
|
198
|
-
path_str, description_callable_name, path_type
|
|
199
|
-
)
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
if agent_data.get("input_callable"):
|
|
203
|
-
logger.debug(
|
|
204
|
-
f"Adding input callable '{agent_data['input_callable']}' from agent '{name}'"
|
|
205
|
-
)
|
|
206
|
-
input_callable_name = agent_data["input_callable"]
|
|
207
|
-
input_callable = agent_instance.input
|
|
208
|
-
path_str = registry.get_callable_path_string(
|
|
209
|
-
input_callable
|
|
210
|
-
)
|
|
211
|
-
if path_str:
|
|
212
|
-
logger.debug(
|
|
213
|
-
f"Adding input callable '{input_callable_name}' (from path '{path_str}') to components"
|
|
214
|
-
)
|
|
215
|
-
components[input_callable_name] = (
|
|
216
|
-
FlockSerializer._get_callable_definition(
|
|
217
|
-
path_str, input_callable_name, path_type
|
|
218
|
-
)
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
if agent_data.get("output_callable"):
|
|
222
|
-
logger.debug(
|
|
223
|
-
f"Adding output callable '{agent_data['output_callable']}' from agent '{name}'"
|
|
224
|
-
)
|
|
225
|
-
output_callable_name = agent_data["output_callable"]
|
|
226
|
-
output_callable = agent_instance.output
|
|
227
|
-
path_str = registry.get_callable_path_string(
|
|
228
|
-
output_callable
|
|
229
|
-
)
|
|
230
|
-
if path_str:
|
|
231
|
-
logger.debug(
|
|
232
|
-
f"Adding output callable '{output_callable_name}' (from path '{path_str}') to components"
|
|
233
|
-
)
|
|
234
|
-
components[output_callable_name] = (
|
|
235
|
-
FlockSerializer._get_callable_definition(
|
|
236
|
-
path_str, output_callable_name, path_type
|
|
237
|
-
)
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
# Tools (Callables)
|
|
241
|
-
if agent_data.get("tools"):
|
|
242
|
-
logger.debug(
|
|
243
|
-
f"Extracting tool information from agent '{name}': {agent_data['tools']}"
|
|
244
|
-
)
|
|
245
|
-
tool_objs = (
|
|
246
|
-
agent_instance.tools if agent_instance.tools else []
|
|
247
|
-
)
|
|
248
|
-
for i, tool_name in enumerate(agent_data["tools"]):
|
|
249
|
-
if tool_name not in components and i < len(tool_objs):
|
|
250
|
-
tool = tool_objs[i]
|
|
251
|
-
if callable(tool) and not isinstance(tool, type):
|
|
252
|
-
path_str = (
|
|
253
|
-
registry.get_callable_path_string(tool)
|
|
254
|
-
)
|
|
255
|
-
if path_str:
|
|
256
|
-
logger.debug(
|
|
257
|
-
f"Adding tool '{tool_name}' (from path '{path_str}') to components"
|
|
258
|
-
)
|
|
259
|
-
components[tool_name] = (
|
|
260
|
-
FlockSerializer._get_callable_definition(
|
|
261
|
-
path_str, tool_name, path_type
|
|
262
|
-
)
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
except Exception as e:
|
|
266
|
-
logger.error(
|
|
267
|
-
f"Failed to serialize agent '{name}' within Flock: {e}",
|
|
268
|
-
exc_info=True,
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
if custom_types:
|
|
272
|
-
logger.info(f"Adding {len(custom_types)} custom type definitions")
|
|
273
|
-
data["types"] = custom_types
|
|
274
|
-
if components:
|
|
275
|
-
logger.info(
|
|
276
|
-
f"Adding {len(components)} component/callable definitions"
|
|
277
|
-
)
|
|
278
|
-
data["components"] = components
|
|
279
|
-
|
|
280
|
-
data["dependencies"] = FlockSerializer._get_dependencies()
|
|
281
|
-
data["metadata"] = {
|
|
282
|
-
"path_type": path_type,
|
|
283
|
-
"flock_version": "0.4.0",
|
|
284
|
-
} # Example version
|
|
285
|
-
|
|
286
|
-
logger.debug(f"Flock '{flock_instance.name}' serialization complete.")
|
|
287
|
-
return data
|
|
288
|
-
|
|
289
|
-
@staticmethod
|
|
290
|
-
def deserialize(cls: type["Flock"], data: dict[str, Any]) -> "Flock":
|
|
291
|
-
"""Create Flock instance from dictionary representation."""
|
|
292
|
-
# Import concrete types needed for instantiation
|
|
293
|
-
from flock.core.flock import Flock # Import the actual class
|
|
294
|
-
from flock.core.flock_agent import FlockAgent as ConcreteFlockAgent
|
|
295
|
-
from flock.core.mcp.flock_mcp_server import (
|
|
296
|
-
FlockMCPServer as ConcreteFlockMCPServer,
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
logger.debug(
|
|
300
|
-
f"Deserializing Flock from dict. Provided keys: {list(data.keys())}"
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
metadata = data.pop("metadata", {})
|
|
304
|
-
path_type = metadata.get(
|
|
305
|
-
"path_type", "relative"
|
|
306
|
-
) # Default to relative for loading flexibility
|
|
307
|
-
logger.debug(
|
|
308
|
-
f"Using path_type '{path_type}' from metadata for component loading"
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
if "types" in data:
|
|
312
|
-
logger.info(f"Processing {len(data['types'])} type definitions")
|
|
313
|
-
FlockSerializer._register_type_definitions(data.pop("types"))
|
|
314
|
-
|
|
315
|
-
if "components" in data:
|
|
316
|
-
logger.info(
|
|
317
|
-
f"Processing {len(data['components'])} component/callable definitions"
|
|
318
|
-
)
|
|
319
|
-
FlockSerializer._register_component_definitions(
|
|
320
|
-
data.pop("components"), path_type
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
if "dependencies" in data:
|
|
324
|
-
logger.debug(f"Checking {len(data['dependencies'])} dependencies")
|
|
325
|
-
FlockSerializer._check_dependencies(data.pop("dependencies"))
|
|
326
|
-
|
|
327
|
-
agents_data = data.pop("agents", {})
|
|
328
|
-
server_data = data.pop("mcp_servers", {})
|
|
329
|
-
logger.info(f"Found {len(server_data)} servers to deserialize")
|
|
330
|
-
logger.info(f"Found {len(agents_data)} agents to deserialize")
|
|
331
|
-
|
|
332
|
-
try:
|
|
333
|
-
# Pass only fields defined in Flock's Pydantic model to constructor
|
|
334
|
-
init_data = {
|
|
335
|
-
k: v for k, v in data.items() if k in Flock.model_fields
|
|
336
|
-
}
|
|
337
|
-
logger.debug(
|
|
338
|
-
f"Creating Flock instance with fields: {list(init_data.keys())}"
|
|
339
|
-
)
|
|
340
|
-
flock_instance = cls(**init_data) # Use cls which is Flock
|
|
341
|
-
except Exception as e:
|
|
342
|
-
logger.error(
|
|
343
|
-
f"Pydantic validation/init failed for Flock: {e}", exc_info=True
|
|
344
|
-
)
|
|
345
|
-
raise ValueError(
|
|
346
|
-
f"Failed to initialize Flock from dict: {e}"
|
|
347
|
-
) from e
|
|
348
|
-
|
|
349
|
-
# Deserialize and add server AFTER Flock instance exists and BEFORE Agents have been added
|
|
350
|
-
for name, server_data in server_data.items():
|
|
351
|
-
try:
|
|
352
|
-
logger.debug(f"Deserializing server '{name}'")
|
|
353
|
-
server_data.setdefault("name", name)
|
|
354
|
-
server_instance = ConcreteFlockMCPServer.from_dict(server_data)
|
|
355
|
-
flock_instance.add_server(server_instance)
|
|
356
|
-
logger.debug(f"Successfully added server '{name}' to Flock")
|
|
357
|
-
except Exception as e:
|
|
358
|
-
logger.error(
|
|
359
|
-
f"Failed to deserialize/add server '{name}': {e}",
|
|
360
|
-
exc_info=True,
|
|
361
|
-
)
|
|
362
|
-
|
|
363
|
-
# Deserialize and add agents AFTER Flock instance exists
|
|
364
|
-
for name, agent_data in agents_data.items():
|
|
365
|
-
try:
|
|
366
|
-
logger.debug(f"Deserializing agent '{name}'")
|
|
367
|
-
agent_data.setdefault("name", name)
|
|
368
|
-
agent_instance = ConcreteFlockAgent.from_dict(agent_data)
|
|
369
|
-
flock_instance.add_agent(agent_instance)
|
|
370
|
-
logger.debug(f"Successfully added agent '{name}' to Flock")
|
|
371
|
-
except Exception as e:
|
|
372
|
-
logger.error(
|
|
373
|
-
f"Failed to deserialize/add agent '{name}': {e}",
|
|
374
|
-
exc_info=True,
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
logger.info(
|
|
378
|
-
f"Successfully deserialized Flock '{flock_instance.name}' with {len(flock_instance._agents)} agents"
|
|
379
|
-
)
|
|
380
|
-
return flock_instance
|
|
381
|
-
|
|
382
|
-
# --- Helper methods moved from Flock ---
|
|
383
|
-
# (Keep all the _extract..., _get..., _register..., _create... methods here)
|
|
384
|
-
# Ensure they use FlockSerializer._... or are standalone functions called directly.
|
|
385
|
-
# Make static if they don't need instance state (which they shouldn't here).
|
|
386
|
-
|
|
387
|
-
@staticmethod
|
|
388
|
-
def _extract_types_from_signature(signature: str) -> list[str]:
|
|
389
|
-
"""Extract type names from an input/output signature string."""
|
|
390
|
-
if not signature:
|
|
391
|
-
return []
|
|
392
|
-
from flock.core.util.input_resolver import (
|
|
393
|
-
split_top_level, # Import locally if needed
|
|
394
|
-
)
|
|
395
|
-
|
|
396
|
-
type_names = set()
|
|
397
|
-
try:
|
|
398
|
-
parts = split_top_level(signature)
|
|
399
|
-
for part in parts:
|
|
400
|
-
if ":" in part:
|
|
401
|
-
type_str = part.split(":", 1)[1].split("|", 1)[0].strip()
|
|
402
|
-
# Use the more robust extractor
|
|
403
|
-
models = extract_pydantic_models_from_type_string(type_str)
|
|
404
|
-
for model in models:
|
|
405
|
-
type_names.add(model.__name__)
|
|
406
|
-
except Exception as e:
|
|
407
|
-
logger.warning(
|
|
408
|
-
f"Could not fully parse types from signature '{signature}': {e}"
|
|
409
|
-
)
|
|
410
|
-
return list(type_names)
|
|
411
|
-
|
|
412
|
-
@staticmethod
|
|
413
|
-
def _get_type_definitions(type_names: list[str]) -> dict[str, Any]:
|
|
414
|
-
"""Get definitions for the specified custom types from the registry."""
|
|
415
|
-
type_definitions = {}
|
|
416
|
-
for type_name in type_names:
|
|
417
|
-
try:
|
|
418
|
-
type_obj = registry.get_type(
|
|
419
|
-
type_name
|
|
420
|
-
) # Throws KeyError if not found
|
|
421
|
-
type_def = FlockSerializer._extract_type_definition(
|
|
422
|
-
type_name, type_obj
|
|
423
|
-
)
|
|
424
|
-
if type_def:
|
|
425
|
-
type_definitions[type_name] = type_def
|
|
426
|
-
except KeyError:
|
|
427
|
-
logger.warning(
|
|
428
|
-
f"Type '{type_name}' requested but not found in registry."
|
|
429
|
-
)
|
|
430
|
-
except Exception as e:
|
|
431
|
-
logger.warning(
|
|
432
|
-
f"Could not extract definition for type {type_name}: {e}"
|
|
433
|
-
)
|
|
434
|
-
return type_definitions
|
|
435
|
-
|
|
436
|
-
@staticmethod
|
|
437
|
-
def _extract_type_definition(
|
|
438
|
-
type_name: str, type_obj: type
|
|
439
|
-
) -> dict[str, Any] | None:
|
|
440
|
-
"""Extract a definition for a custom type (Pydantic or Dataclass)."""
|
|
441
|
-
# Definition includes module path and schema/fields
|
|
442
|
-
module_path = getattr(type_obj, "__module__", "unknown")
|
|
443
|
-
type_def = {"module_path": module_path}
|
|
444
|
-
try:
|
|
445
|
-
if issubclass(type_obj, BaseModel):
|
|
446
|
-
type_def["type"] = "pydantic.BaseModel"
|
|
447
|
-
schema = type_obj.model_json_schema()
|
|
448
|
-
if "title" in schema and schema["title"] == type_name:
|
|
449
|
-
del schema["title"]
|
|
450
|
-
type_def["schema"] = schema
|
|
451
|
-
return type_def
|
|
452
|
-
elif is_dataclass(type_obj):
|
|
453
|
-
type_def["type"] = "dataclass"
|
|
454
|
-
fields = {}
|
|
455
|
-
for field_name, field in getattr(
|
|
456
|
-
type_obj, "__dataclass_fields__", {}
|
|
457
|
-
).items():
|
|
458
|
-
# Attempt to get a string representation of the type
|
|
459
|
-
try:
|
|
460
|
-
type_repr = str(field.type)
|
|
461
|
-
except Exception:
|
|
462
|
-
type_repr = "unknown"
|
|
463
|
-
fields[field_name] = {
|
|
464
|
-
"type": type_repr,
|
|
465
|
-
"default": str(field.default)
|
|
466
|
-
if field.default is not inspect.Parameter.empty
|
|
467
|
-
else None,
|
|
468
|
-
}
|
|
469
|
-
type_def["fields"] = fields
|
|
470
|
-
return type_def
|
|
471
|
-
else:
|
|
472
|
-
logger.debug(
|
|
473
|
-
f"Type '{type_name}' is not Pydantic or Dataclass, skipping detailed definition."
|
|
474
|
-
)
|
|
475
|
-
return (
|
|
476
|
-
None # Don't include non-data types in the 'types' section
|
|
477
|
-
)
|
|
478
|
-
except Exception as e:
|
|
479
|
-
logger.warning(f"Error extracting definition for {type_name}: {e}")
|
|
480
|
-
return None
|
|
481
|
-
|
|
482
|
-
@staticmethod
|
|
483
|
-
def _get_component_definition(
|
|
484
|
-
component_type_name: str, path_type: Literal["absolute", "relative"]
|
|
485
|
-
) -> dict[str, Any]:
|
|
486
|
-
"""Get definition for a component type from the registry."""
|
|
487
|
-
component_def = {
|
|
488
|
-
"type": "flock_component",
|
|
489
|
-
"module_path": "unknown",
|
|
490
|
-
"file_path": None,
|
|
491
|
-
}
|
|
492
|
-
try:
|
|
493
|
-
component_class = registry.get_component(
|
|
494
|
-
component_type_name
|
|
495
|
-
) # Raises KeyError if not found
|
|
496
|
-
component_def["module_path"] = getattr(
|
|
497
|
-
component_class, "__module__", "unknown"
|
|
498
|
-
)
|
|
499
|
-
component_def["description"] = (
|
|
500
|
-
inspect.getdoc(component_class)
|
|
501
|
-
or f"{component_type_name} component"
|
|
502
|
-
)
|
|
503
|
-
|
|
504
|
-
# Get file path
|
|
505
|
-
try:
|
|
506
|
-
file_path_abs = inspect.getfile(component_class)
|
|
507
|
-
component_def["file_path"] = (
|
|
508
|
-
os.path.relpath(file_path_abs)
|
|
509
|
-
if path_type == "relative"
|
|
510
|
-
else file_path_abs
|
|
511
|
-
)
|
|
512
|
-
except (TypeError, ValueError) as e:
|
|
513
|
-
logger.debug(
|
|
514
|
-
f"Could not determine file path for component {component_type_name}: {e}"
|
|
515
|
-
)
|
|
516
|
-
|
|
517
|
-
except KeyError:
|
|
518
|
-
logger.warning(
|
|
519
|
-
f"Component class '{component_type_name}' not found in registry."
|
|
520
|
-
)
|
|
521
|
-
component_def["description"] = (
|
|
522
|
-
f"{component_type_name} component (class not found in registry)"
|
|
523
|
-
)
|
|
524
|
-
except Exception as e:
|
|
525
|
-
logger.warning(
|
|
526
|
-
f"Could not extract full definition for component {component_type_name}: {e}"
|
|
527
|
-
)
|
|
528
|
-
return component_def
|
|
529
|
-
|
|
530
|
-
@staticmethod
|
|
531
|
-
def _get_callable_definition(
|
|
532
|
-
callable_path: str,
|
|
533
|
-
func_name: str,
|
|
534
|
-
path_type: Literal["absolute", "relative"],
|
|
535
|
-
) -> dict[str, Any]:
|
|
536
|
-
"""Get definition for a callable using its registry path."""
|
|
537
|
-
callable_def = {
|
|
538
|
-
"type": "flock_callable",
|
|
539
|
-
"module_path": "unknown",
|
|
540
|
-
"file_path": None,
|
|
541
|
-
}
|
|
542
|
-
try:
|
|
543
|
-
func = registry.get_callable(
|
|
544
|
-
callable_path
|
|
545
|
-
) # Raises KeyError if not found
|
|
546
|
-
callable_def["module_path"] = getattr(func, "__module__", "unknown")
|
|
547
|
-
callable_def["description"] = (
|
|
548
|
-
inspect.getdoc(func) or f"Callable function {func_name}"
|
|
549
|
-
)
|
|
550
|
-
# Get file path
|
|
551
|
-
try:
|
|
552
|
-
file_path_abs = inspect.getfile(func)
|
|
553
|
-
callable_def["file_path"] = (
|
|
554
|
-
os.path.relpath(file_path_abs)
|
|
555
|
-
if path_type == "relative"
|
|
556
|
-
else file_path_abs
|
|
557
|
-
)
|
|
558
|
-
except (TypeError, ValueError) as e:
|
|
559
|
-
logger.debug(
|
|
560
|
-
f"Could not determine file path for callable {callable_path}: {e}"
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
except KeyError:
|
|
564
|
-
logger.warning(
|
|
565
|
-
f"Callable '{callable_path}' (for tool '{func_name}') not found in registry."
|
|
566
|
-
)
|
|
567
|
-
callable_def["description"] = (
|
|
568
|
-
f"Callable {func_name} (function not found in registry)"
|
|
569
|
-
)
|
|
570
|
-
except Exception as e:
|
|
571
|
-
logger.warning(
|
|
572
|
-
f"Could not extract full definition for callable {callable_path}: {e}"
|
|
573
|
-
)
|
|
574
|
-
return callable_def
|
|
575
|
-
|
|
576
|
-
@staticmethod
|
|
577
|
-
def _get_dependencies() -> list[str]:
|
|
578
|
-
"""Get list of core dependencies required by Flock."""
|
|
579
|
-
# Basic static list for now
|
|
580
|
-
return [
|
|
581
|
-
"pydantic>=2.0.0",
|
|
582
|
-
"flock-core>=0.4.0",
|
|
583
|
-
] # Update version as needed
|
|
584
|
-
|
|
585
|
-
@staticmethod
|
|
586
|
-
def _register_type_definitions(type_defs: dict[str, Any]) -> None:
|
|
587
|
-
"""Register type definitions from serialized data."""
|
|
588
|
-
# (Logic remains largely the same as original, ensure it uses FlockRegistry)
|
|
589
|
-
for type_name, type_def in type_defs.items():
|
|
590
|
-
logger.debug(f"Registering type definition for: {type_name}")
|
|
591
|
-
# Prioritize direct import
|
|
592
|
-
module_path = type_def.get("module_path")
|
|
593
|
-
registered = False
|
|
594
|
-
if module_path and module_path != "unknown":
|
|
595
|
-
try:
|
|
596
|
-
module = importlib.import_module(module_path)
|
|
597
|
-
if hasattr(module, type_name):
|
|
598
|
-
type_obj = getattr(module, type_name)
|
|
599
|
-
registry.register_type(type_obj, type_name)
|
|
600
|
-
logger.info(
|
|
601
|
-
f"Registered type '{type_name}' from module '{module_path}'"
|
|
602
|
-
)
|
|
603
|
-
registered = True
|
|
604
|
-
except ImportError:
|
|
605
|
-
logger.debug(
|
|
606
|
-
f"Could not import module {module_path} for type {type_name}"
|
|
607
|
-
)
|
|
608
|
-
except Exception as e:
|
|
609
|
-
logger.warning(
|
|
610
|
-
f"Error registering type {type_name} from module: {e}"
|
|
611
|
-
)
|
|
612
|
-
|
|
613
|
-
if registered:
|
|
614
|
-
continue
|
|
615
|
-
|
|
616
|
-
# Attempt dynamic creation if direct import failed or wasn't possible
|
|
617
|
-
type_kind = type_def.get("type")
|
|
618
|
-
if type_kind == "pydantic.BaseModel" and "schema" in type_def:
|
|
619
|
-
FlockSerializer._create_pydantic_model(type_name, type_def)
|
|
620
|
-
elif type_kind == "dataclass" and "fields" in type_def:
|
|
621
|
-
FlockSerializer._create_dataclass(type_name, type_def)
|
|
622
|
-
else:
|
|
623
|
-
logger.warning(
|
|
624
|
-
f"Cannot dynamically register type '{type_name}' with kind '{type_kind}'"
|
|
625
|
-
)
|
|
626
|
-
|
|
627
|
-
@staticmethod
|
|
628
|
-
def _create_pydantic_model(
|
|
629
|
-
type_name: str, type_def: dict[str, Any]
|
|
630
|
-
) -> None:
|
|
631
|
-
"""Dynamically create and register a Pydantic model from schema."""
|
|
632
|
-
# (Logic remains the same, ensure it uses registry.register_type)
|
|
633
|
-
schema = type_def.get("schema", {})
|
|
634
|
-
try:
|
|
635
|
-
fields = {}
|
|
636
|
-
properties = schema.get("properties", {})
|
|
637
|
-
required = schema.get("required", [])
|
|
638
|
-
for field_name, field_schema in properties.items():
|
|
639
|
-
field_type = FlockSerializer._get_type_from_schema(field_schema)
|
|
640
|
-
default = ... if field_name in required else None
|
|
641
|
-
fields[field_name] = (field_type, default)
|
|
642
|
-
|
|
643
|
-
DynamicModel = create_model(type_name, **fields)
|
|
644
|
-
registry.register_type(DynamicModel, type_name)
|
|
645
|
-
logger.info(
|
|
646
|
-
f"Dynamically created and registered Pydantic model: {type_name}"
|
|
647
|
-
)
|
|
648
|
-
except Exception as e:
|
|
649
|
-
logger.error(f"Failed to create Pydantic model {type_name}: {e}")
|
|
650
|
-
|
|
651
|
-
@staticmethod
|
|
652
|
-
def _get_type_from_schema(field_schema: dict[str, Any]) -> Any:
|
|
653
|
-
"""Convert JSON schema type to Python type."""
|
|
654
|
-
# (Logic remains the same)
|
|
655
|
-
schema_type = field_schema.get("type")
|
|
656
|
-
type_mapping = {
|
|
657
|
-
"string": str,
|
|
658
|
-
"integer": int,
|
|
659
|
-
"number": float,
|
|
660
|
-
"boolean": bool,
|
|
661
|
-
"array": list,
|
|
662
|
-
"object": dict,
|
|
663
|
-
}
|
|
664
|
-
if schema_type in type_mapping:
|
|
665
|
-
return type_mapping[schema_type]
|
|
666
|
-
if "enum" in field_schema:
|
|
667
|
-
from typing import Literal
|
|
668
|
-
|
|
669
|
-
return Literal[tuple(field_schema["enum"])] # type: ignore
|
|
670
|
-
return Any
|
|
671
|
-
|
|
672
|
-
@staticmethod
|
|
673
|
-
def _create_dataclass(type_name: str, type_def: dict[str, Any]) -> None:
|
|
674
|
-
"""Dynamically create and register a dataclass."""
|
|
675
|
-
# (Logic remains the same, ensure it uses registry.register_type)
|
|
676
|
-
from dataclasses import make_dataclass
|
|
677
|
-
|
|
678
|
-
fields_def = type_def.get("fields", {})
|
|
679
|
-
try:
|
|
680
|
-
fields = []
|
|
681
|
-
for field_name, field_props in fields_def.items():
|
|
682
|
-
# Safely map type strings to actual types
|
|
683
|
-
field_type_str = field_props.get("type", "str")
|
|
684
|
-
type_mapping = {
|
|
685
|
-
"str": str,
|
|
686
|
-
"int": int,
|
|
687
|
-
"float": float,
|
|
688
|
-
"bool": bool,
|
|
689
|
-
"list": list,
|
|
690
|
-
"dict": dict,
|
|
691
|
-
"List": list,
|
|
692
|
-
"Dict": dict,
|
|
693
|
-
"Any": Any,
|
|
694
|
-
}
|
|
695
|
-
field_type = type_mapping.get(field_type_str, Any)
|
|
696
|
-
fields.append((field_name, field_type))
|
|
697
|
-
|
|
698
|
-
DynamicDataclass = make_dataclass(type_name, fields)
|
|
699
|
-
registry.register_type(DynamicDataclass, type_name)
|
|
700
|
-
logger.info(
|
|
701
|
-
f"Dynamically created and registered dataclass: {type_name}"
|
|
702
|
-
)
|
|
703
|
-
except Exception as e:
|
|
704
|
-
logger.error(f"Failed to create dataclass {type_name}: {e}")
|
|
705
|
-
|
|
706
|
-
@staticmethod
|
|
707
|
-
def _register_component_definitions(
|
|
708
|
-
component_defs: dict[str, Any],
|
|
709
|
-
path_type: Literal["absolute", "relative"],
|
|
710
|
-
) -> None:
|
|
711
|
-
"""Register component/callable definitions from serialized data."""
|
|
712
|
-
# (Logic remains the same, ensure it uses registry.register_component/register_callable)
|
|
713
|
-
# Key change: Ensure file_path is handled correctly based on path_type from metadata
|
|
714
|
-
for name, comp_def in component_defs.items():
|
|
715
|
-
logger.debug(
|
|
716
|
-
f"Registering component/callable definition for: {name}"
|
|
717
|
-
)
|
|
718
|
-
kind = comp_def.get("type")
|
|
719
|
-
module_path = comp_def.get("module_path")
|
|
720
|
-
file_path = comp_def.get("file_path")
|
|
721
|
-
registered = False
|
|
722
|
-
|
|
723
|
-
# Resolve file path if relative
|
|
724
|
-
if (
|
|
725
|
-
path_type == "relative"
|
|
726
|
-
and file_path
|
|
727
|
-
and not os.path.isabs(file_path)
|
|
728
|
-
):
|
|
729
|
-
abs_file_path = os.path.abspath(file_path)
|
|
730
|
-
logger.debug(
|
|
731
|
-
f"Resolved relative path '{file_path}' to absolute '{abs_file_path}'"
|
|
732
|
-
)
|
|
733
|
-
file_path = abs_file_path # Use absolute path for loading
|
|
734
|
-
|
|
735
|
-
# 1. Try importing from module_path
|
|
736
|
-
if module_path and module_path != "unknown":
|
|
737
|
-
try:
|
|
738
|
-
module = importlib.import_module(module_path)
|
|
739
|
-
if hasattr(module, name):
|
|
740
|
-
obj = getattr(module, name)
|
|
741
|
-
if kind == "flock_callable" and callable(obj):
|
|
742
|
-
registry.register_callable(
|
|
743
|
-
obj, name
|
|
744
|
-
) # Register by simple name
|
|
745
|
-
# Also register by full path if possible
|
|
746
|
-
full_path = f"{module_path}.{name}"
|
|
747
|
-
if full_path != name:
|
|
748
|
-
registry.register_callable(obj, full_path)
|
|
749
|
-
logger.info(
|
|
750
|
-
f"Registered callable '{name}' from module '{module_path}'"
|
|
751
|
-
)
|
|
752
|
-
registered = True
|
|
753
|
-
elif kind == "flock_component" and isinstance(
|
|
754
|
-
obj, type
|
|
755
|
-
):
|
|
756
|
-
registry.register_component(obj, name)
|
|
757
|
-
logger.info(
|
|
758
|
-
f"Registered component '{name}' from module '{module_path}'"
|
|
759
|
-
)
|
|
760
|
-
registered = True
|
|
761
|
-
except (ImportError, AttributeError):
|
|
762
|
-
logger.debug(
|
|
763
|
-
f"Could not import '{name}' from module '{module_path}', trying file path."
|
|
764
|
-
)
|
|
765
|
-
except Exception as e:
|
|
766
|
-
logger.warning(
|
|
767
|
-
f"Error registering '{name}' from module '{module_path}': {e}"
|
|
768
|
-
)
|
|
769
|
-
|
|
770
|
-
if registered:
|
|
771
|
-
continue
|
|
772
|
-
|
|
773
|
-
# 2. Try importing from file_path if module import failed or wasn't possible
|
|
774
|
-
if file_path and os.path.exists(file_path):
|
|
775
|
-
logger.debug(
|
|
776
|
-
f"Attempting to load '{name}' from file: {file_path}"
|
|
777
|
-
)
|
|
778
|
-
try:
|
|
779
|
-
mod_name = f"flock_dynamic_{name}" # Unique module name
|
|
780
|
-
spec = importlib.util.spec_from_file_location(
|
|
781
|
-
mod_name, file_path
|
|
782
|
-
)
|
|
783
|
-
if spec and spec.loader:
|
|
784
|
-
module = importlib.util.module_from_spec(spec)
|
|
785
|
-
sys.modules[spec.name] = (
|
|
786
|
-
module # Important for pickle/cloudpickle
|
|
787
|
-
)
|
|
788
|
-
spec.loader.exec_module(module)
|
|
789
|
-
if hasattr(module, name):
|
|
790
|
-
obj = getattr(module, name)
|
|
791
|
-
if kind == "flock_callable" and callable(obj):
|
|
792
|
-
registry.register_callable(obj, name)
|
|
793
|
-
logger.info(
|
|
794
|
-
f"Registered callable '{name}' from file '{file_path}'"
|
|
795
|
-
)
|
|
796
|
-
elif kind == "flock_component" and isinstance(
|
|
797
|
-
obj, type
|
|
798
|
-
):
|
|
799
|
-
registry.register_component(obj, name)
|
|
800
|
-
logger.info(
|
|
801
|
-
f"Registered component '{name}' from file '{file_path}'"
|
|
802
|
-
)
|
|
803
|
-
else:
|
|
804
|
-
logger.warning(
|
|
805
|
-
f"'{name}' not found in loaded file '{file_path}'"
|
|
806
|
-
)
|
|
807
|
-
else:
|
|
808
|
-
logger.warning(
|
|
809
|
-
f"Could not create import spec for file '{file_path}'"
|
|
810
|
-
)
|
|
811
|
-
except Exception as e:
|
|
812
|
-
logger.error(
|
|
813
|
-
f"Error loading '{name}' from file '{file_path}': {e}",
|
|
814
|
-
exc_info=True,
|
|
815
|
-
)
|
|
816
|
-
elif not registered:
|
|
817
|
-
logger.warning(
|
|
818
|
-
f"Could not register '{name}'. No valid module or file path found."
|
|
819
|
-
)
|
|
820
|
-
|
|
821
|
-
@staticmethod
|
|
822
|
-
def _check_dependencies(dependencies: list[str]) -> None:
|
|
823
|
-
"""Check if required dependencies are available (basic check)."""
|
|
824
|
-
# (Logic remains the same)
|
|
825
|
-
for dep in dependencies:
|
|
826
|
-
match = re.match(r"([^>=<]+)", dep)
|
|
827
|
-
if match:
|
|
828
|
-
pkg_name = match.group(1).replace("-", "_")
|
|
829
|
-
try:
|
|
830
|
-
importlib.import_module(pkg_name)
|
|
831
|
-
except ImportError:
|
|
832
|
-
logger.warning(f"Dependency '{dep}' might be missing.")
|