flock-core 0.4.542__py3-none-any.whl → 0.5.0__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 +1079 -0
- flock/api/themes.py +71 -0
- flock/artifacts.py +86 -0
- flock/cli.py +147 -0
- flock/components.py +189 -0
- flock/dashboard/__init__.py +30 -0
- flock/dashboard/collector.py +559 -0
- flock/dashboard/events.py +188 -0
- flock/dashboard/graph_builder.py +563 -0
- flock/dashboard/launcher.py +235 -0
- flock/dashboard/models/graph.py +156 -0
- flock/dashboard/service.py +991 -0
- flock/dashboard/static_v2/assets/index-DFRnI_mt.js +111 -0
- flock/dashboard/static_v2/assets/index-fPLNdmp1.css +1 -0
- flock/dashboard/static_v2/index.html +13 -0
- flock/dashboard/websocket.py +246 -0
- flock/engines/__init__.py +6 -0
- flock/engines/dspy_engine.py +932 -0
- flock/examples.py +131 -0
- flock/frontend/README.md +778 -0
- flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
- flock/frontend/index.html +12 -0
- flock/frontend/package-lock.json +4337 -0
- flock/frontend/package.json +48 -0
- flock/frontend/src/App.tsx +139 -0
- flock/frontend/src/__tests__/integration/graph-snapshot.test.tsx +647 -0
- flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
- flock/frontend/src/components/common/BuildInfo.tsx +39 -0
- flock/frontend/src/components/common/EmptyState.module.css +115 -0
- flock/frontend/src/components/common/EmptyState.tsx +128 -0
- flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
- flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
- flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
- flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
- flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
- flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
- flock/frontend/src/components/controls/PublishControl.css +547 -0
- flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
- flock/frontend/src/components/controls/PublishControl.tsx +432 -0
- flock/frontend/src/components/details/DetailWindowContainer.tsx +58 -0
- flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
- flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
- flock/frontend/src/components/details/MessageDetailWindow.tsx +439 -0
- flock/frontend/src/components/details/MessageHistoryTab.tsx +374 -0
- flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
- flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
- flock/frontend/src/components/details/RunStatusTab.tsx +348 -0
- flock/frontend/src/components/details/tabs.test.tsx +1015 -0
- flock/frontend/src/components/filters/ArtifactTypeFilter.tsx +21 -0
- flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
- flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
- flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
- flock/frontend/src/components/filters/FilterFlyout.module.css +104 -0
- flock/frontend/src/components/filters/FilterFlyout.tsx +80 -0
- flock/frontend/src/components/filters/FilterPills.module.css +220 -0
- flock/frontend/src/components/filters/FilterPills.test.tsx +189 -0
- flock/frontend/src/components/filters/FilterPills.tsx +143 -0
- flock/frontend/src/components/filters/ProducerFilter.tsx +21 -0
- flock/frontend/src/components/filters/SavedFiltersControl.module.css +60 -0
- flock/frontend/src/components/filters/SavedFiltersControl.test.tsx +158 -0
- flock/frontend/src/components/filters/SavedFiltersControl.tsx +159 -0
- flock/frontend/src/components/filters/TagFilter.tsx +21 -0
- flock/frontend/src/components/filters/TimeRangeFilter.module.css +115 -0
- flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
- flock/frontend/src/components/filters/TimeRangeFilter.tsx +110 -0
- flock/frontend/src/components/filters/VisibilityFilter.tsx +21 -0
- flock/frontend/src/components/graph/AgentNode.test.tsx +77 -0
- flock/frontend/src/components/graph/AgentNode.tsx +324 -0
- flock/frontend/src/components/graph/GraphCanvas.tsx +613 -0
- flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
- flock/frontend/src/components/graph/MessageNode.test.tsx +64 -0
- flock/frontend/src/components/graph/MessageNode.tsx +129 -0
- flock/frontend/src/components/graph/MiniMap.tsx +47 -0
- flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
- flock/frontend/src/components/layout/DashboardLayout.css +420 -0
- flock/frontend/src/components/layout/DashboardLayout.tsx +287 -0
- flock/frontend/src/components/layout/Header.module.css +88 -0
- flock/frontend/src/components/layout/Header.tsx +52 -0
- flock/frontend/src/components/modules/HistoricalArtifactsModule.module.css +288 -0
- flock/frontend/src/components/modules/HistoricalArtifactsModule.tsx +450 -0
- flock/frontend/src/components/modules/HistoricalArtifactsModuleWrapper.tsx +13 -0
- flock/frontend/src/components/modules/JsonAttributeRenderer.tsx +140 -0
- flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
- flock/frontend/src/components/modules/ModuleRegistry.ts +93 -0
- flock/frontend/src/components/modules/ModuleWindow.tsx +223 -0
- flock/frontend/src/components/modules/TraceModuleJaeger.tsx +1971 -0
- flock/frontend/src/components/modules/TraceModuleJaegerWrapper.tsx +13 -0
- flock/frontend/src/components/modules/registerModules.ts +29 -0
- flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
- flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
- flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
- flock/frontend/src/components/settings/MultiSelect.tsx +235 -0
- flock/frontend/src/components/settings/SettingsPanel.css +327 -0
- flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
- flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
- flock/frontend/src/components/settings/TracingSettings.tsx +404 -0
- flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
- flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
- flock/frontend/src/hooks/useModulePersistence.ts +154 -0
- flock/frontend/src/hooks/useModules.ts +157 -0
- flock/frontend/src/hooks/usePersistence.ts +141 -0
- flock/frontend/src/main.tsx +13 -0
- flock/frontend/src/services/api.ts +337 -0
- flock/frontend/src/services/graphService.test.ts +330 -0
- flock/frontend/src/services/graphService.ts +75 -0
- flock/frontend/src/services/indexeddb.test.ts +793 -0
- flock/frontend/src/services/indexeddb.ts +848 -0
- flock/frontend/src/services/layout.test.ts +437 -0
- flock/frontend/src/services/layout.ts +357 -0
- flock/frontend/src/services/themeApplicator.ts +140 -0
- flock/frontend/src/services/themeService.ts +77 -0
- flock/frontend/src/services/websocket.ts +650 -0
- flock/frontend/src/store/filterStore.test.ts +250 -0
- flock/frontend/src/store/filterStore.ts +272 -0
- flock/frontend/src/store/graphStore.test.ts +570 -0
- flock/frontend/src/store/graphStore.ts +462 -0
- flock/frontend/src/store/moduleStore.test.ts +253 -0
- flock/frontend/src/store/moduleStore.ts +75 -0
- flock/frontend/src/store/settingsStore.ts +188 -0
- flock/frontend/src/store/streamStore.ts +68 -0
- flock/frontend/src/store/uiStore.test.ts +54 -0
- flock/frontend/src/store/uiStore.ts +122 -0
- flock/frontend/src/store/wsStore.ts +34 -0
- flock/frontend/src/styles/index.css +15 -0
- flock/frontend/src/styles/scrollbar.css +47 -0
- flock/frontend/src/styles/variables.css +488 -0
- flock/frontend/src/test/setup.ts +1 -0
- flock/frontend/src/types/filters.ts +47 -0
- flock/frontend/src/types/graph.ts +95 -0
- flock/frontend/src/types/modules.ts +10 -0
- flock/frontend/src/types/theme.ts +55 -0
- flock/frontend/src/utils/artifacts.ts +24 -0
- flock/frontend/src/utils/mockData.ts +98 -0
- flock/frontend/src/utils/performance.ts +16 -0
- flock/frontend/src/vite-env.d.ts +17 -0
- flock/frontend/tsconfig.json +27 -0
- flock/frontend/tsconfig.node.json +11 -0
- flock/frontend/vite.config.ts +25 -0
- flock/frontend/vitest.config.ts +11 -0
- flock/{core/util → helper}/cli_helper.py +9 -5
- flock/{core/logging → logging}/__init__.py +2 -3
- flock/logging/auto_trace.py +159 -0
- 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 -107
- flock/{core/logging → logging}/logging.py +78 -61
- flock/{core/logging → logging}/telemetry.py +66 -26
- flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
- flock/logging/telemetry_exporter/duckdb_exporter.py +216 -0
- flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +13 -10
- flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
- flock/logging/trace_and_logged.py +304 -0
- flock/mcp/__init__.py +91 -0
- flock/{core/mcp/mcp_client.py → mcp/client.py} +131 -158
- flock/{core/mcp/mcp_config.py → mcp/config.py} +86 -132
- flock/mcp/manager.py +286 -0
- flock/mcp/servers/sse/__init__.py +1 -1
- flock/mcp/servers/sse/flock_sse_server.py +16 -58
- flock/mcp/servers/stdio/__init__.py +1 -1
- flock/mcp/servers/stdio/flock_stdio_server.py +13 -53
- flock/mcp/servers/streamable_http/flock_streamable_http_server.py +22 -67
- flock/mcp/servers/websockets/flock_websocket_server.py +12 -45
- flock/{core/mcp/flock_mcp_tool_base.py → mcp/tool.py} +24 -78
- flock/mcp/types/__init__.py +42 -0
- flock/{core/mcp → mcp}/types/callbacks.py +12 -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 +3 -3
- flock/orchestrator.py +970 -0
- flock/registry.py +148 -0
- flock/runtime.py +262 -0
- flock/service.py +277 -0
- flock/store.py +1214 -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/darkmatrix.toml +2 -2
- flock/themes/darkside.toml +1 -1
- flock/themes/deep.toml +2 -2
- 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 +5 -5
- 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/utility/output_utility_component.py +226 -0
- flock/visibility.py +107 -0
- flock_core-0.5.0.dist-info/METADATA +964 -0
- flock_core-0.5.0.dist-info/RECORD +525 -0
- flock_core-0.5.0.dist-info/entry_points.txt +2 -0
- {flock_core-0.4.542.dist-info → flock_core-0.5.0.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/config.py +0 -56
- flock/core/__init__.py +0 -44
- flock/core/api/__init__.py +0 -10
- flock/core/api/custom_endpoint.py +0 -45
- flock/core/api/endpoints.py +0 -262
- flock/core/api/main.py +0 -162
- flock/core/api/models.py +0 -101
- flock/core/api/run_store.py +0 -224
- flock/core/api/runner.py +0 -44
- flock/core/api/service.py +0 -214
- flock/core/config/flock_agent_config.py +0 -11
- flock/core/config/scheduled_agent_config.py +0 -40
- flock/core/context/context.py +0 -214
- flock/core/context/context_manager.py +0 -40
- flock/core/context/context_vars.py +0 -11
- flock/core/evaluation/utils.py +0 -395
- 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 -166
- flock/core/flock.py +0 -1003
- flock/core/flock_agent.py +0 -1258
- flock/core/flock_evaluator.py +0 -60
- flock/core/flock_factory.py +0 -513
- flock/core/flock_module.py +0 -207
- flock/core/flock_registry.py +0 -702
- flock/core/flock_router.py +0 -83
- 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/logging/live_capture.py +0 -137
- flock/core/logging/trace_and_logged.py +0 -59
- flock/core/mcp/__init__.py +0 -1
- flock/core/mcp/flock_mcp_server.py +0 -640
- flock/core/mcp/mcp_client_manager.py +0 -201
- flock/core/mcp/types/__init__.py +0 -1
- flock/core/mixin/dspy_integration.py +0 -445
- flock/core/mixin/prompt_parser.py +0 -125
- flock/core/serialization/__init__.py +0 -13
- flock/core/serialization/callable_registry.py +0 -52
- flock/core/serialization/flock_serializer.py +0 -854
- 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 -409
- flock/core/util/file_path_utils.py +0 -223
- flock/core/util/hydrator.py +0 -309
- flock/core/util/input_resolver.py +0 -141
- flock/core/util/loader.py +0 -59
- flock/core/util/splitter.py +0 -219
- flock/di.py +0 -41
- flock/evaluators/__init__.py +0 -1
- flock/evaluators/declarative/__init__.py +0 -1
- flock/evaluators/declarative/declarative_evaluator.py +0 -217
- flock/evaluators/memory/memory_evaluator.py +0 -90
- flock/evaluators/test/test_case_evaluator.py +0 -38
- flock/evaluators/zep/zep_evaluator.py +0 -59
- flock/modules/__init__.py +0 -1
- flock/modules/assertion/__init__.py +0 -1
- flock/modules/assertion/assertion_module.py +0 -286
- flock/modules/callback/__init__.py +0 -1
- flock/modules/callback/callback_module.py +0 -91
- flock/modules/enterprise_memory/README.md +0 -99
- flock/modules/enterprise_memory/enterprise_memory_module.py +0 -526
- flock/modules/mem0/__init__.py +0 -1
- flock/modules/mem0/mem0_module.py +0 -126
- flock/modules/mem0_async/__init__.py +0 -1
- flock/modules/mem0_async/async_mem0_module.py +0 -126
- flock/modules/memory/__init__.py +0 -1
- flock/modules/memory/memory_module.py +0 -429
- flock/modules/memory/memory_parser.py +0 -125
- flock/modules/memory/memory_storage.py +0 -736
- flock/modules/output/__init__.py +0 -1
- flock/modules/output/output_module.py +0 -196
- flock/modules/performance/__init__.py +0 -1
- flock/modules/performance/metrics_module.py +0 -678
- flock/modules/zep/__init__.py +0 -1
- flock/modules/zep/zep_module.py +0 -192
- flock/platform/docker_tools.py +0 -49
- flock/platform/jaeger_install.py +0 -86
- flock/routers/__init__.py +0 -1
- flock/routers/agent/__init__.py +0 -1
- flock/routers/agent/agent_router.py +0 -236
- flock/routers/agent/handoff_agent.py +0 -58
- flock/routers/conditional/conditional_router.py +0 -486
- flock/routers/default/__init__.py +0 -1
- flock/routers/default/default_router.py +0 -80
- flock/routers/feedback/feedback_router.py +0 -114
- flock/routers/list_generator/list_generator_router.py +0 -166
- flock/routers/llm/__init__.py +0 -1
- flock/routers/llm/llm_router.py +0 -365
- flock/tools/__init__.py +0 -0
- flock/tools/azure_tools.py +0 -781
- flock/tools/code_tools.py +0 -167
- flock/tools/file_tools.py +0 -149
- flock/tools/github_tools.py +0 -157
- flock/tools/markdown_tools.py +0 -205
- flock/tools/system_tools.py +0 -9
- flock/tools/text_tools.py +0 -810
- flock/tools/web_tools.py +0 -92
- flock/tools/zendesk_tools.py +0 -501
- 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 -237
- flock/webapp/app/api/execution.py +0 -503
- flock/webapp/app/api/flock_management.py +0 -125
- flock/webapp/app/api/registry_viewer.py +0 -29
- flock/webapp/app/chat.py +0 -662
- flock/webapp/app/config.py +0 -104
- flock/webapp/app/dependencies.py +0 -117
- flock/webapp/app/main.py +0 -1086
- 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 -345
- flock/webapp/app/services/sharing_models.py +0 -81
- flock/webapp/app/services/sharing_store.py +0 -597
- flock/webapp/app/templates/theme_mapper.html +0 -326
- flock/webapp/app/theme_mapper.py +0 -811
- flock/webapp/app/utils.py +0 -85
- flock/webapp/run.py +0 -219
- 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 -281
- flock/webapp/static/css/sidebar.css +0 -127
- flock/webapp/static/css/two-pane.css +0 -48
- flock/webapp/templates/base.html +0 -389
- 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 -127
- 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/_live_logs.html +0 -13
- 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/_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 -237
- flock/workflow/agent_activities.py +0 -24
- flock/workflow/agent_execution_activity.py +0 -240
- flock/workflow/flock_workflow.py +0 -225
- flock/workflow/temporal_config.py +0 -96
- flock/workflow/temporal_setup.py +0 -60
- flock_core-0.4.542.dist-info/METADATA +0 -676
- flock_core-0.4.542.dist-info/RECORD +0 -572
- flock_core-0.4.542.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.4.542.dist-info → flock_core-0.5.0.dist-info}/WHEEL +0 -0
flock/core/flock_agent.py
DELETED
|
@@ -1,1258 +0,0 @@
|
|
|
1
|
-
# src/flock/core/flock_agent.py
|
|
2
|
-
"""FlockAgent is the core, declarative base class for all agents in the Flock framework."""
|
|
3
|
-
|
|
4
|
-
import asyncio
|
|
5
|
-
import json
|
|
6
|
-
import os
|
|
7
|
-
import uuid
|
|
8
|
-
from abc import ABC
|
|
9
|
-
from collections.abc import Callable
|
|
10
|
-
from datetime import datetime
|
|
11
|
-
from typing import TYPE_CHECKING, Any, TypeVar
|
|
12
|
-
|
|
13
|
-
from flock.core.config.flock_agent_config import FlockAgentConfig
|
|
14
|
-
from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
|
|
15
|
-
from flock.core.serialization.json_encoder import FlockJSONEncoder
|
|
16
|
-
from flock.workflow.temporal_config import TemporalActivityConfig
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from flock.core.context.context import FlockContext
|
|
20
|
-
from flock.core.flock_evaluator import FlockEvaluator
|
|
21
|
-
from flock.core.flock_module import FlockModule
|
|
22
|
-
from flock.core.flock_router import FlockRouter
|
|
23
|
-
|
|
24
|
-
from opentelemetry import trace
|
|
25
|
-
from pydantic import BaseModel, Field
|
|
26
|
-
|
|
27
|
-
# Core Flock components (ensure these are importable)
|
|
28
|
-
from flock.core.context.context import FlockContext
|
|
29
|
-
from flock.core.flock_evaluator import FlockEvaluator, FlockEvaluatorConfig
|
|
30
|
-
from flock.core.flock_module import FlockModule, FlockModuleConfig
|
|
31
|
-
from flock.core.flock_router import FlockRouter, FlockRouterConfig
|
|
32
|
-
from flock.core.logging.logging import get_logger
|
|
33
|
-
|
|
34
|
-
# Mixins and Serialization components
|
|
35
|
-
from flock.core.mixin.dspy_integration import DSPyIntegrationMixin
|
|
36
|
-
from flock.core.serialization.serializable import (
|
|
37
|
-
Serializable, # Import Serializable base
|
|
38
|
-
)
|
|
39
|
-
from flock.core.serialization.serialization_utils import (
|
|
40
|
-
deserialize_component,
|
|
41
|
-
serialize_item,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
logger = get_logger("agent")
|
|
45
|
-
tracer = trace.get_tracer(__name__)
|
|
46
|
-
T = TypeVar("T", bound="FlockAgent")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
SignatureType = (
|
|
50
|
-
str
|
|
51
|
-
| Callable[..., str]
|
|
52
|
-
| type[BaseModel]
|
|
53
|
-
| Callable[..., type[BaseModel]]
|
|
54
|
-
| None
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# Make FlockAgent inherit from Serializable
|
|
59
|
-
class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
|
|
60
|
-
"""Core, declarative base class for Flock agents, enabling serialization,
|
|
61
|
-
modularity, and integration with evaluation and routing components.
|
|
62
|
-
Inherits from Pydantic BaseModel, ABC, DSPyIntegrationMixin, and Serializable.
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
agent_id: str = Field(
|
|
66
|
-
default_factory=lambda: str(uuid.uuid4()),
|
|
67
|
-
description="Internal, Unique UUID4 for this agent instance. No need to set it manually. Used for MCP features.",
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
name: str = Field(..., description="Unique identifier for the agent.")
|
|
71
|
-
|
|
72
|
-
model: str | None = Field(
|
|
73
|
-
None,
|
|
74
|
-
description="The model identifier to use (e.g., 'openai/gpt-4o'). If None, uses Flock's default.",
|
|
75
|
-
)
|
|
76
|
-
description: str | Callable[..., str] | None = Field(
|
|
77
|
-
"",
|
|
78
|
-
description="A human-readable description or a callable returning one.",
|
|
79
|
-
)
|
|
80
|
-
input: SignatureType = Field(
|
|
81
|
-
None,
|
|
82
|
-
description=(
|
|
83
|
-
"Signature for input keys. Supports type hints (:) and descriptions (|). "
|
|
84
|
-
"E.g., 'query: str | Search query, context: dict | Conversation context'. Can be a callable."
|
|
85
|
-
),
|
|
86
|
-
)
|
|
87
|
-
output: SignatureType = Field(
|
|
88
|
-
None,
|
|
89
|
-
description=(
|
|
90
|
-
"Signature for output keys. Supports type hints (:) and descriptions (|). "
|
|
91
|
-
"E.g., 'result: str | Generated result, summary: str | Brief summary'. Can be a callable."
|
|
92
|
-
),
|
|
93
|
-
)
|
|
94
|
-
tools: list[Callable[..., Any]] | None = (
|
|
95
|
-
Field( # Assume tools are always callable for serialization simplicity
|
|
96
|
-
default=None,
|
|
97
|
-
description="List of callable tools the agent can use for development/default runs.",
|
|
98
|
-
)
|
|
99
|
-
)
|
|
100
|
-
production_tools: list[Callable[..., Any]] | None = Field(
|
|
101
|
-
default=None,
|
|
102
|
-
description="Optional list of callable tools to use when production tool set is enabled.",
|
|
103
|
-
)
|
|
104
|
-
servers: list[str | FlockMCPServerBase] | None = Field(
|
|
105
|
-
default=None,
|
|
106
|
-
description="List of MCP Servers the agent can use to enhance its capabilities. These must be registered.",
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
write_to_file: bool = Field(
|
|
110
|
-
default=False,
|
|
111
|
-
description="Write the agent's output to a file.",
|
|
112
|
-
)
|
|
113
|
-
wait_for_input: bool = Field(
|
|
114
|
-
default=False,
|
|
115
|
-
description="Wait for user input after the agent's output is displayed.",
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
# --- Components ---
|
|
119
|
-
evaluator: FlockEvaluator | None = Field( # Make optional, allow None
|
|
120
|
-
default=None,
|
|
121
|
-
description="The evaluator instance defining the agent's core logic.",
|
|
122
|
-
)
|
|
123
|
-
handoff_router: FlockRouter | None = Field( # Make optional, allow None
|
|
124
|
-
default=None,
|
|
125
|
-
description="Router determining the next agent in the workflow.",
|
|
126
|
-
)
|
|
127
|
-
modules: dict[str, FlockModule] = Field( # Keep as dict
|
|
128
|
-
default_factory=dict,
|
|
129
|
-
description="Dictionary of FlockModules attached to this agent.",
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
config: FlockAgentConfig = Field(
|
|
133
|
-
default_factory=lambda: FlockAgentConfig(),
|
|
134
|
-
description="Configuration for this agent, holding various settings and parameters.",
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
# --- Temporal Configuration (Optional) ---
|
|
138
|
-
temporal_activity_config: TemporalActivityConfig | None = Field(
|
|
139
|
-
default=None,
|
|
140
|
-
description="Optional Temporal settings specific to this agent's activity execution.",
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# --- Runtime State (Excluded from Serialization) ---
|
|
144
|
-
context: FlockContext | None = Field(
|
|
145
|
-
default=None,
|
|
146
|
-
exclude=True, # Exclude context from model_dump and serialization
|
|
147
|
-
description="Runtime context associated with the flock execution.",
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
def __init__(
|
|
151
|
-
self,
|
|
152
|
-
name: str,
|
|
153
|
-
model: str | None = None,
|
|
154
|
-
description: str | Callable[..., str] | None = "",
|
|
155
|
-
input: SignatureType = None,
|
|
156
|
-
output: SignatureType = None,
|
|
157
|
-
tools: list[Callable[..., Any]] | None = None,
|
|
158
|
-
production_tools: list[Callable[..., Any]] | None = None,
|
|
159
|
-
servers: list[str | FlockMCPServerBase] | None = None,
|
|
160
|
-
evaluator: "FlockEvaluator | None" = None,
|
|
161
|
-
handoff_router: "FlockRouter | None" = None,
|
|
162
|
-
# Use dict for modules
|
|
163
|
-
modules: dict[str, "FlockModule"] | None = None,
|
|
164
|
-
write_to_file: bool = False,
|
|
165
|
-
wait_for_input: bool = False,
|
|
166
|
-
temporal_activity_config: TemporalActivityConfig | None = None,
|
|
167
|
-
**kwargs,
|
|
168
|
-
):
|
|
169
|
-
super().__init__(
|
|
170
|
-
name=name,
|
|
171
|
-
model=model,
|
|
172
|
-
description=description,
|
|
173
|
-
input=input, # Store the raw input spec
|
|
174
|
-
output=output, # Store the raw output spec
|
|
175
|
-
tools=tools,
|
|
176
|
-
production_tools=production_tools,
|
|
177
|
-
servers=servers,
|
|
178
|
-
write_to_file=write_to_file,
|
|
179
|
-
wait_for_input=wait_for_input,
|
|
180
|
-
evaluator=evaluator,
|
|
181
|
-
handoff_router=handoff_router,
|
|
182
|
-
modules=modules
|
|
183
|
-
if modules is not None
|
|
184
|
-
else {}, # Ensure modules is a dict
|
|
185
|
-
temporal_activity_config=temporal_activity_config,
|
|
186
|
-
**kwargs,
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
if isinstance(self.input, type) and issubclass(self.input, BaseModel):
|
|
190
|
-
self._input_model = self.input
|
|
191
|
-
if isinstance(self.output, type) and issubclass(self.output, BaseModel):
|
|
192
|
-
self._output_model = self.output
|
|
193
|
-
|
|
194
|
-
# --- Existing Methods (add_module, remove_module, etc.) ---
|
|
195
|
-
# (Keep these methods as they were, adding type hints where useful)
|
|
196
|
-
def add_module(self, module: FlockModule) -> None:
|
|
197
|
-
"""Add a module to this agent."""
|
|
198
|
-
if not module.name:
|
|
199
|
-
logger.error("Module must have a name to be added.")
|
|
200
|
-
return
|
|
201
|
-
if module.name in self.modules:
|
|
202
|
-
logger.warning(f"Overwriting existing module: {module.name}")
|
|
203
|
-
self.modules[module.name] = module
|
|
204
|
-
logger.debug(f"Added module '{module.name}' to agent '{self.name}'")
|
|
205
|
-
|
|
206
|
-
def remove_module(self, module_name: str) -> None:
|
|
207
|
-
"""Remove a module from this agent."""
|
|
208
|
-
if module_name in self.modules:
|
|
209
|
-
del self.modules[module_name]
|
|
210
|
-
logger.debug(
|
|
211
|
-
f"Removed module '{module_name}' from agent '{self.name}'"
|
|
212
|
-
)
|
|
213
|
-
else:
|
|
214
|
-
logger.warning(
|
|
215
|
-
f"Module '{module_name}' not found on agent '{self.name}'."
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
def get_module(self, module_name: str) -> FlockModule | None:
|
|
219
|
-
"""Get a module by name."""
|
|
220
|
-
return self.modules.get(module_name)
|
|
221
|
-
|
|
222
|
-
def get_enabled_modules(self) -> list[FlockModule]:
|
|
223
|
-
"""Get a list of currently enabled modules attached to this agent."""
|
|
224
|
-
return [m for m in self.modules.values() if m.config.enabled]
|
|
225
|
-
|
|
226
|
-
@property
|
|
227
|
-
def resolved_description(self) -> str | None:
|
|
228
|
-
"""Returns the resolved agent description.
|
|
229
|
-
If the description is a callable, it attempts to call it.
|
|
230
|
-
Returns None if the description is None or a callable that fails.
|
|
231
|
-
"""
|
|
232
|
-
if callable(self.description):
|
|
233
|
-
try:
|
|
234
|
-
# Attempt to call without context first.
|
|
235
|
-
# If callables consistently need context, this might need adjustment
|
|
236
|
-
# or the template-facing property might need to be simpler,
|
|
237
|
-
# relying on prior resolution via resolve_callables.
|
|
238
|
-
return self.description()
|
|
239
|
-
except TypeError:
|
|
240
|
-
# Log a warning that context might be needed?
|
|
241
|
-
# For now, treat as unresolvable in this simple property.
|
|
242
|
-
logger.warning(
|
|
243
|
-
f"Callable description for agent '{self.name}' could not be resolved "
|
|
244
|
-
f"without context via the simple 'resolved_description' property. "
|
|
245
|
-
f"Consider calling 'agent.resolve_callables(context)' beforehand if context is required."
|
|
246
|
-
)
|
|
247
|
-
return None # Or a placeholder like "[Callable Description]"
|
|
248
|
-
except Exception as e:
|
|
249
|
-
logger.error(
|
|
250
|
-
f"Error resolving callable description for agent '{self.name}': {e}"
|
|
251
|
-
)
|
|
252
|
-
return None
|
|
253
|
-
elif isinstance(self.description, str):
|
|
254
|
-
return self.description
|
|
255
|
-
return None
|
|
256
|
-
|
|
257
|
-
# --- Lifecycle Hooks (Keep as they were) ---
|
|
258
|
-
async def initialize(self, inputs: dict[str, Any]) -> None:
|
|
259
|
-
"""Initialize agent and run module initializers."""
|
|
260
|
-
logger.debug(f"Initializing agent '{self.name}'")
|
|
261
|
-
with tracer.start_as_current_span("agent.initialize") as span:
|
|
262
|
-
span.set_attribute("agent.name", self.name)
|
|
263
|
-
span.set_attribute("inputs", str(inputs))
|
|
264
|
-
logger.info(
|
|
265
|
-
f"agent.initialize",
|
|
266
|
-
agent=self.name,
|
|
267
|
-
)
|
|
268
|
-
try:
|
|
269
|
-
for module in self.get_enabled_modules():
|
|
270
|
-
await module.on_initialize(self, inputs, self.context)
|
|
271
|
-
except Exception as module_error:
|
|
272
|
-
logger.error(
|
|
273
|
-
"Error during initialize",
|
|
274
|
-
agent=self.name,
|
|
275
|
-
error=str(module_error),
|
|
276
|
-
)
|
|
277
|
-
span.record_exception(module_error)
|
|
278
|
-
|
|
279
|
-
async def terminate(
|
|
280
|
-
self, inputs: dict[str, Any], result: dict[str, Any]
|
|
281
|
-
) -> None:
|
|
282
|
-
"""Terminate agent and run module terminators."""
|
|
283
|
-
logger.debug(f"Terminating agent '{self.name}'")
|
|
284
|
-
with tracer.start_as_current_span("agent.terminate") as span:
|
|
285
|
-
span.set_attribute("agent.name", self.name)
|
|
286
|
-
span.set_attribute("inputs", str(inputs))
|
|
287
|
-
span.set_attribute("result", str(result))
|
|
288
|
-
logger.info(
|
|
289
|
-
f"agent.terminate",
|
|
290
|
-
agent=self.name,
|
|
291
|
-
)
|
|
292
|
-
try:
|
|
293
|
-
current_result = result
|
|
294
|
-
for module in self.get_enabled_modules():
|
|
295
|
-
tmp_result = await module.on_terminate(
|
|
296
|
-
self, inputs, self.context, current_result
|
|
297
|
-
)
|
|
298
|
-
# If the module returns a result, use it
|
|
299
|
-
if tmp_result:
|
|
300
|
-
current_result = tmp_result
|
|
301
|
-
|
|
302
|
-
if self.write_to_file:
|
|
303
|
-
self._save_output(self.name, current_result)
|
|
304
|
-
|
|
305
|
-
if self.wait_for_input:
|
|
306
|
-
# simple input prompt
|
|
307
|
-
input("Press Enter to continue...")
|
|
308
|
-
|
|
309
|
-
except Exception as module_error:
|
|
310
|
-
logger.error(
|
|
311
|
-
"Error during terminate",
|
|
312
|
-
agent=self.name,
|
|
313
|
-
error=str(module_error),
|
|
314
|
-
)
|
|
315
|
-
span.record_exception(module_error)
|
|
316
|
-
|
|
317
|
-
async def on_error(self, error: Exception, inputs: dict[str, Any]) -> None:
|
|
318
|
-
"""Handle errors and run module error handlers."""
|
|
319
|
-
logger.error(f"Error occurred in agent '{self.name}': {error}")
|
|
320
|
-
with tracer.start_as_current_span("agent.on_error") as span:
|
|
321
|
-
span.set_attribute("agent.name", self.name)
|
|
322
|
-
span.set_attribute("inputs", str(inputs))
|
|
323
|
-
try:
|
|
324
|
-
for module in self.get_enabled_modules():
|
|
325
|
-
await module.on_error(self, inputs, self.context, error)
|
|
326
|
-
except Exception as module_error:
|
|
327
|
-
logger.error(
|
|
328
|
-
"Error during on_error",
|
|
329
|
-
agent=self.name,
|
|
330
|
-
error=str(module_error),
|
|
331
|
-
)
|
|
332
|
-
span.record_exception(module_error)
|
|
333
|
-
|
|
334
|
-
def _get_runtime_tools(self, use_production_tools: bool | None = None) -> list[Callable[..., Any]]:
|
|
335
|
-
"""Select the tool list for the current run."""
|
|
336
|
-
if use_production_tools and self.production_tools:
|
|
337
|
-
logger.info(
|
|
338
|
-
"Using production tool set",
|
|
339
|
-
agent=self.name,
|
|
340
|
-
tool_count=len(self.production_tools),
|
|
341
|
-
)
|
|
342
|
-
return list(self.production_tools)
|
|
343
|
-
|
|
344
|
-
if use_production_tools and not self.production_tools:
|
|
345
|
-
logger.warning(
|
|
346
|
-
"Production tool set requested but not configured; falling back to default tools",
|
|
347
|
-
agent=self.name,
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
return list(self.tools or [])
|
|
351
|
-
|
|
352
|
-
async def evaluate(
|
|
353
|
-
self, inputs: dict[str, Any], *, use_production_tools: bool | None = None
|
|
354
|
-
) -> dict[str, Any]:
|
|
355
|
-
"""Core evaluation logic, calling the assigned evaluator and modules."""
|
|
356
|
-
if not self.evaluator:
|
|
357
|
-
raise RuntimeError(
|
|
358
|
-
f"Agent '{self.name}' has no evaluator assigned."
|
|
359
|
-
)
|
|
360
|
-
with tracer.start_as_current_span("agent.evaluate") as span:
|
|
361
|
-
span.set_attribute("agent.name", self.name)
|
|
362
|
-
span.set_attribute("inputs", str(inputs))
|
|
363
|
-
logger.info(
|
|
364
|
-
f"agent.evaluate",
|
|
365
|
-
agent=self.name,
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
logger.debug(f"Evaluating agent '{self.name}'")
|
|
369
|
-
current_inputs = inputs
|
|
370
|
-
|
|
371
|
-
# Pre-evaluate hooks
|
|
372
|
-
for module in self.get_enabled_modules():
|
|
373
|
-
current_inputs = await module.on_pre_evaluate(
|
|
374
|
-
self, current_inputs, self.context
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
# Actual evaluation
|
|
378
|
-
try:
|
|
379
|
-
# Pass registered tools if the evaluator needs them
|
|
380
|
-
registered_tools = self._get_runtime_tools(use_production_tools)
|
|
381
|
-
|
|
382
|
-
# Retrieve available mcp_tools if the evaluator needs them
|
|
383
|
-
mcp_tools = []
|
|
384
|
-
if self.servers:
|
|
385
|
-
from flock.core.flock_registry import get_registry
|
|
386
|
-
|
|
387
|
-
FlockRegistry = get_registry() # Get the registry
|
|
388
|
-
for server in self.servers:
|
|
389
|
-
registered_server: FlockMCPServerBase | None = None
|
|
390
|
-
server_tools = []
|
|
391
|
-
if isinstance(server, FlockMCPServerBase):
|
|
392
|
-
# check if registered
|
|
393
|
-
server_name = server.config.name
|
|
394
|
-
registered_server = FlockRegistry.get_server(
|
|
395
|
-
server_name
|
|
396
|
-
)
|
|
397
|
-
else:
|
|
398
|
-
# servers must be registered.
|
|
399
|
-
registered_server = FlockRegistry.get_server(
|
|
400
|
-
name=server
|
|
401
|
-
)
|
|
402
|
-
if registered_server:
|
|
403
|
-
server_tools = await registered_server.get_tools(
|
|
404
|
-
agent_id=self.agent_id,
|
|
405
|
-
run_id=self.context.run_id,
|
|
406
|
-
)
|
|
407
|
-
else:
|
|
408
|
-
logger.warning(
|
|
409
|
-
f"No Server with name '{server.config.name}' registered! Skipping."
|
|
410
|
-
)
|
|
411
|
-
mcp_tools = mcp_tools + server_tools
|
|
412
|
-
|
|
413
|
-
# --------------------------------------------------
|
|
414
|
-
# Optional DI middleware pipeline
|
|
415
|
-
# --------------------------------------------------
|
|
416
|
-
container = None
|
|
417
|
-
if self.context is not None:
|
|
418
|
-
container = self.context.get_variable("di.container")
|
|
419
|
-
|
|
420
|
-
# If a MiddlewarePipeline is registered in DI, wrap the evaluator
|
|
421
|
-
result: dict[str, Any] | None = None
|
|
422
|
-
|
|
423
|
-
if container is not None:
|
|
424
|
-
try:
|
|
425
|
-
from wd.di.middleware import (
|
|
426
|
-
MiddlewarePipeline,
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
pipeline: MiddlewarePipeline | None = None
|
|
430
|
-
try:
|
|
431
|
-
pipeline = container.get_service(MiddlewarePipeline)
|
|
432
|
-
except Exception:
|
|
433
|
-
pipeline = None
|
|
434
|
-
|
|
435
|
-
if pipeline is not None:
|
|
436
|
-
# Build execution chain where the evaluator is the terminal handler
|
|
437
|
-
|
|
438
|
-
async def _final_handler():
|
|
439
|
-
return await self.evaluator.evaluate(
|
|
440
|
-
self,
|
|
441
|
-
current_inputs,
|
|
442
|
-
registered_tools,
|
|
443
|
-
mcp_tools=mcp_tools,
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
idx = 0
|
|
447
|
-
|
|
448
|
-
async def _invoke_next():
|
|
449
|
-
nonlocal idx
|
|
450
|
-
|
|
451
|
-
if idx < len(pipeline._middleware):
|
|
452
|
-
mw = pipeline._middleware[idx]
|
|
453
|
-
idx += 1
|
|
454
|
-
return await mw(self.context, _invoke_next) # type: ignore[arg-type]
|
|
455
|
-
return await _final_handler()
|
|
456
|
-
|
|
457
|
-
# Execute pipeline
|
|
458
|
-
result = await _invoke_next()
|
|
459
|
-
else:
|
|
460
|
-
# No pipeline registered, direct evaluation
|
|
461
|
-
result = await self.evaluator.evaluate(
|
|
462
|
-
self,
|
|
463
|
-
current_inputs,
|
|
464
|
-
registered_tools,
|
|
465
|
-
mcp_tools=mcp_tools,
|
|
466
|
-
)
|
|
467
|
-
except ImportError:
|
|
468
|
-
# wd.di not installed – fall back
|
|
469
|
-
result = await self.evaluator.evaluate(
|
|
470
|
-
self,
|
|
471
|
-
current_inputs,
|
|
472
|
-
registered_tools,
|
|
473
|
-
mcp_tools=mcp_tools,
|
|
474
|
-
)
|
|
475
|
-
else:
|
|
476
|
-
# No DI container – standard execution
|
|
477
|
-
result = await self.evaluator.evaluate(
|
|
478
|
-
self,
|
|
479
|
-
current_inputs,
|
|
480
|
-
registered_tools,
|
|
481
|
-
mcp_tools=mcp_tools,
|
|
482
|
-
)
|
|
483
|
-
except Exception as eval_error:
|
|
484
|
-
logger.error(
|
|
485
|
-
"Error during evaluate",
|
|
486
|
-
agent=self.name,
|
|
487
|
-
error=str(eval_error),
|
|
488
|
-
)
|
|
489
|
-
span.record_exception(eval_error)
|
|
490
|
-
await self.on_error(
|
|
491
|
-
eval_error, current_inputs
|
|
492
|
-
) # Call error hook
|
|
493
|
-
raise # Re-raise the exception
|
|
494
|
-
|
|
495
|
-
# Post-evaluate hooks
|
|
496
|
-
current_result = result
|
|
497
|
-
for module in self.get_enabled_modules():
|
|
498
|
-
tmp_result = await module.on_post_evaluate(
|
|
499
|
-
self,
|
|
500
|
-
current_inputs,
|
|
501
|
-
self.context,
|
|
502
|
-
current_result,
|
|
503
|
-
)
|
|
504
|
-
# If the module returns a result, use it
|
|
505
|
-
if tmp_result:
|
|
506
|
-
current_result = tmp_result
|
|
507
|
-
|
|
508
|
-
logger.debug(f"Evaluation completed for agent '{self.name}'")
|
|
509
|
-
return current_result
|
|
510
|
-
|
|
511
|
-
def run(
|
|
512
|
-
self, inputs: dict[str, Any], *, use_production_tools: bool | None = None
|
|
513
|
-
) -> dict[str, Any]:
|
|
514
|
-
"""Synchronous wrapper for run_async."""
|
|
515
|
-
try:
|
|
516
|
-
loop = asyncio.get_running_loop()
|
|
517
|
-
except (
|
|
518
|
-
RuntimeError
|
|
519
|
-
): # 'RuntimeError: There is no current event loop...'
|
|
520
|
-
loop = asyncio.new_event_loop()
|
|
521
|
-
asyncio.set_event_loop(loop)
|
|
522
|
-
return loop.run_until_complete(
|
|
523
|
-
self.run_async(inputs, use_production_tools=use_production_tools)
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
def set_model(self, model: str):
|
|
527
|
-
"""Set the model for the agent and its evaluator."""
|
|
528
|
-
self.model = model
|
|
529
|
-
if self.evaluator and hasattr(self.evaluator, "config"):
|
|
530
|
-
self.evaluator.config.model = model
|
|
531
|
-
logger.info(
|
|
532
|
-
f"Set model to '{model}' for agent '{self.name}' and its evaluator."
|
|
533
|
-
)
|
|
534
|
-
elif self.evaluator:
|
|
535
|
-
logger.warning(
|
|
536
|
-
f"Evaluator for agent '{self.name}' does not have a standard config to set model."
|
|
537
|
-
)
|
|
538
|
-
else:
|
|
539
|
-
logger.warning(
|
|
540
|
-
f"Agent '{self.name}' has no evaluator to set model for."
|
|
541
|
-
)
|
|
542
|
-
|
|
543
|
-
async def run_async(
|
|
544
|
-
self, inputs: dict[str, Any], *, use_production_tools: bool | None = None
|
|
545
|
-
) -> dict[str, Any]:
|
|
546
|
-
"""Asynchronous execution logic with lifecycle hooks."""
|
|
547
|
-
with tracer.start_as_current_span("agent.run") as span:
|
|
548
|
-
span.set_attribute("agent.name", self.name)
|
|
549
|
-
span.set_attribute("inputs", str(inputs))
|
|
550
|
-
try:
|
|
551
|
-
await self.initialize(inputs)
|
|
552
|
-
result = await self.evaluate(
|
|
553
|
-
inputs, use_production_tools=use_production_tools
|
|
554
|
-
)
|
|
555
|
-
await self.terminate(inputs, result)
|
|
556
|
-
span.set_attribute("result", str(result))
|
|
557
|
-
logger.info("Agent run completed", agent=self.name)
|
|
558
|
-
return result
|
|
559
|
-
except Exception as run_error:
|
|
560
|
-
logger.error(
|
|
561
|
-
"Error running agent", agent=self.name, error=str(run_error)
|
|
562
|
-
)
|
|
563
|
-
if "evaluate" not in str(
|
|
564
|
-
run_error
|
|
565
|
-
): # Simple check, might need refinement
|
|
566
|
-
await self.on_error(run_error, inputs)
|
|
567
|
-
logger.error(
|
|
568
|
-
f"Agent '{self.name}' run failed: {run_error}",
|
|
569
|
-
exc_info=True,
|
|
570
|
-
)
|
|
571
|
-
span.record_exception(run_error)
|
|
572
|
-
raise # Re-raise after handling
|
|
573
|
-
|
|
574
|
-
async def run_temporal(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
575
|
-
with tracer.start_as_current_span("agent.run_temporal") as span:
|
|
576
|
-
span.set_attribute("agent.name", self.name)
|
|
577
|
-
span.set_attribute("inputs", str(inputs))
|
|
578
|
-
try:
|
|
579
|
-
from temporalio.client import Client
|
|
580
|
-
|
|
581
|
-
from flock.workflow.agent_activities import (
|
|
582
|
-
run_flock_agent_activity,
|
|
583
|
-
)
|
|
584
|
-
from flock.workflow.temporal_setup import run_activity
|
|
585
|
-
|
|
586
|
-
client = await Client.connect(
|
|
587
|
-
"localhost:7233", namespace="default"
|
|
588
|
-
)
|
|
589
|
-
agent_data = self.to_dict()
|
|
590
|
-
inputs_data = inputs
|
|
591
|
-
|
|
592
|
-
result = await run_activity(
|
|
593
|
-
client,
|
|
594
|
-
self.name,
|
|
595
|
-
run_flock_agent_activity,
|
|
596
|
-
{"agent_data": agent_data, "inputs": inputs_data},
|
|
597
|
-
)
|
|
598
|
-
span.set_attribute("result", str(result))
|
|
599
|
-
logger.info("Temporal run successful", agent=self.name)
|
|
600
|
-
return result
|
|
601
|
-
except Exception as temporal_error:
|
|
602
|
-
logger.error(
|
|
603
|
-
"Error in Temporal workflow",
|
|
604
|
-
agent=self.name,
|
|
605
|
-
error=str(temporal_error),
|
|
606
|
-
)
|
|
607
|
-
span.record_exception(temporal_error)
|
|
608
|
-
raise
|
|
609
|
-
|
|
610
|
-
def add_component(
|
|
611
|
-
self,
|
|
612
|
-
config_instance: FlockModuleConfig
|
|
613
|
-
| FlockRouterConfig
|
|
614
|
-
| FlockEvaluatorConfig,
|
|
615
|
-
component_name: str | None = None,
|
|
616
|
-
) -> "FlockAgent":
|
|
617
|
-
"""Adds or replaces a component (Evaluator, Router, Module) based on its configuration object.
|
|
618
|
-
|
|
619
|
-
Args:
|
|
620
|
-
config_instance: An instance of a config class inheriting from
|
|
621
|
-
FlockModuleConfig, FlockRouterConfig, or FlockEvaluatorConfig.
|
|
622
|
-
component_name: Explicit name for the component (required for Modules if not in config).
|
|
623
|
-
|
|
624
|
-
Returns:
|
|
625
|
-
self for potential chaining.
|
|
626
|
-
"""
|
|
627
|
-
from flock.core.flock_registry import get_registry
|
|
628
|
-
|
|
629
|
-
config_type = type(config_instance)
|
|
630
|
-
registry = get_registry() # Get registry instance
|
|
631
|
-
logger.debug(
|
|
632
|
-
f"Attempting to add component via config: {config_type.__name__}"
|
|
633
|
-
)
|
|
634
|
-
|
|
635
|
-
# --- 1. Find Component Class using Registry Map ---
|
|
636
|
-
ComponentClass = registry.get_component_class_for_config(config_type)
|
|
637
|
-
|
|
638
|
-
if not ComponentClass:
|
|
639
|
-
logger.error(
|
|
640
|
-
f"No component class registered for config type {config_type.__name__}. Use @flock_component(config_class=...) on the component."
|
|
641
|
-
)
|
|
642
|
-
raise TypeError(
|
|
643
|
-
f"Cannot find component class for config {config_type.__name__}"
|
|
644
|
-
)
|
|
645
|
-
|
|
646
|
-
component_class_name = ComponentClass.__name__
|
|
647
|
-
logger.debug(
|
|
648
|
-
f"Found component class '{component_class_name}' mapped to config '{config_type.__name__}'"
|
|
649
|
-
)
|
|
650
|
-
|
|
651
|
-
# --- 2. Determine Assignment Target and Name (Same as before) ---
|
|
652
|
-
instance_name = component_name
|
|
653
|
-
attribute_name: str = ""
|
|
654
|
-
|
|
655
|
-
if issubclass(ComponentClass, FlockEvaluator):
|
|
656
|
-
attribute_name = "evaluator"
|
|
657
|
-
if not instance_name:
|
|
658
|
-
instance_name = getattr(
|
|
659
|
-
config_instance, "name", component_class_name.lower()
|
|
660
|
-
)
|
|
661
|
-
|
|
662
|
-
elif issubclass(ComponentClass, FlockRouter):
|
|
663
|
-
attribute_name = "handoff_router"
|
|
664
|
-
if not instance_name:
|
|
665
|
-
instance_name = getattr(
|
|
666
|
-
config_instance, "name", component_class_name.lower()
|
|
667
|
-
)
|
|
668
|
-
|
|
669
|
-
elif issubclass(ComponentClass, FlockModule):
|
|
670
|
-
attribute_name = "modules"
|
|
671
|
-
if not instance_name:
|
|
672
|
-
instance_name = getattr(
|
|
673
|
-
config_instance, "name", component_class_name.lower()
|
|
674
|
-
)
|
|
675
|
-
if not instance_name:
|
|
676
|
-
raise ValueError(
|
|
677
|
-
"Module name must be provided either in config or as component_name argument."
|
|
678
|
-
)
|
|
679
|
-
# Ensure config has name if module expects it
|
|
680
|
-
if hasattr(config_instance, "name") and not getattr(
|
|
681
|
-
config_instance, "name", None
|
|
682
|
-
):
|
|
683
|
-
setattr(config_instance, "name", instance_name)
|
|
684
|
-
|
|
685
|
-
else: # Should be caught by registry map logic ideally
|
|
686
|
-
raise TypeError(
|
|
687
|
-
f"Class '{component_class_name}' mapped from config is not a valid Flock component."
|
|
688
|
-
)
|
|
689
|
-
|
|
690
|
-
# --- 3. Instantiate the Component (Same as before) ---
|
|
691
|
-
try:
|
|
692
|
-
init_args = {"config": config_instance, "name": instance_name}
|
|
693
|
-
|
|
694
|
-
component_instance = ComponentClass(**init_args)
|
|
695
|
-
except Exception as e:
|
|
696
|
-
logger.error(
|
|
697
|
-
f"Failed to instantiate {ComponentClass.__name__} with config {config_type.__name__}: {e}",
|
|
698
|
-
exc_info=True,
|
|
699
|
-
)
|
|
700
|
-
raise RuntimeError(f"Component instantiation failed: {e}") from e
|
|
701
|
-
|
|
702
|
-
# --- 4. Assign to the Agent (Same as before) ---
|
|
703
|
-
if attribute_name == "modules":
|
|
704
|
-
if not isinstance(self.modules, dict):
|
|
705
|
-
self.modules = {}
|
|
706
|
-
self.modules[instance_name] = component_instance
|
|
707
|
-
logger.info(
|
|
708
|
-
f"Added/Updated module '{instance_name}' (type: {ComponentClass.__name__}) to agent '{self.name}'"
|
|
709
|
-
)
|
|
710
|
-
else:
|
|
711
|
-
setattr(self, attribute_name, component_instance)
|
|
712
|
-
logger.info(
|
|
713
|
-
f"Set {attribute_name} to {ComponentClass.__name__} (instance name: '{instance_name}') for agent '{self.name}'"
|
|
714
|
-
)
|
|
715
|
-
|
|
716
|
-
return self
|
|
717
|
-
|
|
718
|
-
# resolve_callables remains useful for dynamic definitions
|
|
719
|
-
def resolve_callables(self, context: FlockContext | None = None) -> None:
|
|
720
|
-
"""Resolves callable fields (description, input, output) using context."""
|
|
721
|
-
if callable(self.description):
|
|
722
|
-
self.description = self.description(
|
|
723
|
-
context
|
|
724
|
-
) # Pass context if needed by callable
|
|
725
|
-
if callable(self.input):
|
|
726
|
-
self.input = self.input(context)
|
|
727
|
-
if callable(self.output):
|
|
728
|
-
self.output = self.output(context)
|
|
729
|
-
|
|
730
|
-
# --- Serialization Implementation ---
|
|
731
|
-
|
|
732
|
-
def _save_output(self, agent_name: str, result: dict[str, Any]) -> None:
|
|
733
|
-
"""Save output to file if configured."""
|
|
734
|
-
if not self.write_to_file:
|
|
735
|
-
return
|
|
736
|
-
|
|
737
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
738
|
-
filename = f"{agent_name}_output_{timestamp}.json"
|
|
739
|
-
filepath = os.path.join(".flock/output/", filename)
|
|
740
|
-
os.makedirs(".flock/output/", exist_ok=True)
|
|
741
|
-
|
|
742
|
-
output_data = {
|
|
743
|
-
"agent": agent_name,
|
|
744
|
-
"timestamp": timestamp,
|
|
745
|
-
"output": result,
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
try:
|
|
749
|
-
with open(filepath, "w") as f:
|
|
750
|
-
json.dump(output_data, f, indent=2, cls=FlockJSONEncoder)
|
|
751
|
-
except Exception as e:
|
|
752
|
-
logger.warning(f"Failed to save output to file: {e}")
|
|
753
|
-
|
|
754
|
-
def to_dict(self) -> dict[str, Any]:
|
|
755
|
-
"""Convert instance to dictionary representation suitable for serialization."""
|
|
756
|
-
from flock.core.flock_registry import get_registry
|
|
757
|
-
|
|
758
|
-
FlockRegistry = get_registry()
|
|
759
|
-
|
|
760
|
-
exclude = [
|
|
761
|
-
"context",
|
|
762
|
-
"evaluator",
|
|
763
|
-
"modules",
|
|
764
|
-
"handoff_router",
|
|
765
|
-
"tools",
|
|
766
|
-
"production_tools",
|
|
767
|
-
"servers",
|
|
768
|
-
]
|
|
769
|
-
|
|
770
|
-
is_descrition_callable = False
|
|
771
|
-
is_input_callable = False
|
|
772
|
-
is_output_callable = False
|
|
773
|
-
|
|
774
|
-
# if self.description is a callable, exclude it
|
|
775
|
-
if callable(self.description):
|
|
776
|
-
is_descrition_callable = True
|
|
777
|
-
exclude.append("description")
|
|
778
|
-
# if self.input is a callable, exclude it
|
|
779
|
-
if callable(self.input):
|
|
780
|
-
is_input_callable = True
|
|
781
|
-
exclude.append("input")
|
|
782
|
-
# if self.output is a callable, exclude it
|
|
783
|
-
if callable(self.output):
|
|
784
|
-
is_output_callable = True
|
|
785
|
-
exclude.append("output")
|
|
786
|
-
|
|
787
|
-
logger.debug(f"Serializing agent '{self.name}' to dict.")
|
|
788
|
-
# Use Pydantic's dump, exclude manually handled fields and runtime context
|
|
789
|
-
data = self.model_dump(
|
|
790
|
-
exclude=exclude,
|
|
791
|
-
mode="json", # Use json mode for better handling of standard types by Pydantic
|
|
792
|
-
exclude_none=True, # Exclude None values for cleaner output
|
|
793
|
-
)
|
|
794
|
-
logger.debug(f"Base agent data for '{self.name}': {list(data.keys())}")
|
|
795
|
-
serialized_modules = {}
|
|
796
|
-
|
|
797
|
-
def add_serialized_component(component: Any, field_name: str):
|
|
798
|
-
if component:
|
|
799
|
-
comp_type = type(component)
|
|
800
|
-
type_name = FlockRegistry.get_component_type_name(
|
|
801
|
-
comp_type
|
|
802
|
-
) # Get registered name
|
|
803
|
-
if type_name:
|
|
804
|
-
try:
|
|
805
|
-
serialized_component_data = serialize_item(component)
|
|
806
|
-
|
|
807
|
-
if not isinstance(serialized_component_data, dict):
|
|
808
|
-
logger.error(
|
|
809
|
-
f"Serialization of component {type_name} for field '{field_name}' did not result in a dictionary. Got: {type(serialized_component_data)}"
|
|
810
|
-
)
|
|
811
|
-
serialized_modules[field_name] = {
|
|
812
|
-
"type": type_name,
|
|
813
|
-
"name": getattr(component, "name", "unknown"),
|
|
814
|
-
"error": "serialization_failed_non_dict",
|
|
815
|
-
}
|
|
816
|
-
else:
|
|
817
|
-
serialized_component_data["type"] = type_name
|
|
818
|
-
serialized_modules[field_name] = (
|
|
819
|
-
serialized_component_data
|
|
820
|
-
)
|
|
821
|
-
logger.debug(
|
|
822
|
-
f"Successfully serialized component for field '{field_name}' (type: {type_name})"
|
|
823
|
-
)
|
|
824
|
-
|
|
825
|
-
except Exception as e:
|
|
826
|
-
logger.error(
|
|
827
|
-
f"Failed to serialize component {type_name} for field '{field_name}': {e}",
|
|
828
|
-
exc_info=True,
|
|
829
|
-
)
|
|
830
|
-
serialized_modules[field_name] = {
|
|
831
|
-
"type": type_name,
|
|
832
|
-
"name": getattr(component, "name", "unknown"),
|
|
833
|
-
"error": "serialization_failed",
|
|
834
|
-
}
|
|
835
|
-
else:
|
|
836
|
-
logger.warning(
|
|
837
|
-
f"Cannot serialize unregistered component {comp_type.__name__} for field '{field_name}'"
|
|
838
|
-
)
|
|
839
|
-
|
|
840
|
-
add_serialized_component(self.evaluator, "evaluator")
|
|
841
|
-
if serialized_modules:
|
|
842
|
-
data["evaluator"] = serialized_modules["evaluator"]
|
|
843
|
-
logger.debug(f"Added evaluator to agent '{self.name}'")
|
|
844
|
-
|
|
845
|
-
serialized_modules = {}
|
|
846
|
-
add_serialized_component(self.handoff_router, "handoff_router")
|
|
847
|
-
if serialized_modules:
|
|
848
|
-
data["handoff_router"] = serialized_modules["handoff_router"]
|
|
849
|
-
logger.debug(f"Added handoff_router to agent '{self.name}'")
|
|
850
|
-
|
|
851
|
-
serialized_modules = {}
|
|
852
|
-
for module in self.modules.values():
|
|
853
|
-
add_serialized_component(module, module.name)
|
|
854
|
-
|
|
855
|
-
if serialized_modules:
|
|
856
|
-
data["modules"] = serialized_modules
|
|
857
|
-
logger.debug(
|
|
858
|
-
f"Added {len(serialized_modules)} modules to agent '{self.name}'"
|
|
859
|
-
)
|
|
860
|
-
|
|
861
|
-
# --- Serialize Servers ---
|
|
862
|
-
if self.servers:
|
|
863
|
-
logger.debug(
|
|
864
|
-
f"Serializing {len(self.servers)} servers for agent '{self.name}'"
|
|
865
|
-
)
|
|
866
|
-
serialized_servers = []
|
|
867
|
-
for server in self.servers:
|
|
868
|
-
if isinstance(server, FlockMCPServerBase):
|
|
869
|
-
serialized_servers.append(server.config.name)
|
|
870
|
-
else:
|
|
871
|
-
# Write it down as a list of server names.
|
|
872
|
-
serialized_servers.append(server)
|
|
873
|
-
|
|
874
|
-
if serialized_servers:
|
|
875
|
-
data["mcp_servers"] = serialized_servers
|
|
876
|
-
logger.debug(
|
|
877
|
-
f"Added {len(serialized_servers)} servers to agent '{self.name}'"
|
|
878
|
-
)
|
|
879
|
-
|
|
880
|
-
# --- Serialize Tools (Callables) ---
|
|
881
|
-
if self.tools:
|
|
882
|
-
logger.debug(
|
|
883
|
-
f"Serializing {len(self.tools)} tools for agent '{self.name}'"
|
|
884
|
-
)
|
|
885
|
-
serialized_tools = []
|
|
886
|
-
for tool in self.tools:
|
|
887
|
-
if callable(tool) and not isinstance(tool, type):
|
|
888
|
-
path_str = FlockRegistry.get_callable_path_string(tool)
|
|
889
|
-
if path_str:
|
|
890
|
-
# Get just the function name from the path string
|
|
891
|
-
# If it's a namespaced path like module.submodule.function_name
|
|
892
|
-
# Just use the function_name part
|
|
893
|
-
func_name = path_str.split(".")[-1]
|
|
894
|
-
serialized_tools.append(func_name)
|
|
895
|
-
logger.debug(
|
|
896
|
-
f"Added tool '{func_name}' (from path '{path_str}') to agent '{self.name}'"
|
|
897
|
-
)
|
|
898
|
-
else:
|
|
899
|
-
logger.warning(
|
|
900
|
-
f"Could not get path string for tool {tool} in agent '{self.name}'. Skipping."
|
|
901
|
-
)
|
|
902
|
-
else:
|
|
903
|
-
logger.warning(
|
|
904
|
-
f"Non-callable item found in tools list for agent '{self.name}': {tool}. Skipping."
|
|
905
|
-
)
|
|
906
|
-
if serialized_tools:
|
|
907
|
-
data["tools"] = serialized_tools
|
|
908
|
-
logger.debug(
|
|
909
|
-
f"Added {len(serialized_tools)} tools to agent '{self.name}'"
|
|
910
|
-
)
|
|
911
|
-
|
|
912
|
-
if self.production_tools:
|
|
913
|
-
logger.debug(
|
|
914
|
-
f"Serializing {len(self.production_tools)} production tools for agent '{self.name}'"
|
|
915
|
-
)
|
|
916
|
-
serialized_prod_tools = []
|
|
917
|
-
for tool in self.production_tools:
|
|
918
|
-
if callable(tool) and not isinstance(tool, type):
|
|
919
|
-
path_str = FlockRegistry.get_callable_path_string(tool)
|
|
920
|
-
if path_str:
|
|
921
|
-
func_name = path_str.split(".")[-1]
|
|
922
|
-
serialized_prod_tools.append(func_name)
|
|
923
|
-
logger.debug(
|
|
924
|
-
f"Added production tool '{func_name}' (from path '{path_str}') to agent '{self.name}'"
|
|
925
|
-
)
|
|
926
|
-
else:
|
|
927
|
-
logger.warning(
|
|
928
|
-
f"Could not get path string for production tool {tool} in agent '{self.name}'. Skipping."
|
|
929
|
-
)
|
|
930
|
-
else:
|
|
931
|
-
logger.warning(
|
|
932
|
-
f"Non-callable item found in production tools list for agent '{self.name}': {tool}. Skipping."
|
|
933
|
-
)
|
|
934
|
-
if serialized_prod_tools:
|
|
935
|
-
data["production_tools"] = serialized_prod_tools
|
|
936
|
-
logger.debug(
|
|
937
|
-
f"Added {len(serialized_prod_tools)} production tools to agent '{self.name}'"
|
|
938
|
-
)
|
|
939
|
-
|
|
940
|
-
if is_descrition_callable:
|
|
941
|
-
path_str = FlockRegistry.get_callable_path_string(self.description)
|
|
942
|
-
if path_str:
|
|
943
|
-
func_name = path_str.split(".")[-1]
|
|
944
|
-
data["description_callable"] = func_name
|
|
945
|
-
logger.debug(
|
|
946
|
-
f"Added description '{func_name}' (from path '{path_str}') to agent '{self.name}'"
|
|
947
|
-
)
|
|
948
|
-
else:
|
|
949
|
-
logger.warning(
|
|
950
|
-
f"Could not get path string for description {self.description} in agent '{self.name}'. Skipping."
|
|
951
|
-
)
|
|
952
|
-
|
|
953
|
-
if is_input_callable:
|
|
954
|
-
path_str = FlockRegistry.get_callable_path_string(self.input)
|
|
955
|
-
if path_str:
|
|
956
|
-
func_name = path_str.split(".")[-1]
|
|
957
|
-
data["input_callable"] = func_name
|
|
958
|
-
logger.debug(
|
|
959
|
-
f"Added input '{func_name}' (from path '{path_str}') to agent '{self.name}'"
|
|
960
|
-
)
|
|
961
|
-
else:
|
|
962
|
-
logger.warning(
|
|
963
|
-
f"Could not get path string for input {self.input} in agent '{self.name}'. Skipping."
|
|
964
|
-
)
|
|
965
|
-
|
|
966
|
-
if is_output_callable:
|
|
967
|
-
path_str = FlockRegistry.get_callable_path_string(self.output)
|
|
968
|
-
if path_str:
|
|
969
|
-
func_name = path_str.split(".")[-1]
|
|
970
|
-
data["output_callable"] = func_name
|
|
971
|
-
logger.debug(
|
|
972
|
-
f"Added output '{func_name}' (from path '{path_str}') to agent '{self.name}'"
|
|
973
|
-
)
|
|
974
|
-
else:
|
|
975
|
-
logger.warning(
|
|
976
|
-
f"Could not get path string for output {self.output} in agent '{self.name}'. Skipping."
|
|
977
|
-
)
|
|
978
|
-
|
|
979
|
-
# No need to call _filter_none_values here as model_dump(exclude_none=True) handles it
|
|
980
|
-
logger.info(
|
|
981
|
-
f"Serialization of agent '{self.name}' complete with {len(data)} fields"
|
|
982
|
-
)
|
|
983
|
-
return data
|
|
984
|
-
|
|
985
|
-
@classmethod
|
|
986
|
-
def from_dict(cls: type[T], data: dict[str, Any]) -> T:
|
|
987
|
-
"""Deserialize the agent from a dictionary, including components, tools, and callables."""
|
|
988
|
-
from flock.core.flock_registry import (
|
|
989
|
-
get_registry, # Import registry locally
|
|
990
|
-
)
|
|
991
|
-
|
|
992
|
-
registry = get_registry()
|
|
993
|
-
logger.debug(
|
|
994
|
-
f"Deserializing agent from dict. Keys: {list(data.keys())}"
|
|
995
|
-
)
|
|
996
|
-
|
|
997
|
-
# --- Separate Data ---
|
|
998
|
-
component_configs = {}
|
|
999
|
-
callable_configs = {}
|
|
1000
|
-
tool_config = []
|
|
1001
|
-
production_tool_config = []
|
|
1002
|
-
servers_config = []
|
|
1003
|
-
agent_data = {}
|
|
1004
|
-
|
|
1005
|
-
component_keys = [
|
|
1006
|
-
"evaluator",
|
|
1007
|
-
"handoff_router",
|
|
1008
|
-
"modules",
|
|
1009
|
-
"temporal_activity_config",
|
|
1010
|
-
]
|
|
1011
|
-
callable_keys = [
|
|
1012
|
-
"description_callable",
|
|
1013
|
-
"input_callable",
|
|
1014
|
-
"output_callable",
|
|
1015
|
-
]
|
|
1016
|
-
tool_key = "tools"
|
|
1017
|
-
production_tool_key = "production_tools"
|
|
1018
|
-
|
|
1019
|
-
servers_key = "mcp_servers"
|
|
1020
|
-
|
|
1021
|
-
for key, value in data.items():
|
|
1022
|
-
if key in component_keys and value is not None:
|
|
1023
|
-
component_configs[key] = value
|
|
1024
|
-
elif key in callable_keys and value is not None:
|
|
1025
|
-
callable_configs[key] = value
|
|
1026
|
-
elif key == tool_key and value is not None:
|
|
1027
|
-
tool_config = value # Expecting a list of names
|
|
1028
|
-
elif key == production_tool_key and value is not None:
|
|
1029
|
-
production_tool_config = value
|
|
1030
|
-
elif key == servers_key and value is not None:
|
|
1031
|
-
servers_config = value # Expecting a list of names
|
|
1032
|
-
elif key not in component_keys + callable_keys + [
|
|
1033
|
-
tool_key,
|
|
1034
|
-
production_tool_key,
|
|
1035
|
-
servers_key,
|
|
1036
|
-
]: # Avoid double adding
|
|
1037
|
-
agent_data[key] = value
|
|
1038
|
-
# else: ignore keys that are None or already handled
|
|
1039
|
-
|
|
1040
|
-
# --- Deserialize Base Agent ---
|
|
1041
|
-
# Ensure required fields like 'name' are present if needed by __init__
|
|
1042
|
-
if "name" not in agent_data:
|
|
1043
|
-
raise ValueError(
|
|
1044
|
-
"Agent data must include a 'name' field for deserialization."
|
|
1045
|
-
)
|
|
1046
|
-
agent_name_log = agent_data["name"] # For logging
|
|
1047
|
-
logger.info(f"Deserializing base agent data for '{agent_name_log}'")
|
|
1048
|
-
|
|
1049
|
-
# Pydantic should handle base fields based on type hints in __init__
|
|
1050
|
-
agent = cls(**agent_data)
|
|
1051
|
-
logger.debug(f"Base agent '{agent.name}' instantiated.")
|
|
1052
|
-
|
|
1053
|
-
# --- Deserialize Components ---
|
|
1054
|
-
logger.debug(f"Deserializing components for '{agent.name}'")
|
|
1055
|
-
# Evaluator
|
|
1056
|
-
if "evaluator" in component_configs:
|
|
1057
|
-
try:
|
|
1058
|
-
agent.evaluator = deserialize_component(
|
|
1059
|
-
component_configs["evaluator"], FlockEvaluator
|
|
1060
|
-
)
|
|
1061
|
-
logger.debug(f"Deserialized evaluator for '{agent.name}'")
|
|
1062
|
-
except Exception as e:
|
|
1063
|
-
logger.error(
|
|
1064
|
-
f"Failed to deserialize evaluator for '{agent.name}': {e}",
|
|
1065
|
-
exc_info=True,
|
|
1066
|
-
)
|
|
1067
|
-
|
|
1068
|
-
# Handoff Router
|
|
1069
|
-
if "handoff_router" in component_configs:
|
|
1070
|
-
try:
|
|
1071
|
-
agent.handoff_router = deserialize_component(
|
|
1072
|
-
component_configs["handoff_router"], FlockRouter
|
|
1073
|
-
)
|
|
1074
|
-
logger.debug(f"Deserialized handoff_router for '{agent.name}'")
|
|
1075
|
-
except Exception as e:
|
|
1076
|
-
logger.error(
|
|
1077
|
-
f"Failed to deserialize handoff_router for '{agent.name}': {e}",
|
|
1078
|
-
exc_info=True,
|
|
1079
|
-
)
|
|
1080
|
-
|
|
1081
|
-
# Modules
|
|
1082
|
-
if "modules" in component_configs:
|
|
1083
|
-
agent.modules = {} # Initialize
|
|
1084
|
-
for module_name, module_data in component_configs[
|
|
1085
|
-
"modules"
|
|
1086
|
-
].items():
|
|
1087
|
-
try:
|
|
1088
|
-
module_instance = deserialize_component(
|
|
1089
|
-
module_data, FlockModule
|
|
1090
|
-
)
|
|
1091
|
-
if module_instance:
|
|
1092
|
-
# Use add_module for potential logic within it
|
|
1093
|
-
agent.add_module(module_instance)
|
|
1094
|
-
logger.debug(
|
|
1095
|
-
f"Deserialized and added module '{module_name}' for '{agent.name}'"
|
|
1096
|
-
)
|
|
1097
|
-
except Exception as e:
|
|
1098
|
-
logger.error(
|
|
1099
|
-
f"Failed to deserialize module '{module_name}' for '{agent.name}': {e}",
|
|
1100
|
-
exc_info=True,
|
|
1101
|
-
)
|
|
1102
|
-
|
|
1103
|
-
# Temporal Activity Config
|
|
1104
|
-
if "temporal_activity_config" in component_configs:
|
|
1105
|
-
try:
|
|
1106
|
-
agent.temporal_activity_config = TemporalActivityConfig(
|
|
1107
|
-
**component_configs["temporal_activity_config"]
|
|
1108
|
-
)
|
|
1109
|
-
logger.debug(
|
|
1110
|
-
f"Deserialized temporal_activity_config for '{agent.name}'"
|
|
1111
|
-
)
|
|
1112
|
-
except Exception as e:
|
|
1113
|
-
logger.error(
|
|
1114
|
-
f"Failed to deserialize temporal_activity_config for '{agent.name}': {e}",
|
|
1115
|
-
exc_info=True,
|
|
1116
|
-
)
|
|
1117
|
-
agent.temporal_activity_config = None
|
|
1118
|
-
|
|
1119
|
-
# --- Deserialize Tools ---
|
|
1120
|
-
agent.tools = [] # Initialize tools list
|
|
1121
|
-
if tool_config:
|
|
1122
|
-
logger.debug(
|
|
1123
|
-
f"Deserializing {len(tool_config)} tools for '{agent.name}'"
|
|
1124
|
-
)
|
|
1125
|
-
# Use get_callable to find each tool
|
|
1126
|
-
for tool_name_or_path in tool_config:
|
|
1127
|
-
try:
|
|
1128
|
-
found_tool = registry.get_callable(tool_name_or_path)
|
|
1129
|
-
if found_tool and callable(found_tool):
|
|
1130
|
-
agent.tools.append(found_tool)
|
|
1131
|
-
logger.debug(
|
|
1132
|
-
f"Resolved and added tool '{tool_name_or_path}' for agent '{agent.name}'"
|
|
1133
|
-
)
|
|
1134
|
-
else:
|
|
1135
|
-
# Should not happen if get_callable returns successfully but just in case
|
|
1136
|
-
logger.warning(
|
|
1137
|
-
f"Registry returned non-callable for tool '{tool_name_or_path}' for agent '{agent.name}'. Skipping."
|
|
1138
|
-
)
|
|
1139
|
-
except (
|
|
1140
|
-
ValueError
|
|
1141
|
-
) as e: # get_callable raises ValueError if not found/ambiguous
|
|
1142
|
-
logger.warning(
|
|
1143
|
-
f"Could not resolve tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping."
|
|
1144
|
-
)
|
|
1145
|
-
except Exception as e:
|
|
1146
|
-
logger.error(
|
|
1147
|
-
f"Unexpected error resolving tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping.",
|
|
1148
|
-
exc_info=True,
|
|
1149
|
-
)
|
|
1150
|
-
|
|
1151
|
-
agent.production_tools = []
|
|
1152
|
-
if production_tool_config:
|
|
1153
|
-
logger.debug(
|
|
1154
|
-
f"Deserializing {len(production_tool_config)} production tools for '{agent.name}'"
|
|
1155
|
-
)
|
|
1156
|
-
for tool_name_or_path in production_tool_config:
|
|
1157
|
-
try:
|
|
1158
|
-
found_tool = registry.get_callable(tool_name_or_path)
|
|
1159
|
-
if found_tool and callable(found_tool):
|
|
1160
|
-
agent.production_tools.append(found_tool)
|
|
1161
|
-
logger.debug(
|
|
1162
|
-
f"Resolved and added production tool '{tool_name_or_path}' for agent '{agent.name}'"
|
|
1163
|
-
)
|
|
1164
|
-
else:
|
|
1165
|
-
logger.warning(
|
|
1166
|
-
f"Registry returned non-callable for production tool '{tool_name_or_path}' for agent '{agent.name}'. Skipping."
|
|
1167
|
-
)
|
|
1168
|
-
except (ValueError) as e:
|
|
1169
|
-
logger.warning(
|
|
1170
|
-
f"Could not resolve production tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping."
|
|
1171
|
-
)
|
|
1172
|
-
except Exception as e:
|
|
1173
|
-
logger.error(
|
|
1174
|
-
f"Unexpected error resolving production tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping.",
|
|
1175
|
-
exc_info=True,
|
|
1176
|
-
)
|
|
1177
|
-
|
|
1178
|
-
# --- Deserialize Servers ---
|
|
1179
|
-
agent.servers = [] # Initialize Servers list.
|
|
1180
|
-
if servers_config:
|
|
1181
|
-
logger.debug(
|
|
1182
|
-
f"Deserializing {len(servers_config)} servers for '{agent.name}'"
|
|
1183
|
-
)
|
|
1184
|
-
# Agents keep track of server by getting a list of server names.
|
|
1185
|
-
# The server instances will be retrieved during runtime from the registry. (default behavior)
|
|
1186
|
-
|
|
1187
|
-
for server_name in servers_config:
|
|
1188
|
-
if isinstance(server_name, str):
|
|
1189
|
-
# Case 1 (default behavior): A server name is passe.
|
|
1190
|
-
agent.servers.append(server_name)
|
|
1191
|
-
elif isinstance(server_name, FlockMCPServerBase):
|
|
1192
|
-
# Case 2 (highly unlikely): If someone somehow manages to pass
|
|
1193
|
-
# an instance of a server during the deserialization step (however that might be achieved)
|
|
1194
|
-
# check the registry, if the server is already registered, if not, register it
|
|
1195
|
-
# and store the name in the servers list
|
|
1196
|
-
FlockRegistry = get_registry()
|
|
1197
|
-
server_exists = (
|
|
1198
|
-
FlockRegistry.get_server(server_name.config.name)
|
|
1199
|
-
is not None
|
|
1200
|
-
)
|
|
1201
|
-
if server_exists:
|
|
1202
|
-
agent.servers.append(server_name.config.name)
|
|
1203
|
-
else:
|
|
1204
|
-
FlockRegistry.register_server(
|
|
1205
|
-
server=server_name
|
|
1206
|
-
) # register it.
|
|
1207
|
-
agent.servers.append(server_name.config.name)
|
|
1208
|
-
|
|
1209
|
-
# --- Deserialize Callables ---
|
|
1210
|
-
logger.debug(f"Deserializing callable fields for '{agent.name}'")
|
|
1211
|
-
# available_callables = registry.get_all_callables() # Incorrect
|
|
1212
|
-
|
|
1213
|
-
def resolve_and_assign(field_name: str, callable_key: str):
|
|
1214
|
-
if callable_key in callable_configs:
|
|
1215
|
-
callable_name = callable_configs[callable_key]
|
|
1216
|
-
try:
|
|
1217
|
-
# Use get_callable to find the signature function
|
|
1218
|
-
found_callable = registry.get_callable(callable_name)
|
|
1219
|
-
if found_callable and callable(found_callable):
|
|
1220
|
-
setattr(agent, field_name, found_callable)
|
|
1221
|
-
logger.debug(
|
|
1222
|
-
f"Resolved callable '{callable_name}' for field '{field_name}' on agent '{agent.name}'"
|
|
1223
|
-
)
|
|
1224
|
-
else:
|
|
1225
|
-
logger.warning(
|
|
1226
|
-
f"Registry returned non-callable for name '{callable_name}' for field '{field_name}' on agent '{agent.name}'. Field remains default."
|
|
1227
|
-
)
|
|
1228
|
-
except (
|
|
1229
|
-
ValueError
|
|
1230
|
-
) as e: # get_callable raises ValueError if not found/ambiguous
|
|
1231
|
-
logger.warning(
|
|
1232
|
-
f"Could not resolve callable '{callable_name}' in registry for field '{field_name}' on agent '{agent.name}': {e}. Field remains default."
|
|
1233
|
-
)
|
|
1234
|
-
except Exception as e:
|
|
1235
|
-
logger.error(
|
|
1236
|
-
f"Unexpected error resolving callable '{callable_name}' for field '{field_name}' on agent '{agent.name}': {e}. Field remains default.",
|
|
1237
|
-
exc_info=True,
|
|
1238
|
-
)
|
|
1239
|
-
# Else: key not present, field retains its default value from __init__
|
|
1240
|
-
|
|
1241
|
-
resolve_and_assign("description", "description_callable")
|
|
1242
|
-
resolve_and_assign("input", "input_callable")
|
|
1243
|
-
resolve_and_assign("output", "output_callable")
|
|
1244
|
-
|
|
1245
|
-
logger.info(f"Successfully deserialized agent '{agent.name}'.")
|
|
1246
|
-
return agent
|
|
1247
|
-
|
|
1248
|
-
# --- Pydantic v2 Configuration ---
|
|
1249
|
-
class Config:
|
|
1250
|
-
arbitrary_types_allowed = (
|
|
1251
|
-
True # Important for components like evaluator, router etc.
|
|
1252
|
-
)
|
|
1253
|
-
# Might need custom json_encoders if not using model_dump(mode='json') everywhere
|
|
1254
|
-
# json_encoders = {
|
|
1255
|
-
# FlockEvaluator: lambda v: v.to_dict() if v else None,
|
|
1256
|
-
# FlockRouter: lambda v: v.to_dict() if v else None,
|
|
1257
|
-
# FlockModule: lambda v: v.to_dict() if v else None,
|
|
1258
|
-
# }
|