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
flock/cli/registry_management.py
DELETED
|
@@ -1,889 +0,0 @@
|
|
|
1
|
-
"""Registry Management Module for the Flock CLI."""
|
|
2
|
-
|
|
3
|
-
import datetime
|
|
4
|
-
import importlib
|
|
5
|
-
import inspect
|
|
6
|
-
import os
|
|
7
|
-
from dataclasses import is_dataclass
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
import questionary
|
|
12
|
-
from rich.console import Console
|
|
13
|
-
from rich.panel import Panel
|
|
14
|
-
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
15
|
-
from rich.table import Table
|
|
16
|
-
|
|
17
|
-
from flock.core.flock_registry import (
|
|
18
|
-
get_registry,
|
|
19
|
-
)
|
|
20
|
-
from flock.core.logging.logging import get_logger
|
|
21
|
-
|
|
22
|
-
logger = get_logger("registry_cli")
|
|
23
|
-
console = Console()
|
|
24
|
-
|
|
25
|
-
# Constants for registry item types
|
|
26
|
-
REGISTRY_CATEGORIES = ["Agent", "Callable", "Type", "Component"]
|
|
27
|
-
REGISTRY_ACTIONS = [
|
|
28
|
-
"View Registry Contents",
|
|
29
|
-
"Add Item to Registry",
|
|
30
|
-
"Remove Item from Registry",
|
|
31
|
-
"Auto-Registration Scanner",
|
|
32
|
-
"Export Registry",
|
|
33
|
-
"Back to Main Menu",
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def manage_registry() -> None:
|
|
38
|
-
"""Main function for managing the Flock Registry from the CLI."""
|
|
39
|
-
while True:
|
|
40
|
-
console.clear()
|
|
41
|
-
console.print(
|
|
42
|
-
Panel("[bold blue]Flock Registry Management[/]"), justify="center"
|
|
43
|
-
)
|
|
44
|
-
console.line()
|
|
45
|
-
|
|
46
|
-
# Show registry stats
|
|
47
|
-
display_registry_stats()
|
|
48
|
-
|
|
49
|
-
action = questionary.select(
|
|
50
|
-
"What would you like to do?",
|
|
51
|
-
choices=REGISTRY_ACTIONS,
|
|
52
|
-
).ask()
|
|
53
|
-
|
|
54
|
-
if action == "View Registry Contents":
|
|
55
|
-
view_registry_contents()
|
|
56
|
-
elif action == "Add Item to Registry":
|
|
57
|
-
add_item_to_registry()
|
|
58
|
-
elif action == "Remove Item from Registry":
|
|
59
|
-
remove_item_from_registry()
|
|
60
|
-
elif action == "Auto-Registration Scanner":
|
|
61
|
-
auto_registration_scanner()
|
|
62
|
-
elif action == "Export Registry":
|
|
63
|
-
export_registry()
|
|
64
|
-
elif action == "Back to Main Menu":
|
|
65
|
-
break
|
|
66
|
-
|
|
67
|
-
input("\nPress Enter to continue...")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def display_registry_stats() -> None:
|
|
71
|
-
"""Display statistics about the current registry contents."""
|
|
72
|
-
registry = get_registry()
|
|
73
|
-
|
|
74
|
-
table = Table(title="Registry Statistics")
|
|
75
|
-
table.add_column("Category", style="cyan")
|
|
76
|
-
table.add_column("Count", style="green")
|
|
77
|
-
|
|
78
|
-
table.add_row("Agents", str(len(registry._agents)))
|
|
79
|
-
table.add_row("Callables", str(len(registry._callables)))
|
|
80
|
-
table.add_row("Types", str(len(registry._types)))
|
|
81
|
-
table.add_row("Components", str(len(registry._components)))
|
|
82
|
-
|
|
83
|
-
console.print(table)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def view_registry_contents(
|
|
87
|
-
category: str | None = None, search_pattern: str | None = None
|
|
88
|
-
) -> None:
|
|
89
|
-
"""Display registry contents with filtering options."""
|
|
90
|
-
registry = get_registry()
|
|
91
|
-
|
|
92
|
-
if category is None:
|
|
93
|
-
category = questionary.select(
|
|
94
|
-
"Select a category to view:",
|
|
95
|
-
choices=REGISTRY_CATEGORIES + ["All Categories"],
|
|
96
|
-
).ask()
|
|
97
|
-
|
|
98
|
-
if search_pattern is None:
|
|
99
|
-
search_pattern = questionary.text(
|
|
100
|
-
"Enter search pattern (leave empty to show all):"
|
|
101
|
-
).ask()
|
|
102
|
-
|
|
103
|
-
console.clear()
|
|
104
|
-
|
|
105
|
-
if category == "All Categories" or category == "Agent":
|
|
106
|
-
display_registry_section("Agents", registry._agents, search_pattern)
|
|
107
|
-
|
|
108
|
-
if category == "All Categories" or category == "Callable":
|
|
109
|
-
display_registry_section(
|
|
110
|
-
"Callables", registry._callables, search_pattern
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
if category == "All Categories" or category == "Type":
|
|
114
|
-
display_registry_section("Types", registry._types, search_pattern)
|
|
115
|
-
|
|
116
|
-
if category == "All Categories" or category == "Component":
|
|
117
|
-
display_registry_section(
|
|
118
|
-
"Components", registry._components, search_pattern
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def display_registry_section(
|
|
123
|
-
title: str, items: dict[str, Any], search_pattern: str
|
|
124
|
-
) -> None:
|
|
125
|
-
"""Display a section of registry items in a table."""
|
|
126
|
-
filtered_items = {
|
|
127
|
-
k: v
|
|
128
|
-
for k, v in items.items()
|
|
129
|
-
if not search_pattern or search_pattern.lower() in k.lower()
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if not filtered_items:
|
|
133
|
-
console.print(
|
|
134
|
-
f"[yellow]No {title.lower()} found matching the search pattern.[/]"
|
|
135
|
-
)
|
|
136
|
-
return
|
|
137
|
-
|
|
138
|
-
table = Table(title=f"Registered {title}")
|
|
139
|
-
table.add_column("Name/Path", style="cyan")
|
|
140
|
-
table.add_column("Type", style="green")
|
|
141
|
-
|
|
142
|
-
# Add file path column for components
|
|
143
|
-
if title == "Components":
|
|
144
|
-
table.add_column("File Path", style="yellow")
|
|
145
|
-
|
|
146
|
-
for name, item in filtered_items.items():
|
|
147
|
-
item_type = type(item).__name__
|
|
148
|
-
|
|
149
|
-
if title == "Components":
|
|
150
|
-
# Try to get the file path for component classes
|
|
151
|
-
file_path = (
|
|
152
|
-
inspect.getfile(item) if inspect.isclass(item) else "N/A"
|
|
153
|
-
)
|
|
154
|
-
table.add_row(name, item_type, file_path)
|
|
155
|
-
else:
|
|
156
|
-
table.add_row(name, item_type)
|
|
157
|
-
|
|
158
|
-
console.print(table)
|
|
159
|
-
console.print(f"Total: {len(filtered_items)} {title.lower()}")
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def add_item_to_registry() -> None:
|
|
163
|
-
"""Add an item to the registry manually."""
|
|
164
|
-
registry = get_registry()
|
|
165
|
-
|
|
166
|
-
item_type = questionary.select(
|
|
167
|
-
"What type of item do you want to add?",
|
|
168
|
-
choices=["agent", "callable", "type", "component"],
|
|
169
|
-
).ask()
|
|
170
|
-
|
|
171
|
-
# For component types, offer file path option
|
|
172
|
-
use_file_path = False
|
|
173
|
-
if item_type == "component":
|
|
174
|
-
path_type = questionary.select(
|
|
175
|
-
"How do you want to specify the component?",
|
|
176
|
-
choices=["Module Path", "File Path"],
|
|
177
|
-
).ask()
|
|
178
|
-
use_file_path = path_type == "File Path"
|
|
179
|
-
|
|
180
|
-
if use_file_path:
|
|
181
|
-
file_path = questionary.path(
|
|
182
|
-
"Enter the file path to the component:", only_directories=False
|
|
183
|
-
).ask()
|
|
184
|
-
|
|
185
|
-
if not file_path or not os.path.exists(file_path):
|
|
186
|
-
console.print(f"[red]Error: File {file_path} does not exist[/]")
|
|
187
|
-
return False
|
|
188
|
-
|
|
189
|
-
module_name = questionary.text(
|
|
190
|
-
"Enter the component class name in the file:"
|
|
191
|
-
).ask()
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
# Use dynamic import to load the module from file path
|
|
195
|
-
import importlib.util
|
|
196
|
-
|
|
197
|
-
spec = importlib.util.spec_from_file_location(
|
|
198
|
-
"temp_module", file_path
|
|
199
|
-
)
|
|
200
|
-
module = importlib.util.module_from_spec(spec)
|
|
201
|
-
spec.loader.exec_module(module)
|
|
202
|
-
|
|
203
|
-
if not hasattr(module, module_name):
|
|
204
|
-
console.print(
|
|
205
|
-
f"[red]Error: {module_name} not found in {file_path}[/]"
|
|
206
|
-
)
|
|
207
|
-
return False
|
|
208
|
-
|
|
209
|
-
item = getattr(module, module_name)
|
|
210
|
-
except Exception as e:
|
|
211
|
-
console.print(f"[red]Error importing from file: {e!s}[/]")
|
|
212
|
-
return False
|
|
213
|
-
else:
|
|
214
|
-
module_path = questionary.text(
|
|
215
|
-
"Enter the module path (e.g., 'your_module.submodule'):"
|
|
216
|
-
).ask()
|
|
217
|
-
|
|
218
|
-
item_name = questionary.text(
|
|
219
|
-
"Enter the item name within the module:"
|
|
220
|
-
).ask()
|
|
221
|
-
|
|
222
|
-
try:
|
|
223
|
-
# Attempt to import the module
|
|
224
|
-
module = importlib.import_module(module_path)
|
|
225
|
-
|
|
226
|
-
# Get the item from the module
|
|
227
|
-
if not hasattr(module, item_name):
|
|
228
|
-
console.print(
|
|
229
|
-
f"[red]Error: {item_name} not found in {module_path}[/]"
|
|
230
|
-
)
|
|
231
|
-
return False
|
|
232
|
-
|
|
233
|
-
item = getattr(module, item_name)
|
|
234
|
-
except Exception as e:
|
|
235
|
-
console.print(f"[red]Error importing module: {e!s}[/]")
|
|
236
|
-
return False
|
|
237
|
-
|
|
238
|
-
alias = questionary.text(
|
|
239
|
-
"Enter an alias (optional, press Enter to skip):"
|
|
240
|
-
).ask()
|
|
241
|
-
|
|
242
|
-
if not alias:
|
|
243
|
-
alias = None
|
|
244
|
-
|
|
245
|
-
# Register the item based on its type
|
|
246
|
-
try:
|
|
247
|
-
if item_type == "agent":
|
|
248
|
-
registry.register_agent(item)
|
|
249
|
-
console.print(
|
|
250
|
-
f"[green]Successfully registered agent: {item_name}[/]"
|
|
251
|
-
)
|
|
252
|
-
elif item_type == "callable":
|
|
253
|
-
result = registry.register_callable(item, alias)
|
|
254
|
-
console.print(
|
|
255
|
-
f"[green]Successfully registered callable: {result}[/]"
|
|
256
|
-
)
|
|
257
|
-
elif item_type == "type":
|
|
258
|
-
result = registry.register_type(item, alias)
|
|
259
|
-
console.print(f"[green]Successfully registered type: {result}[/]")
|
|
260
|
-
elif item_type == "component":
|
|
261
|
-
result = registry.register_component(item, alias)
|
|
262
|
-
# Store the file path information if we loaded from a file
|
|
263
|
-
if use_file_path and hasattr(registry, "_component_file_paths"):
|
|
264
|
-
# Check if the registry has component file paths attribute
|
|
265
|
-
# This will be added to registry in our update
|
|
266
|
-
registry._component_file_paths[result] = file_path
|
|
267
|
-
console.print(
|
|
268
|
-
f"[green]Successfully registered component: {result}[/]"
|
|
269
|
-
)
|
|
270
|
-
except Exception as e:
|
|
271
|
-
console.print(f"[red]Error registering item: {e!s}[/]")
|
|
272
|
-
return False
|
|
273
|
-
|
|
274
|
-
return True
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def remove_item_from_registry() -> None:
|
|
278
|
-
"""Remove an item from the registry."""
|
|
279
|
-
registry = get_registry()
|
|
280
|
-
|
|
281
|
-
item_type = questionary.select(
|
|
282
|
-
"What type of item do you want to remove?",
|
|
283
|
-
choices=["agent", "callable", "type", "component"],
|
|
284
|
-
).ask()
|
|
285
|
-
|
|
286
|
-
# Get the appropriate dictionary based on item type
|
|
287
|
-
if item_type == "agent":
|
|
288
|
-
items = registry._agents
|
|
289
|
-
elif item_type == "callable":
|
|
290
|
-
items = registry._callables
|
|
291
|
-
elif item_type == "type":
|
|
292
|
-
items = registry._types
|
|
293
|
-
elif item_type == "component":
|
|
294
|
-
items = registry._components
|
|
295
|
-
|
|
296
|
-
if not items:
|
|
297
|
-
console.print(f"[yellow]No {item_type}s registered.[/]")
|
|
298
|
-
return False
|
|
299
|
-
|
|
300
|
-
# Create a list of items for selection
|
|
301
|
-
item_names = list(items.keys())
|
|
302
|
-
item_name = questionary.select(
|
|
303
|
-
f"Select the {item_type} to remove:",
|
|
304
|
-
choices=item_names + ["Cancel"],
|
|
305
|
-
).ask()
|
|
306
|
-
|
|
307
|
-
if item_name == "Cancel":
|
|
308
|
-
return False
|
|
309
|
-
|
|
310
|
-
# Ask for confirmation
|
|
311
|
-
confirm = questionary.confirm(
|
|
312
|
-
f"Are you sure you want to remove {item_name}?",
|
|
313
|
-
default=False,
|
|
314
|
-
).ask()
|
|
315
|
-
|
|
316
|
-
if not confirm:
|
|
317
|
-
console.print("[yellow]Operation cancelled.[/]")
|
|
318
|
-
return False
|
|
319
|
-
|
|
320
|
-
# Remove the item
|
|
321
|
-
try:
|
|
322
|
-
if item_type == "agent":
|
|
323
|
-
del registry._agents[item_name]
|
|
324
|
-
elif item_type == "callable":
|
|
325
|
-
del registry._callables[item_name]
|
|
326
|
-
elif item_type == "type":
|
|
327
|
-
del registry._types[item_name]
|
|
328
|
-
elif item_type == "component":
|
|
329
|
-
del registry._components[item_name]
|
|
330
|
-
|
|
331
|
-
console.print(
|
|
332
|
-
f"[green]Successfully removed {item_type}: {item_name}[/]"
|
|
333
|
-
)
|
|
334
|
-
return True
|
|
335
|
-
|
|
336
|
-
except Exception as e:
|
|
337
|
-
console.print(f"[red]Error: {e!s}[/]")
|
|
338
|
-
return False
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
def auto_registration_scanner() -> None:
|
|
342
|
-
"""Launch the auto-registration scanner interface."""
|
|
343
|
-
console.clear()
|
|
344
|
-
console.print(
|
|
345
|
-
Panel("[bold blue]Auto-Registration Scanner[/]"), justify="center"
|
|
346
|
-
)
|
|
347
|
-
console.line()
|
|
348
|
-
|
|
349
|
-
console.print(
|
|
350
|
-
"This utility will scan Python files for components, types, callables (tools), and agents that can be registered."
|
|
351
|
-
)
|
|
352
|
-
console.print(
|
|
353
|
-
"[yellow]Note: Registration is required for proper serialization and deserialization of your Flock.[/]"
|
|
354
|
-
)
|
|
355
|
-
console.line()
|
|
356
|
-
|
|
357
|
-
# Target directory selection
|
|
358
|
-
def path_filter(path):
|
|
359
|
-
"""Filter paths for selection."""
|
|
360
|
-
if os.path.isdir(path):
|
|
361
|
-
return True
|
|
362
|
-
return path.endswith(".py")
|
|
363
|
-
|
|
364
|
-
target_path = questionary.path(
|
|
365
|
-
"Select directory to scan:", file_filter=path_filter
|
|
366
|
-
).ask()
|
|
367
|
-
|
|
368
|
-
if not target_path or not os.path.exists(target_path):
|
|
369
|
-
console.print("[red]Invalid path selected. Aborting.[/]")
|
|
370
|
-
return
|
|
371
|
-
|
|
372
|
-
is_recursive = questionary.confirm(
|
|
373
|
-
"Scan recursively (include subdirectories)?", default=True
|
|
374
|
-
).ask()
|
|
375
|
-
|
|
376
|
-
auto_register = questionary.confirm(
|
|
377
|
-
"Automatically register items found during scan?", default=True
|
|
378
|
-
).ask()
|
|
379
|
-
|
|
380
|
-
# Special callout for tools/callables
|
|
381
|
-
console.print(
|
|
382
|
-
"[bold blue]Tool Registration:[/] This scanner will look for functions that can be used as tools."
|
|
383
|
-
)
|
|
384
|
-
console.print(
|
|
385
|
-
"These will be registered as callables and can be properly serialized in your Flock YAML."
|
|
386
|
-
)
|
|
387
|
-
console.line()
|
|
388
|
-
|
|
389
|
-
with Progress(
|
|
390
|
-
SpinnerColumn(),
|
|
391
|
-
TextColumn("[bold blue]{task.description}"),
|
|
392
|
-
BarColumn(),
|
|
393
|
-
TextColumn("[bold green]{task.completed}/{task.total}"),
|
|
394
|
-
console=console,
|
|
395
|
-
) as progress:
|
|
396
|
-
task_id = progress.add_task(
|
|
397
|
-
"Scanning for registry items...", total=None
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
# Perform the scan
|
|
401
|
-
results = scan_for_registry_items(
|
|
402
|
-
target_path, recursive=is_recursive, auto_register=auto_register
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
# Mark task as complete
|
|
406
|
-
progress.update(task_id, completed=1, total=1)
|
|
407
|
-
console.line()
|
|
408
|
-
|
|
409
|
-
# Display results
|
|
410
|
-
console.print("[bold green]Scan Complete![/]")
|
|
411
|
-
console.line()
|
|
412
|
-
|
|
413
|
-
total_found = sum(len(items) for items in results.values())
|
|
414
|
-
total_categories = sum(1 for items in results.values() if items)
|
|
415
|
-
|
|
416
|
-
console.print(
|
|
417
|
-
f"Found {total_found} items across {total_categories} categories."
|
|
418
|
-
)
|
|
419
|
-
|
|
420
|
-
# Enhanced report section
|
|
421
|
-
table = Table(title="Scan Results")
|
|
422
|
-
table.add_column("Category", style="cyan")
|
|
423
|
-
table.add_column("Count", style="green")
|
|
424
|
-
table.add_column("Example Items", style="blue")
|
|
425
|
-
|
|
426
|
-
for category, items in results.items():
|
|
427
|
-
if items:
|
|
428
|
-
examples = ", ".join(items[:3])
|
|
429
|
-
if len(items) > 3:
|
|
430
|
-
examples += ", ..."
|
|
431
|
-
table.add_row(category, str(len(items)), examples)
|
|
432
|
-
else:
|
|
433
|
-
table.add_row(category, "0", "")
|
|
434
|
-
|
|
435
|
-
console.print(table)
|
|
436
|
-
console.line()
|
|
437
|
-
|
|
438
|
-
# Callout for tools and future serialization
|
|
439
|
-
if results.get("callables"):
|
|
440
|
-
console.print(
|
|
441
|
-
"[bold green]Note:[/] Found callable functions that can be used as tools."
|
|
442
|
-
)
|
|
443
|
-
console.print(
|
|
444
|
-
"These functions will now be properly serialized as callable references in your Flock YAML."
|
|
445
|
-
)
|
|
446
|
-
console.print(
|
|
447
|
-
"When sharing Flocks, ensure these callables are registered on the target system."
|
|
448
|
-
)
|
|
449
|
-
console.line()
|
|
450
|
-
|
|
451
|
-
# Show details options
|
|
452
|
-
if total_found > 0:
|
|
453
|
-
view_details = questionary.confirm(
|
|
454
|
-
"Would you like to view detailed results?", default=True
|
|
455
|
-
).ask()
|
|
456
|
-
|
|
457
|
-
if view_details:
|
|
458
|
-
view_registry_contents() # Show the registry contents after scan
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
def scan_for_registry_items(
|
|
462
|
-
target_path: str, recursive: bool = True, auto_register: bool = False
|
|
463
|
-
) -> dict[str, list[str]]:
|
|
464
|
-
"""Scan directory for potential registry items and optionally register them."""
|
|
465
|
-
results = {
|
|
466
|
-
"Agents": [],
|
|
467
|
-
"Callables": [],
|
|
468
|
-
"Types": [],
|
|
469
|
-
"Components": [],
|
|
470
|
-
"Potential Items": [],
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
registry = get_registry()
|
|
474
|
-
path = Path(target_path)
|
|
475
|
-
|
|
476
|
-
with Progress(
|
|
477
|
-
SpinnerColumn(),
|
|
478
|
-
TextColumn("[progress.description]{task.description}"),
|
|
479
|
-
BarColumn(),
|
|
480
|
-
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
481
|
-
) as progress:
|
|
482
|
-
scan_task = progress.add_task(f"Scanning {target_path}...", total=100)
|
|
483
|
-
|
|
484
|
-
# If path is a file, scan it directly
|
|
485
|
-
if path.is_file() and path.suffix == ".py":
|
|
486
|
-
module_path = get_module_path_from_file(path)
|
|
487
|
-
if module_path:
|
|
488
|
-
scan_python_file(path, module_path, results, auto_register)
|
|
489
|
-
progress.update(scan_task, completed=100)
|
|
490
|
-
|
|
491
|
-
# If path is a directory, scan all Python files
|
|
492
|
-
elif path.is_dir():
|
|
493
|
-
python_files = []
|
|
494
|
-
if recursive:
|
|
495
|
-
for root, _, files in os.walk(path):
|
|
496
|
-
python_files.extend(
|
|
497
|
-
[
|
|
498
|
-
Path(os.path.join(root, f))
|
|
499
|
-
for f in files
|
|
500
|
-
if f.endswith(".py")
|
|
501
|
-
]
|
|
502
|
-
)
|
|
503
|
-
else:
|
|
504
|
-
python_files = [p for p in path.glob("*.py")]
|
|
505
|
-
|
|
506
|
-
total_files = len(python_files)
|
|
507
|
-
for i, file_path in enumerate(python_files):
|
|
508
|
-
module_path = get_module_path_from_file(file_path)
|
|
509
|
-
if module_path:
|
|
510
|
-
scan_python_file(
|
|
511
|
-
file_path, module_path, results, auto_register
|
|
512
|
-
)
|
|
513
|
-
progress.update(
|
|
514
|
-
scan_task, completed=(i + 1) / total_files * 100
|
|
515
|
-
)
|
|
516
|
-
|
|
517
|
-
return results
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
def get_module_path_from_file(file_path: Path) -> str | None:
|
|
521
|
-
"""Convert a file path to a module path for import."""
|
|
522
|
-
try:
|
|
523
|
-
# Get absolute path
|
|
524
|
-
abs_path = file_path.resolve()
|
|
525
|
-
|
|
526
|
-
# Check if it's a Python file
|
|
527
|
-
if abs_path.suffix != ".py":
|
|
528
|
-
return None
|
|
529
|
-
|
|
530
|
-
# Get the directory containing the file
|
|
531
|
-
file_dir = abs_path.parent
|
|
532
|
-
|
|
533
|
-
# Find the nearest parent directory with __init__.py
|
|
534
|
-
# to determine the package root
|
|
535
|
-
package_root = None
|
|
536
|
-
current_dir = file_dir
|
|
537
|
-
while current_dir != current_dir.parent:
|
|
538
|
-
if (current_dir / "__init__.py").exists():
|
|
539
|
-
if package_root is None:
|
|
540
|
-
package_root = current_dir
|
|
541
|
-
else:
|
|
542
|
-
# We've reached a directory without __init__.py
|
|
543
|
-
# If we found a package root earlier, use that
|
|
544
|
-
if package_root is not None:
|
|
545
|
-
break
|
|
546
|
-
current_dir = current_dir.parent
|
|
547
|
-
|
|
548
|
-
# If no package root was found, this file can't be imported as a module
|
|
549
|
-
if package_root is None:
|
|
550
|
-
return None
|
|
551
|
-
|
|
552
|
-
# Calculate the module path
|
|
553
|
-
rel_path = abs_path.relative_to(package_root.parent)
|
|
554
|
-
module_path = str(rel_path.with_suffix("")).replace(os.sep, ".")
|
|
555
|
-
|
|
556
|
-
return module_path
|
|
557
|
-
|
|
558
|
-
except Exception as e:
|
|
559
|
-
logger.error(f"Error determining module path: {e}")
|
|
560
|
-
return None
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
def scan_python_file(
|
|
564
|
-
file_path: Path,
|
|
565
|
-
module_path: str,
|
|
566
|
-
results: dict[str, list[str]],
|
|
567
|
-
auto_register: bool,
|
|
568
|
-
) -> None:
|
|
569
|
-
"""Scan a Python file for registry-eligible items."""
|
|
570
|
-
try:
|
|
571
|
-
# Try to import the module
|
|
572
|
-
module = importlib.import_module(module_path)
|
|
573
|
-
|
|
574
|
-
# Scan for classes and functions
|
|
575
|
-
for name, obj in inspect.getmembers(module):
|
|
576
|
-
if name.startswith("_"):
|
|
577
|
-
continue
|
|
578
|
-
|
|
579
|
-
# Check for registry decorator presence
|
|
580
|
-
is_registry_item = False
|
|
581
|
-
|
|
582
|
-
# Check for classes
|
|
583
|
-
if inspect.isclass(obj):
|
|
584
|
-
# Check if it has a FlockAgent as a base class
|
|
585
|
-
if is_flock_agent(obj):
|
|
586
|
-
if auto_register:
|
|
587
|
-
get_registry().register_agent(obj)
|
|
588
|
-
results["Agents"].append(f"{module_path}.{name}")
|
|
589
|
-
is_registry_item = True
|
|
590
|
-
|
|
591
|
-
# Check for components
|
|
592
|
-
elif has_component_base(obj):
|
|
593
|
-
if auto_register:
|
|
594
|
-
get_registry().register_component(obj)
|
|
595
|
-
results["Components"].append(f"{module_path}.{name}")
|
|
596
|
-
is_registry_item = True
|
|
597
|
-
|
|
598
|
-
# Check for Pydantic models or dataclasses
|
|
599
|
-
elif is_potential_type(obj):
|
|
600
|
-
if auto_register:
|
|
601
|
-
get_registry().register_type(obj)
|
|
602
|
-
results["Types"].append(f"{module_path}.{name}")
|
|
603
|
-
is_registry_item = True
|
|
604
|
-
|
|
605
|
-
# If not already identified but seems like a potential candidate
|
|
606
|
-
elif not is_registry_item and is_potential_registry_candidate(
|
|
607
|
-
obj
|
|
608
|
-
):
|
|
609
|
-
results["Potential Items"].append(
|
|
610
|
-
f"{module_path}.{name} (class)"
|
|
611
|
-
)
|
|
612
|
-
|
|
613
|
-
# Check for functions (potential callables/tools)
|
|
614
|
-
elif inspect.isfunction(obj) and obj.__module__ == module.__name__:
|
|
615
|
-
if auto_register:
|
|
616
|
-
get_registry().register_callable(obj)
|
|
617
|
-
results["Callables"].append(f"{module_path}.{name}")
|
|
618
|
-
is_registry_item = True
|
|
619
|
-
|
|
620
|
-
except (ImportError, AttributeError) as e:
|
|
621
|
-
logger.warning(f"Could not import {module_path}: {e}")
|
|
622
|
-
except Exception as e:
|
|
623
|
-
logger.error(f"Error scanning {file_path}: {e}")
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
def is_flock_agent(cls: type) -> bool:
|
|
627
|
-
"""Check if a class is a FlockAgent or a subclass of FlockAgent."""
|
|
628
|
-
try:
|
|
629
|
-
from flock.core.flock_agent import FlockAgent
|
|
630
|
-
|
|
631
|
-
return issubclass(cls, FlockAgent)
|
|
632
|
-
except (ImportError, TypeError):
|
|
633
|
-
# If FlockAgent can't be imported or cls is not a class
|
|
634
|
-
return False
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
def has_component_base(cls: type) -> bool:
|
|
638
|
-
"""Check if a class has a base class that looks like a Flock component."""
|
|
639
|
-
try:
|
|
640
|
-
# Common Flock component base classes
|
|
641
|
-
component_bases = ["FlockModule", "FlockEvaluator", "FlockRouter"]
|
|
642
|
-
bases = [base.__name__ for base in cls.__mro__]
|
|
643
|
-
return any(base in bases for base in component_bases)
|
|
644
|
-
except (AttributeError, TypeError):
|
|
645
|
-
return False
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
def is_potential_type(cls: type) -> bool:
|
|
649
|
-
"""Check if a class is a Pydantic model or dataclass."""
|
|
650
|
-
try:
|
|
651
|
-
from pydantic import BaseModel
|
|
652
|
-
|
|
653
|
-
return issubclass(cls, BaseModel) or is_dataclass(cls)
|
|
654
|
-
except (ImportError, TypeError):
|
|
655
|
-
return False
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
def is_potential_registry_candidate(obj: Any) -> bool:
|
|
659
|
-
"""Check if an object seems like it could be registry-eligible."""
|
|
660
|
-
# This is a heuristic function to identify potential registry candidates
|
|
661
|
-
if inspect.isclass(obj):
|
|
662
|
-
# Classes with "Flock" in their name
|
|
663
|
-
if "Flock" in obj.__name__:
|
|
664
|
-
return True
|
|
665
|
-
|
|
666
|
-
# Classes with docstrings mentioning certain keywords
|
|
667
|
-
if obj.__doc__ and any(
|
|
668
|
-
kw in obj.__doc__.lower()
|
|
669
|
-
for kw in [
|
|
670
|
-
"agent",
|
|
671
|
-
"flock",
|
|
672
|
-
"tool",
|
|
673
|
-
"module",
|
|
674
|
-
"evaluator",
|
|
675
|
-
"router",
|
|
676
|
-
]
|
|
677
|
-
):
|
|
678
|
-
return True
|
|
679
|
-
|
|
680
|
-
elif inspect.isfunction(obj):
|
|
681
|
-
# Functions with docstrings mentioning certain keywords
|
|
682
|
-
if obj.__doc__ and any(
|
|
683
|
-
kw in obj.__doc__.lower() for kw in ["tool", "agent", "flock"]
|
|
684
|
-
):
|
|
685
|
-
return True
|
|
686
|
-
|
|
687
|
-
return False
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
def export_registry() -> None:
|
|
691
|
-
"""Export registry contents to a file."""
|
|
692
|
-
registry = get_registry()
|
|
693
|
-
|
|
694
|
-
# Select what to export
|
|
695
|
-
export_items = questionary.checkbox(
|
|
696
|
-
"Select what to export:",
|
|
697
|
-
choices=[
|
|
698
|
-
questionary.Choice("Agents", checked=True),
|
|
699
|
-
questionary.Choice("Callables (Tools)", checked=True),
|
|
700
|
-
questionary.Choice("Types", checked=True),
|
|
701
|
-
questionary.Choice("Components", checked=True),
|
|
702
|
-
questionary.Choice("File Paths", checked=True),
|
|
703
|
-
],
|
|
704
|
-
).ask()
|
|
705
|
-
|
|
706
|
-
if not export_items:
|
|
707
|
-
console.print("[yellow]No items selected for export.[/]")
|
|
708
|
-
return
|
|
709
|
-
|
|
710
|
-
# Select export format
|
|
711
|
-
export_format = questionary.select(
|
|
712
|
-
"Select export format:",
|
|
713
|
-
choices=["YAML", "JSON", "Python"],
|
|
714
|
-
).ask()
|
|
715
|
-
|
|
716
|
-
# Select path type for serialization
|
|
717
|
-
path_type = questionary.select(
|
|
718
|
-
"How should file paths be formatted?",
|
|
719
|
-
choices=[
|
|
720
|
-
"absolute (full paths, best for local use)",
|
|
721
|
-
"relative (relative paths, better for sharing)",
|
|
722
|
-
],
|
|
723
|
-
default="absolute (full paths, best for local use)",
|
|
724
|
-
).ask()
|
|
725
|
-
|
|
726
|
-
# Extract just the first word
|
|
727
|
-
path_type = path_type.split()[0]
|
|
728
|
-
|
|
729
|
-
console.print(
|
|
730
|
-
f"\n[bold]Path type selected: [green]{path_type}[/green][/bold]"
|
|
731
|
-
)
|
|
732
|
-
if path_type == "relative":
|
|
733
|
-
console.print(
|
|
734
|
-
"Relative paths are recommended when sharing Flocks between systems.\n"
|
|
735
|
-
"They'll be converted to paths relative to the current directory."
|
|
736
|
-
)
|
|
737
|
-
else:
|
|
738
|
-
console.print(
|
|
739
|
-
"Absolute paths work best for local usage but may not work correctly\n"
|
|
740
|
-
"when sharing with others or moving files."
|
|
741
|
-
)
|
|
742
|
-
console.line()
|
|
743
|
-
|
|
744
|
-
# Get file path for export
|
|
745
|
-
file_path = questionary.path(
|
|
746
|
-
"Enter file path for export:",
|
|
747
|
-
default=f"flock_registry_export.{export_format.lower()}",
|
|
748
|
-
).ask()
|
|
749
|
-
|
|
750
|
-
if not file_path:
|
|
751
|
-
return
|
|
752
|
-
|
|
753
|
-
# Prepare export data
|
|
754
|
-
export_data = {}
|
|
755
|
-
|
|
756
|
-
if "Agents" in export_items:
|
|
757
|
-
export_data["agents"] = list(registry._agents.keys())
|
|
758
|
-
|
|
759
|
-
if "Callables (Tools)" in export_items:
|
|
760
|
-
export_data["callables"] = list(registry._callables.keys())
|
|
761
|
-
|
|
762
|
-
# Add serialization format information for tools
|
|
763
|
-
callable_details = {}
|
|
764
|
-
for callable_name in registry._callables.keys():
|
|
765
|
-
callable_obj = registry._callables[callable_name]
|
|
766
|
-
file_path_value = (
|
|
767
|
-
inspect.getfile(callable_obj)
|
|
768
|
-
if callable_obj and inspect.isfunction(callable_obj)
|
|
769
|
-
else "Unknown"
|
|
770
|
-
)
|
|
771
|
-
|
|
772
|
-
# Convert to relative path if needed
|
|
773
|
-
if path_type == "relative" and file_path_value != "Unknown":
|
|
774
|
-
try:
|
|
775
|
-
file_path_value = os.path.relpath(file_path_value)
|
|
776
|
-
except ValueError:
|
|
777
|
-
# Keep as absolute if can't make relative
|
|
778
|
-
pass
|
|
779
|
-
|
|
780
|
-
callable_details[callable_name] = {
|
|
781
|
-
"module": callable_obj.__module__,
|
|
782
|
-
"file": file_path_value,
|
|
783
|
-
"type": "function"
|
|
784
|
-
if inspect.isfunction(callable_obj)
|
|
785
|
-
else "other_callable",
|
|
786
|
-
}
|
|
787
|
-
export_data["callable_details"] = callable_details
|
|
788
|
-
|
|
789
|
-
if "Types" in export_items:
|
|
790
|
-
export_data["types"] = list(registry._types.keys())
|
|
791
|
-
|
|
792
|
-
if "Components" in export_items:
|
|
793
|
-
export_data["components"] = list(registry._components.keys())
|
|
794
|
-
|
|
795
|
-
# Include file paths if selected
|
|
796
|
-
if "File Paths" in export_items and hasattr(
|
|
797
|
-
registry, "_component_file_paths"
|
|
798
|
-
):
|
|
799
|
-
export_data["component_file_paths"] = {}
|
|
800
|
-
for component_name in registry._components.keys():
|
|
801
|
-
# Get the file path if available
|
|
802
|
-
if component_name in registry._component_file_paths:
|
|
803
|
-
file_path_value = registry._component_file_paths[
|
|
804
|
-
component_name
|
|
805
|
-
]
|
|
806
|
-
|
|
807
|
-
# Convert to relative path if needed
|
|
808
|
-
if path_type == "relative" and file_path_value:
|
|
809
|
-
try:
|
|
810
|
-
file_path_value = os.path.relpath(file_path_value)
|
|
811
|
-
except ValueError:
|
|
812
|
-
# Keep as absolute if can't make relative
|
|
813
|
-
pass
|
|
814
|
-
|
|
815
|
-
export_data["component_file_paths"][component_name] = (
|
|
816
|
-
file_path_value
|
|
817
|
-
)
|
|
818
|
-
|
|
819
|
-
# Add metadata about serialization format
|
|
820
|
-
export_data["metadata"] = {
|
|
821
|
-
"export_date": datetime.datetime.now().isoformat(),
|
|
822
|
-
"flock_version": "0.3.41", # Update with actual version
|
|
823
|
-
"serialization_format": {
|
|
824
|
-
"tools": "Callable reference names",
|
|
825
|
-
"components": "Module and class names",
|
|
826
|
-
"types": "Module and class names",
|
|
827
|
-
},
|
|
828
|
-
"path_type": path_type,
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
# Add serialization settings as a top-level element
|
|
832
|
-
export_data["serialization_settings"] = {"path_type": path_type}
|
|
833
|
-
|
|
834
|
-
try:
|
|
835
|
-
# Export the data
|
|
836
|
-
if export_format == "YAML":
|
|
837
|
-
import yaml
|
|
838
|
-
|
|
839
|
-
with open(file_path, "w") as f:
|
|
840
|
-
yaml.dump(export_data, f, default_flow_style=False)
|
|
841
|
-
elif export_format == "JSON":
|
|
842
|
-
import json
|
|
843
|
-
|
|
844
|
-
with open(file_path, "w") as f:
|
|
845
|
-
json.dump(export_data, f, indent=2)
|
|
846
|
-
elif export_format == "Python":
|
|
847
|
-
with open(file_path, "w") as f:
|
|
848
|
-
f.write("# Flock Registry Export\n")
|
|
849
|
-
f.write(f"# Generated on {datetime.datetime.now()}\n\n")
|
|
850
|
-
f.write("registry_data = ")
|
|
851
|
-
f.write(repr(export_data))
|
|
852
|
-
f.write("\n")
|
|
853
|
-
|
|
854
|
-
console.print(f"[green]Registry exported to {file_path}[/]")
|
|
855
|
-
console.print(f"[green]Paths formatted as: {path_type}[/]")
|
|
856
|
-
|
|
857
|
-
# Print information about tool serialization if tools were exported
|
|
858
|
-
if "Callables (Tools)" in export_items and registry._callables:
|
|
859
|
-
console.print("\n[bold blue]Tool Serialization Information:[/]")
|
|
860
|
-
console.print(
|
|
861
|
-
"Tools in Flock are now serialized as callable references rather than dictionaries."
|
|
862
|
-
)
|
|
863
|
-
console.print(
|
|
864
|
-
"This makes YAML files more readable and simplifies tool management."
|
|
865
|
-
)
|
|
866
|
-
console.print("When loading a Flock with tools:")
|
|
867
|
-
console.print(" 1. Tools must be registered in the registry")
|
|
868
|
-
console.print(" 2. The tools' modules must be importable")
|
|
869
|
-
console.print(
|
|
870
|
-
" 3. Tool functions have the same signature across systems"
|
|
871
|
-
)
|
|
872
|
-
|
|
873
|
-
# Show example of how a tool would appear in YAML
|
|
874
|
-
if registry._callables:
|
|
875
|
-
console.print("\n[bold green]Example tool in YAML:[/]")
|
|
876
|
-
example_callable = next(iter(registry._callables.keys()))
|
|
877
|
-
console.print(
|
|
878
|
-
f" - {example_callable} # Function name reference"
|
|
879
|
-
)
|
|
880
|
-
console.print("instead of the old format:")
|
|
881
|
-
console.print(f" - __callable_ref__: {example_callable}")
|
|
882
|
-
|
|
883
|
-
except Exception as e:
|
|
884
|
-
console.print(f"[red]Error exporting registry: {e}[/]")
|
|
885
|
-
logger.error(f"Failed to export registry: {e}", exc_info=True)
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
if __name__ == "__main__":
|
|
889
|
-
manage_registry()
|