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,412 +0,0 @@
|
|
|
1
|
-
# src/flock/core/serialization/serialization_utils.py
|
|
2
|
-
"""Utilities for recursive serialization/deserialization with callable handling."""
|
|
3
|
-
|
|
4
|
-
import ast
|
|
5
|
-
import builtins
|
|
6
|
-
import importlib
|
|
7
|
-
import sys
|
|
8
|
-
import types
|
|
9
|
-
import typing
|
|
10
|
-
from collections.abc import Mapping, Sequence
|
|
11
|
-
from enum import Enum
|
|
12
|
-
from typing import (
|
|
13
|
-
TYPE_CHECKING,
|
|
14
|
-
Any,
|
|
15
|
-
Literal,
|
|
16
|
-
Union,
|
|
17
|
-
get_args,
|
|
18
|
-
get_origin,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
from pydantic import BaseModel
|
|
22
|
-
|
|
23
|
-
# Use TYPE_CHECKING to avoid circular imports
|
|
24
|
-
if TYPE_CHECKING:
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
from flock.core.registry import get_registry
|
|
28
|
-
from flock.core.logging.logging import get_logger
|
|
29
|
-
|
|
30
|
-
logger = get_logger("serialization.utils")
|
|
31
|
-
|
|
32
|
-
# Remove this line to avoid circular import at module level
|
|
33
|
-
# registry = get_registry() # Get singleton instance
|
|
34
|
-
|
|
35
|
-
# --- Serialization Helper ---
|
|
36
|
-
|
|
37
|
-
# src/flock/util/hydrator.py (or import from serialization_utils)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _format_type_to_string(type_hint: type) -> str:
|
|
41
|
-
"""Converts a Python type object back into its string representation."""
|
|
42
|
-
# This needs to handle various typing scenarios (List, Dict, Union, Optional, Literal, custom types)
|
|
43
|
-
origin = typing.get_origin(type_hint)
|
|
44
|
-
args = typing.get_args(type_hint)
|
|
45
|
-
|
|
46
|
-
# Handle common cases first
|
|
47
|
-
if origin is list or origin is list:
|
|
48
|
-
if args:
|
|
49
|
-
return f"list[{_format_type_to_string(args[0])}]"
|
|
50
|
-
return "list[Any]" # Or just "list"
|
|
51
|
-
elif origin is dict or origin is dict:
|
|
52
|
-
if args and len(args) == 2:
|
|
53
|
-
return f"dict[{_format_type_to_string(args[0])}, {_format_type_to_string(args[1])}]"
|
|
54
|
-
return "dict[Any, Any]" # Or just "dict"
|
|
55
|
-
elif origin is Union or origin is types.UnionType:
|
|
56
|
-
# Handle Optional[T] as Union[T, NoneType]
|
|
57
|
-
if len(args) == 2 and type(None) in args:
|
|
58
|
-
inner_type = next(t for t in args if t is not type(None))
|
|
59
|
-
return _format_type_to_string(inner_type)
|
|
60
|
-
# return f"Optional[{_format_type_to_string(inner_type)}]"
|
|
61
|
-
return (
|
|
62
|
-
f"Union[{', '.join(_format_type_to_string(arg) for arg in args)}]"
|
|
63
|
-
)
|
|
64
|
-
elif origin is Literal:
|
|
65
|
-
formatted_args = []
|
|
66
|
-
for arg in args:
|
|
67
|
-
if isinstance(arg, str):
|
|
68
|
-
formatted_args.append(f"'{arg}'")
|
|
69
|
-
else:
|
|
70
|
-
formatted_args.append(str(arg))
|
|
71
|
-
return f"Literal[{', '.join(formatted_args)}]"
|
|
72
|
-
elif hasattr(
|
|
73
|
-
type_hint, "__forward_arg__"
|
|
74
|
-
): # Handle ForwardRefs if necessary
|
|
75
|
-
return type_hint.__forward_arg__
|
|
76
|
-
elif hasattr(type_hint, "__name__"):
|
|
77
|
-
# Handle custom types registered in registry (get preferred name)
|
|
78
|
-
registry = get_registry()
|
|
79
|
-
try:
|
|
80
|
-
# Prefer explicit type registration names when available
|
|
81
|
-
for name, reg_type in registry.types.get_all_types().items():
|
|
82
|
-
if reg_type == type_hint:
|
|
83
|
-
return name
|
|
84
|
-
except Exception:
|
|
85
|
-
# Defensive: if registry helper is unavailable, fall back below
|
|
86
|
-
pass
|
|
87
|
-
return type_hint.__name__ # Fallback to class name if not registered
|
|
88
|
-
else:
|
|
89
|
-
# Fallback for complex types or types not handled above
|
|
90
|
-
type_repr = str(type_hint).replace("typing.", "") # Basic cleanup
|
|
91
|
-
type_repr = str(type_hint).replace("| None", "")
|
|
92
|
-
type_repr = type_repr.strip()
|
|
93
|
-
logger.debug(
|
|
94
|
-
f"Using fallback string representation for type: {type_repr}"
|
|
95
|
-
)
|
|
96
|
-
return type_repr
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def extract_identifiers_from_type_str(type_str: str) -> set[str]:
|
|
100
|
-
"""Extract all identifiers from a type annotation string using the AST."""
|
|
101
|
-
tree = ast.parse(type_str, mode="eval")
|
|
102
|
-
identifiers = set()
|
|
103
|
-
|
|
104
|
-
class IdentifierVisitor(ast.NodeVisitor):
|
|
105
|
-
def visit_Name(self, node):
|
|
106
|
-
identifiers.add(node.id)
|
|
107
|
-
|
|
108
|
-
def visit_Attribute(self, node):
|
|
109
|
-
# Optionally support dotted names like mymodule.MyModel
|
|
110
|
-
full_name = []
|
|
111
|
-
while isinstance(node, ast.Attribute):
|
|
112
|
-
full_name.append(node.attr)
|
|
113
|
-
node = node.value
|
|
114
|
-
if isinstance(node, ast.Name):
|
|
115
|
-
full_name.append(node.id)
|
|
116
|
-
identifiers.add(".".join(reversed(full_name)))
|
|
117
|
-
|
|
118
|
-
IdentifierVisitor().visit(tree)
|
|
119
|
-
return identifiers
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def resolve_name(name: str):
|
|
123
|
-
"""Resolve a name to a Python object from loaded modules."""
|
|
124
|
-
# Try dotted names first
|
|
125
|
-
parts = name.split(".")
|
|
126
|
-
obj = None
|
|
127
|
-
|
|
128
|
-
if len(parts) == 1:
|
|
129
|
-
# Search globals and builtins
|
|
130
|
-
if parts[0] in globals():
|
|
131
|
-
return globals()[parts[0]]
|
|
132
|
-
if parts[0] in builtins.__dict__:
|
|
133
|
-
return builtins.__dict__[parts[0]]
|
|
134
|
-
else:
|
|
135
|
-
try:
|
|
136
|
-
obj = sys.modules[parts[0]]
|
|
137
|
-
for part in parts[1:]:
|
|
138
|
-
obj = getattr(obj, part)
|
|
139
|
-
return obj
|
|
140
|
-
except Exception:
|
|
141
|
-
return None
|
|
142
|
-
|
|
143
|
-
# Try all loaded modules' symbols
|
|
144
|
-
for module in list(sys.modules.values()):
|
|
145
|
-
if module is None or not hasattr(module, "__dict__"):
|
|
146
|
-
continue
|
|
147
|
-
if parts[0] in module.__dict__:
|
|
148
|
-
return module.__dict__[parts[0]]
|
|
149
|
-
|
|
150
|
-
return None
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def extract_pydantic_models_from_type_string(
|
|
154
|
-
type_str: str,
|
|
155
|
-
) -> list[type[BaseModel]]:
|
|
156
|
-
identifiers = extract_identifiers_from_type_str(type_str)
|
|
157
|
-
models = []
|
|
158
|
-
for name in identifiers:
|
|
159
|
-
resolved = resolve_name(name)
|
|
160
|
-
if (
|
|
161
|
-
isinstance(resolved, type)
|
|
162
|
-
and issubclass(resolved, BaseModel)
|
|
163
|
-
and resolved is not BaseModel
|
|
164
|
-
):
|
|
165
|
-
models.append(resolved)
|
|
166
|
-
return models
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
def collect_pydantic_models(
|
|
170
|
-
type_hint, seen: set[type[BaseModel]] | None = None
|
|
171
|
-
) -> set[type[BaseModel]]:
|
|
172
|
-
if seen is None:
|
|
173
|
-
seen = set()
|
|
174
|
-
|
|
175
|
-
origin = get_origin(type_hint)
|
|
176
|
-
args = get_args(type_hint)
|
|
177
|
-
|
|
178
|
-
# Direct BaseModel
|
|
179
|
-
if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
|
|
180
|
-
seen.add(type_hint)
|
|
181
|
-
return seen
|
|
182
|
-
|
|
183
|
-
# For Unions, Lists, Dicts, Tuples, etc.
|
|
184
|
-
if origin is not None:
|
|
185
|
-
for arg in args:
|
|
186
|
-
collect_pydantic_models(arg, seen)
|
|
187
|
-
|
|
188
|
-
return seen
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def serialize_item(item: Any) -> Any:
|
|
192
|
-
"""Recursively prepares an item for serialization (e.g., to dict for YAML/JSON).
|
|
193
|
-
Converts known callables to their path strings using registry.
|
|
194
|
-
Converts Pydantic models using model_dump.
|
|
195
|
-
"""
|
|
196
|
-
# Import the registry lazily when needed
|
|
197
|
-
from flock.core.registry import get_registry
|
|
198
|
-
|
|
199
|
-
registry = get_registry()
|
|
200
|
-
|
|
201
|
-
if callable(item) and not isinstance(item, type):
|
|
202
|
-
path_str = registry.get_callable_path_string(
|
|
203
|
-
item
|
|
204
|
-
) # Use registry helper
|
|
205
|
-
if path_str:
|
|
206
|
-
# Store the simple name (last part of the path) for cleaner YAML/JSON
|
|
207
|
-
simple_name = path_str.split(".")[-1]
|
|
208
|
-
logger.debug(
|
|
209
|
-
f"Serializing callable '{getattr(item, '__name__', 'unknown')}' as reference: '{simple_name}' (from path '{path_str}')"
|
|
210
|
-
)
|
|
211
|
-
return {
|
|
212
|
-
"__callable_ref__": simple_name
|
|
213
|
-
} # Use simple name convention
|
|
214
|
-
else:
|
|
215
|
-
# Handle unregistered callables (e.g., lambdas defined inline)
|
|
216
|
-
# Option 1: Raise error (stricter)
|
|
217
|
-
# raise ValueError(f"Cannot serialize unregistered callable: {getattr(item, '__name__', item)}")
|
|
218
|
-
# Option 2: Store string representation with warning (more lenient)
|
|
219
|
-
logger.warning(
|
|
220
|
-
f"Cannot serialize unregistered callable {getattr(item, '__name__', item)}, storing as string."
|
|
221
|
-
)
|
|
222
|
-
return str(item)
|
|
223
|
-
elif isinstance(item, BaseModel):
|
|
224
|
-
logger.debug(
|
|
225
|
-
f"Serializing Pydantic model instance: {item.__class__.__name__}"
|
|
226
|
-
)
|
|
227
|
-
serialized_dict = {}
|
|
228
|
-
# Iterate through defined fields in the model
|
|
229
|
-
fields_to_iterate = {}
|
|
230
|
-
if hasattr(item, "model_fields"): # Pydantic v2
|
|
231
|
-
fields_to_iterate = item.model_fields
|
|
232
|
-
|
|
233
|
-
for field_name in fields_to_iterate:
|
|
234
|
-
# Get the value *from the instance*
|
|
235
|
-
try:
|
|
236
|
-
value = getattr(item, field_name)
|
|
237
|
-
if value is not None: # Exclude None values
|
|
238
|
-
# Recursively serialize the field's value
|
|
239
|
-
serialized_dict[field_name] = serialize_item(value)
|
|
240
|
-
except AttributeError:
|
|
241
|
-
# Should not happen if iterating model_fields/__fields__ but handle defensively
|
|
242
|
-
logger.warning(
|
|
243
|
-
f"Attribute '{field_name}' not found on instance of {item.__class__.__name__} during serialization."
|
|
244
|
-
)
|
|
245
|
-
return serialized_dict
|
|
246
|
-
elif isinstance(item, Mapping):
|
|
247
|
-
return {key: serialize_item(value) for key, value in item.items()}
|
|
248
|
-
elif isinstance(item, Sequence) and not isinstance(item, str):
|
|
249
|
-
return [serialize_item(sub_item) for sub_item in item]
|
|
250
|
-
elif isinstance(item, type): # Handle type objects themselves (e.g. if stored directly)
|
|
251
|
-
# Prefer registered component/type names when possible
|
|
252
|
-
type_name = registry.get_component_type_name(item)
|
|
253
|
-
if type_name:
|
|
254
|
-
return {"__component_ref__": type_name}
|
|
255
|
-
# Fall back to module-qualified path for general types
|
|
256
|
-
try:
|
|
257
|
-
module = getattr(item, "__module__", None)
|
|
258
|
-
name = getattr(item, "__name__", None)
|
|
259
|
-
if module and name:
|
|
260
|
-
return {"__type_ref__": f"{module}.{name}"}
|
|
261
|
-
except Exception:
|
|
262
|
-
pass
|
|
263
|
-
logger.warning(f"Could not serialize type object {item}, storing as string.")
|
|
264
|
-
return str(item)
|
|
265
|
-
elif isinstance(item, Enum):
|
|
266
|
-
return item.value
|
|
267
|
-
else:
|
|
268
|
-
# Return basic types as is
|
|
269
|
-
return item
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
# --- Deserialization Helper ---
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
def deserialize_item(item: Any) -> Any:
|
|
276
|
-
"""Recursively processes a deserialized item (e.g., from YAML/JSON dict).
|
|
277
|
-
Converts reference dicts back to actual callables or types using registry.
|
|
278
|
-
Handles nested lists and dicts.
|
|
279
|
-
"""
|
|
280
|
-
# Import the registry lazily when needed
|
|
281
|
-
from flock.core.registry import get_registry
|
|
282
|
-
|
|
283
|
-
registry = get_registry()
|
|
284
|
-
|
|
285
|
-
if isinstance(item, Mapping):
|
|
286
|
-
if "__callable_ref__" in item and len(item) == 1:
|
|
287
|
-
ref_name = item["__callable_ref__"]
|
|
288
|
-
try:
|
|
289
|
-
# The registry's get_callable needs to handle lookup by simple name OR full path
|
|
290
|
-
# Or we assume get_callable handles finding the right function from the simple name
|
|
291
|
-
resolved_callable = registry.get_callable(ref_name)
|
|
292
|
-
logger.debug(
|
|
293
|
-
f"Deserialized callable reference '{ref_name}' to {resolved_callable}"
|
|
294
|
-
)
|
|
295
|
-
return resolved_callable
|
|
296
|
-
except KeyError:
|
|
297
|
-
logger.error(
|
|
298
|
-
f"Callable reference '{ref_name}' not found during deserialization."
|
|
299
|
-
)
|
|
300
|
-
return None # Or raise?
|
|
301
|
-
except Exception as e:
|
|
302
|
-
logger.error(
|
|
303
|
-
f"Error resolving callable reference '{ref_name}': {e}",
|
|
304
|
-
exc_info=True,
|
|
305
|
-
)
|
|
306
|
-
return None
|
|
307
|
-
elif "__component_ref__" in item and len(item) == 1:
|
|
308
|
-
type_name = item["__component_ref__"]
|
|
309
|
-
try:
|
|
310
|
-
return registry.get_component(type_name)
|
|
311
|
-
except KeyError:
|
|
312
|
-
logger.error(
|
|
313
|
-
f"Component reference '{type_name}' not found during deserialization."
|
|
314
|
-
)
|
|
315
|
-
return None
|
|
316
|
-
elif "__type_ref__" in item and len(item) == 1:
|
|
317
|
-
type_name = item["__type_ref__"]
|
|
318
|
-
try:
|
|
319
|
-
# For general types, use get_type or fallback to dynamic import like get_callable
|
|
320
|
-
# Using get_type for now, assuming it needs registration
|
|
321
|
-
return registry.get_type(type_name)
|
|
322
|
-
except KeyError:
|
|
323
|
-
# Attempt dynamic import as fallback if get_type fails (similar to get_callable)
|
|
324
|
-
try:
|
|
325
|
-
if "." not in type_name: # Builtins?
|
|
326
|
-
mod = importlib.import_module("builtins")
|
|
327
|
-
else:
|
|
328
|
-
module_name, class_name = type_name.rsplit(".", 1)
|
|
329
|
-
mod = importlib.import_module(module_name)
|
|
330
|
-
type_obj = getattr(mod, class_name)
|
|
331
|
-
if isinstance(type_obj, type):
|
|
332
|
-
registry.register_type(
|
|
333
|
-
type_obj, type_name
|
|
334
|
-
) # Cache it
|
|
335
|
-
return type_obj
|
|
336
|
-
else:
|
|
337
|
-
raise TypeError()
|
|
338
|
-
except Exception:
|
|
339
|
-
logger.error(
|
|
340
|
-
f"Type reference '{type_name}' not found in registry or via dynamic import."
|
|
341
|
-
)
|
|
342
|
-
return None
|
|
343
|
-
|
|
344
|
-
else:
|
|
345
|
-
# Recursively deserialize dictionary values
|
|
346
|
-
return {key: deserialize_item(value) for key, value in item.items()}
|
|
347
|
-
elif isinstance(item, Sequence) and not isinstance(item, str):
|
|
348
|
-
return [deserialize_item(sub_item) for sub_item in item]
|
|
349
|
-
else:
|
|
350
|
-
# Return basic types as is
|
|
351
|
-
return item
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
# --- Component Deserialization Helper ---
|
|
355
|
-
def deserialize_component(
|
|
356
|
-
data: dict | None, expected_base_type: type
|
|
357
|
-
) -> Any | None:
|
|
358
|
-
"""Deserializes a component (Module, Evaluator, Router) from its dict representation.
|
|
359
|
-
Uses the 'type' field to find the correct class via registry.
|
|
360
|
-
"""
|
|
361
|
-
# Import the registry and COMPONENT_BASE_TYPES lazily when needed
|
|
362
|
-
from flock.core.registry import get_registry
|
|
363
|
-
|
|
364
|
-
registry = get_registry()
|
|
365
|
-
|
|
366
|
-
if data is None:
|
|
367
|
-
return None
|
|
368
|
-
if not isinstance(data, dict):
|
|
369
|
-
logger.error(
|
|
370
|
-
f"Expected dict for component deserialization, got {type(data)}"
|
|
371
|
-
)
|
|
372
|
-
return None
|
|
373
|
-
|
|
374
|
-
type_name = data.get(
|
|
375
|
-
"type"
|
|
376
|
-
) # Assuming 'type' key holds the class name string
|
|
377
|
-
if not type_name:
|
|
378
|
-
logger.error(f"Component data missing 'type' field: {data}")
|
|
379
|
-
return None
|
|
380
|
-
|
|
381
|
-
try:
|
|
382
|
-
ComponentClass = registry.get_component(type_name) # Use registry
|
|
383
|
-
# Optional: Keep the base type check
|
|
384
|
-
try:
|
|
385
|
-
from flock.core.component.agent_component_base import AgentComponent
|
|
386
|
-
if not issubclass(ComponentClass, AgentComponent):
|
|
387
|
-
logger.warning(
|
|
388
|
-
f"Deserialized class '{type_name}' is not a subclass of AgentComponent."
|
|
389
|
-
)
|
|
390
|
-
except ImportError:
|
|
391
|
-
# AgentComponent not available during setup
|
|
392
|
-
pass
|
|
393
|
-
|
|
394
|
-
# Recursively deserialize the data *before* passing to Pydantic constructor
|
|
395
|
-
# This handles nested callables/types within the component's config/data
|
|
396
|
-
deserialized_data_for_init = {}
|
|
397
|
-
for k, v in data.items():
|
|
398
|
-
# Don't pass the 'type' field itself to the constructor if it matches class name
|
|
399
|
-
if k == "type" and v == ComponentClass.__name__:
|
|
400
|
-
continue
|
|
401
|
-
deserialized_data_for_init[k] = deserialize_item(v)
|
|
402
|
-
|
|
403
|
-
# Use Pydantic constructor directly. Assumes keys match field names.
|
|
404
|
-
# from_dict could be added to components for more complex logic if needed.
|
|
405
|
-
return ComponentClass(**deserialized_data_for_init)
|
|
406
|
-
|
|
407
|
-
except (KeyError, TypeError, Exception) as e:
|
|
408
|
-
logger.error(
|
|
409
|
-
f"Failed to deserialize component of type '{type_name}': {e}",
|
|
410
|
-
exc_info=True,
|
|
411
|
-
)
|
|
412
|
-
return None
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
"""Utility functions for handling file paths in Flock.
|
|
2
|
-
|
|
3
|
-
This module provides utilities for working with file paths,
|
|
4
|
-
especially for components that may be loaded from file system paths
|
|
5
|
-
rather than module imports.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import importlib.util
|
|
9
|
-
import inspect
|
|
10
|
-
import os
|
|
11
|
-
import sys
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def get_file_path(obj: Any) -> str | None:
|
|
17
|
-
"""Get the file path for a Python object.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
obj: The object to get the file path for
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
The file path if it can be determined, None otherwise
|
|
24
|
-
"""
|
|
25
|
-
try:
|
|
26
|
-
if inspect.ismodule(obj):
|
|
27
|
-
return obj.__file__
|
|
28
|
-
elif inspect.isclass(obj) or inspect.isfunction(obj):
|
|
29
|
-
return inspect.getfile(obj)
|
|
30
|
-
return None
|
|
31
|
-
except (TypeError, ValueError):
|
|
32
|
-
return None
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def normalize_path(path: str) -> str:
|
|
36
|
-
"""Normalize a path for consistent representation.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
path: The path to normalize
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
The normalized path
|
|
43
|
-
"""
|
|
44
|
-
return os.path.normpath(path)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def is_same_path(path1: str, path2: str) -> bool:
|
|
48
|
-
"""Check if two paths point to the same file.
|
|
49
|
-
|
|
50
|
-
Args:
|
|
51
|
-
path1: The first path
|
|
52
|
-
path2: The second path
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
True if the paths point to the same file, False otherwise
|
|
56
|
-
"""
|
|
57
|
-
return os.path.normpath(os.path.abspath(path1)) == os.path.normpath(
|
|
58
|
-
os.path.abspath(path2)
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def get_relative_path(path: str, base_path: str | None = None) -> str:
|
|
63
|
-
"""Get a path relative to a base path.
|
|
64
|
-
|
|
65
|
-
Args:
|
|
66
|
-
path: The path to make relative
|
|
67
|
-
base_path: The base path (defaults to current working directory)
|
|
68
|
-
|
|
69
|
-
Returns:
|
|
70
|
-
The relative path
|
|
71
|
-
"""
|
|
72
|
-
if base_path is None:
|
|
73
|
-
base_path = os.getcwd()
|
|
74
|
-
|
|
75
|
-
return os.path.relpath(path, base_path)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def load_class_from_file(file_path: str, class_name: str) -> type | None:
|
|
79
|
-
"""Load a class from a file.
|
|
80
|
-
|
|
81
|
-
Args:
|
|
82
|
-
file_path: The path to the file
|
|
83
|
-
class_name: The name of the class to load
|
|
84
|
-
|
|
85
|
-
Returns:
|
|
86
|
-
The loaded class, or None if it could not be loaded
|
|
87
|
-
"""
|
|
88
|
-
try:
|
|
89
|
-
# Generate a unique module name to avoid conflicts
|
|
90
|
-
module_name = f"flock_dynamic_import_{hash(file_path)}"
|
|
91
|
-
|
|
92
|
-
# Create a spec for the module
|
|
93
|
-
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
94
|
-
if not spec or not spec.loader:
|
|
95
|
-
return None
|
|
96
|
-
|
|
97
|
-
# Create and load the module
|
|
98
|
-
module = importlib.util.module_from_spec(spec)
|
|
99
|
-
sys.modules[module_name] = module
|
|
100
|
-
spec.loader.exec_module(module)
|
|
101
|
-
|
|
102
|
-
# Get the class from the module
|
|
103
|
-
if not hasattr(module, class_name):
|
|
104
|
-
return None
|
|
105
|
-
|
|
106
|
-
return getattr(module, class_name)
|
|
107
|
-
except Exception:
|
|
108
|
-
return None
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def get_project_root() -> Path:
|
|
112
|
-
"""Get the project root directory.
|
|
113
|
-
|
|
114
|
-
Returns:
|
|
115
|
-
The project root path
|
|
116
|
-
"""
|
|
117
|
-
# Try to find the directory containing pyproject.toml or setup.py
|
|
118
|
-
current_dir = Path(os.getcwd())
|
|
119
|
-
|
|
120
|
-
# Walk up the directory tree looking for project markers
|
|
121
|
-
for path in [current_dir, *current_dir.parents]:
|
|
122
|
-
if (path / "pyproject.toml").exists() or (path / "setup.py").exists():
|
|
123
|
-
return path
|
|
124
|
-
|
|
125
|
-
# Default to current directory if no project markers found
|
|
126
|
-
return current_dir
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def component_path_to_file_path(component_path: str) -> str | None:
|
|
130
|
-
"""Convert a component path (module.ClassName) to a file path.
|
|
131
|
-
|
|
132
|
-
Args:
|
|
133
|
-
component_path: The component path in the form module.ClassName
|
|
134
|
-
|
|
135
|
-
Returns:
|
|
136
|
-
The file path if it can be determined, None otherwise
|
|
137
|
-
"""
|
|
138
|
-
try:
|
|
139
|
-
# Split into module path and class name
|
|
140
|
-
if "." not in component_path:
|
|
141
|
-
return None
|
|
142
|
-
|
|
143
|
-
module_path, class_name = component_path.rsplit(".", 1)
|
|
144
|
-
|
|
145
|
-
# Import the module
|
|
146
|
-
module = importlib.import_module(module_path)
|
|
147
|
-
|
|
148
|
-
# Get the file path
|
|
149
|
-
if hasattr(module, "__file__"):
|
|
150
|
-
return module.__file__
|
|
151
|
-
|
|
152
|
-
return None
|
|
153
|
-
except (ImportError, AttributeError):
|
|
154
|
-
return None
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def file_path_to_component_path(file_path: str, class_name: str) -> str | None:
|
|
158
|
-
"""Convert a file path and class name to a component path (module.ClassName).
|
|
159
|
-
|
|
160
|
-
This is approximate and may not work in all cases, especially for non-standard
|
|
161
|
-
module structures.
|
|
162
|
-
|
|
163
|
-
Args:
|
|
164
|
-
file_path: The file path to the module
|
|
165
|
-
class_name: The name of the class
|
|
166
|
-
|
|
167
|
-
Returns:
|
|
168
|
-
The component path if it can be determined, None otherwise
|
|
169
|
-
"""
|
|
170
|
-
try:
|
|
171
|
-
# Convert the file path to an absolute path
|
|
172
|
-
abs_path = os.path.abspath(file_path)
|
|
173
|
-
|
|
174
|
-
# Get the project root
|
|
175
|
-
root = get_project_root()
|
|
176
|
-
|
|
177
|
-
# Get the relative path from the project root
|
|
178
|
-
rel_path = os.path.relpath(abs_path, root)
|
|
179
|
-
|
|
180
|
-
# Convert to a module path
|
|
181
|
-
module_path = os.path.splitext(rel_path)[0].replace(os.sep, ".")
|
|
182
|
-
|
|
183
|
-
# Remove 'src.' prefix if present (common in Python projects)
|
|
184
|
-
if module_path.startswith("src."):
|
|
185
|
-
module_path = module_path[4:]
|
|
186
|
-
|
|
187
|
-
# Combine with the class name
|
|
188
|
-
return f"{module_path}.{class_name}"
|
|
189
|
-
except Exception:
|
|
190
|
-
return None
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def register_file_paths_in_registry(
|
|
194
|
-
component_paths: dict[str, str], registry: Any | None = None
|
|
195
|
-
) -> bool:
|
|
196
|
-
"""Register file paths in the registry.
|
|
197
|
-
|
|
198
|
-
Args:
|
|
199
|
-
component_paths: Dictionary mapping component paths to file paths
|
|
200
|
-
registry: The registry to register in (defaults to the global registry)
|
|
201
|
-
|
|
202
|
-
Returns:
|
|
203
|
-
True if all paths were registered, False otherwise
|
|
204
|
-
"""
|
|
205
|
-
try:
|
|
206
|
-
# Get the global registry if none provided
|
|
207
|
-
if registry is None:
|
|
208
|
-
from flock.core.flock_registry import get_registry
|
|
209
|
-
|
|
210
|
-
registry = get_registry()
|
|
211
|
-
|
|
212
|
-
# Initialize component_file_paths if needed
|
|
213
|
-
if not hasattr(registry, "_component_file_paths"):
|
|
214
|
-
registry._component_file_paths = {}
|
|
215
|
-
|
|
216
|
-
# Register each path
|
|
217
|
-
for component_name, file_path in component_paths.items():
|
|
218
|
-
if component_name in registry._components:
|
|
219
|
-
registry._component_file_paths[component_name] = file_path
|
|
220
|
-
|
|
221
|
-
return True
|
|
222
|
-
except Exception:
|
|
223
|
-
return False
|