flock-core 0.4.543__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.543.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.543.dist-info/METADATA +0 -676
- flock_core-0.4.543.dist-info/RECORD +0 -572
- flock_core-0.4.543.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.543.dist-info → flock_core-0.5.0.dist-info}/WHEEL +0 -0
|
@@ -1,486 +0,0 @@
|
|
|
1
|
-
# src/flock/routers/conditional/conditional_router.py
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
from collections.abc import Callable
|
|
5
|
-
from typing import Any, Literal
|
|
6
|
-
|
|
7
|
-
from pydantic import Field, model_validator
|
|
8
|
-
|
|
9
|
-
from flock.core.context.context import FlockContext
|
|
10
|
-
from flock.core.flock_agent import FlockAgent
|
|
11
|
-
from flock.core.flock_registry import flock_component, get_registry
|
|
12
|
-
from flock.core.flock_router import (
|
|
13
|
-
FlockRouter,
|
|
14
|
-
FlockRouterConfig,
|
|
15
|
-
HandOffRequest,
|
|
16
|
-
)
|
|
17
|
-
from flock.core.logging.logging import get_logger
|
|
18
|
-
|
|
19
|
-
logger = get_logger("router.conditional")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ConditionalRouterConfig(FlockRouterConfig):
|
|
23
|
-
"""Configuration for the ConditionalRouter."""
|
|
24
|
-
|
|
25
|
-
condition_context_key: str = Field(
|
|
26
|
-
default="flock.condition",
|
|
27
|
-
description="Context key containing the value to evaluate the condition against.",
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
# --- Define ONE type of condition check ---
|
|
31
|
-
condition_callable: (
|
|
32
|
-
str | Callable[[Any], tuple[bool, str | None]] | None
|
|
33
|
-
) = Field(
|
|
34
|
-
default=None,
|
|
35
|
-
description="A callable (or registered name) that takes the context value and returns a tuple containing: (bool: True if condition passed, False otherwise, Optional[str]: Feedback message if condition failed).",
|
|
36
|
-
)
|
|
37
|
-
# String Checks
|
|
38
|
-
expected_string: str | None = Field(
|
|
39
|
-
default=None, description="String value to compare against."
|
|
40
|
-
)
|
|
41
|
-
string_mode: Literal[
|
|
42
|
-
"equals",
|
|
43
|
-
"contains",
|
|
44
|
-
"regex",
|
|
45
|
-
"startswith",
|
|
46
|
-
"endswith",
|
|
47
|
-
"not_equals",
|
|
48
|
-
"not_contains",
|
|
49
|
-
] = Field(default="equals", description="How to compare strings.")
|
|
50
|
-
ignore_case: bool = Field(
|
|
51
|
-
default=True, description="Ignore case during string comparison."
|
|
52
|
-
)
|
|
53
|
-
# Length Checks (String or List)
|
|
54
|
-
min_length: int | None = Field(
|
|
55
|
-
default=None,
|
|
56
|
-
description="Minimum length for strings or items for lists.",
|
|
57
|
-
)
|
|
58
|
-
max_length: int | None = Field(
|
|
59
|
-
default=None,
|
|
60
|
-
description="Maximum length for strings or items for lists.",
|
|
61
|
-
)
|
|
62
|
-
# Number Checks
|
|
63
|
-
expected_number: int | float | None = Field(
|
|
64
|
-
default=None, description="Number to compare against."
|
|
65
|
-
)
|
|
66
|
-
number_mode: Literal["<", "<=", "==", "!=", ">=", ">"] = Field(
|
|
67
|
-
default="==", description="How to compare numbers."
|
|
68
|
-
)
|
|
69
|
-
# List Checks
|
|
70
|
-
min_items: int | None = Field(
|
|
71
|
-
default=None, description="Minimum number of items in a list."
|
|
72
|
-
)
|
|
73
|
-
max_items: int | None = Field(
|
|
74
|
-
default=None, description="Maximum number of items in a list."
|
|
75
|
-
)
|
|
76
|
-
# Type Check
|
|
77
|
-
expected_type_name: str | None = Field(
|
|
78
|
-
default=None,
|
|
79
|
-
description="Registered name of the expected Python type (e.g., 'str', 'list', 'MyCustomType').",
|
|
80
|
-
)
|
|
81
|
-
# Boolean Check
|
|
82
|
-
expected_bool: bool | None = Field(
|
|
83
|
-
default=None, description="Expected boolean value (True or False)."
|
|
84
|
-
)
|
|
85
|
-
# Existence Check
|
|
86
|
-
check_exists: bool | None = Field(
|
|
87
|
-
default=None,
|
|
88
|
-
description="If True, succeeds if key exists; if False, succeeds if key *doesn't* exist. Ignores value.",
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
# --- Routing Targets ---
|
|
92
|
-
success_agent: str | None = Field(
|
|
93
|
-
default=None,
|
|
94
|
-
description="Agent name to route to if the condition evaluates to True.",
|
|
95
|
-
)
|
|
96
|
-
failure_agent: str | None = Field(
|
|
97
|
-
default=None,
|
|
98
|
-
description="Agent name to route to if the condition evaluates to False (after retries, if enabled).",
|
|
99
|
-
)
|
|
100
|
-
retry_agent: str | None = Field(
|
|
101
|
-
default=None,
|
|
102
|
-
description="Agent name to route to if the condition evaluates to False (during retries, if enabled).",
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
# --- Optional Retry Logic (for Failure Path) ---
|
|
106
|
-
retry_on_failure: bool = Field(
|
|
107
|
-
default=False,
|
|
108
|
-
description="If True, route back to the retry_agent on failure before going to failure_agent.",
|
|
109
|
-
)
|
|
110
|
-
max_retries: int = Field(
|
|
111
|
-
default=1,
|
|
112
|
-
description="Maximum number of times to retry the current agent on failure.",
|
|
113
|
-
)
|
|
114
|
-
feedback_context_key: str | None = Field(
|
|
115
|
-
default="flock.assertion_feedback", # Useful if paired with AssertionCheckerModule
|
|
116
|
-
description="Optional context key containing feedback message to potentially include when retrying.",
|
|
117
|
-
)
|
|
118
|
-
feedback_on_failure: str | None = Field(
|
|
119
|
-
default=None,
|
|
120
|
-
description="Default feedback message to use when condition evaluation fails.",
|
|
121
|
-
)
|
|
122
|
-
retry_count_context_key_prefix: str = Field(
|
|
123
|
-
default="flock.conditional_retry_count_",
|
|
124
|
-
description="Internal prefix for context key storing retry attempts per agent.",
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
# --- Validator to ensure only one condition type is set ---
|
|
128
|
-
@model_validator(mode="after")
|
|
129
|
-
def check_exclusive_condition(self) -> "ConditionalRouterConfig":
|
|
130
|
-
conditions_set = [
|
|
131
|
-
self.condition_callable is not None,
|
|
132
|
-
self.expected_string is not None
|
|
133
|
-
or self.min_length is not None
|
|
134
|
-
or self.max_length is not None, # String/Length group
|
|
135
|
-
self.expected_number is not None, # Number group
|
|
136
|
-
self.min_items is not None
|
|
137
|
-
or self.max_items is not None, # List size group
|
|
138
|
-
self.expected_type_name is not None, # Type group
|
|
139
|
-
self.expected_bool is not None, # Bool group
|
|
140
|
-
self.check_exists is not None, # Existence group
|
|
141
|
-
]
|
|
142
|
-
if sum(conditions_set) > 1:
|
|
143
|
-
raise ValueError(
|
|
144
|
-
"Only one type of condition (callable, string/length, number, list size, type, boolean, exists) can be configured per ConditionalRouter."
|
|
145
|
-
)
|
|
146
|
-
if sum(conditions_set) == 0:
|
|
147
|
-
raise ValueError(
|
|
148
|
-
"At least one condition type must be configured for ConditionalRouter."
|
|
149
|
-
)
|
|
150
|
-
return self
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
@flock_component(config_class=ConditionalRouterConfig)
|
|
154
|
-
class ConditionalRouter(FlockRouter):
|
|
155
|
-
"""Routes workflow based on evaluating a condition against a value in the FlockContext.
|
|
156
|
-
Supports various built-in checks (string, number, list, type, bool, existence)
|
|
157
|
-
or a custom callable. Can optionally retry the current agent on failure.
|
|
158
|
-
"""
|
|
159
|
-
|
|
160
|
-
name: str = "conditional_router"
|
|
161
|
-
config: ConditionalRouterConfig = Field(
|
|
162
|
-
default_factory=ConditionalRouterConfig
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
def _evaluate_condition(self, value: Any) -> tuple[bool, str | None]:
|
|
166
|
-
"""Evaluates the condition based on the router's configuration.
|
|
167
|
-
|
|
168
|
-
Returns:
|
|
169
|
-
Tuple[bool, Optional[str]]: A tuple containing:
|
|
170
|
-
- bool: True if the condition passed, False otherwise.
|
|
171
|
-
- Optional[str]: A feedback message if the condition failed, otherwise None.
|
|
172
|
-
"""
|
|
173
|
-
cfg = self.config
|
|
174
|
-
condition_passed = False
|
|
175
|
-
feedback = cfg.feedback_on_failure # Default feedback
|
|
176
|
-
condition_type = "unknown"
|
|
177
|
-
|
|
178
|
-
try:
|
|
179
|
-
# 0. Check Existence first (simplest)
|
|
180
|
-
if cfg.check_exists is not None:
|
|
181
|
-
condition_type = "existence"
|
|
182
|
-
value_exists = value is not None
|
|
183
|
-
condition_passed = (
|
|
184
|
-
value_exists if cfg.check_exists else not value_exists
|
|
185
|
-
)
|
|
186
|
-
if not condition_passed:
|
|
187
|
-
feedback = f"Existence check failed: Expected key '{cfg.condition_context_key}' to {'exist' if cfg.check_exists else 'not exist or be None'}, but it was {'found' if value_exists else 'missing/None'}."
|
|
188
|
-
|
|
189
|
-
# 1. Custom Callable
|
|
190
|
-
elif cfg.condition_callable:
|
|
191
|
-
condition_type = "callable"
|
|
192
|
-
callable_func = cfg.condition_callable
|
|
193
|
-
if isinstance(callable_func, str): # Lookup registered callable
|
|
194
|
-
registry = get_registry()
|
|
195
|
-
try:
|
|
196
|
-
callable_func = registry.get_callable(callable_func)
|
|
197
|
-
except KeyError:
|
|
198
|
-
feedback = f"Condition callable '{cfg.condition_callable}' not found in registry."
|
|
199
|
-
logger.error(feedback)
|
|
200
|
-
return False, feedback # Treat as failure
|
|
201
|
-
|
|
202
|
-
if callable(callable_func):
|
|
203
|
-
eval_result = callable_func(value)
|
|
204
|
-
if (
|
|
205
|
-
isinstance(eval_result, tuple)
|
|
206
|
-
and len(eval_result) == 2
|
|
207
|
-
and isinstance(eval_result[0], bool)
|
|
208
|
-
):
|
|
209
|
-
condition_passed, custom_feedback = eval_result
|
|
210
|
-
if not condition_passed and isinstance(
|
|
211
|
-
custom_feedback, str
|
|
212
|
-
):
|
|
213
|
-
feedback = custom_feedback
|
|
214
|
-
elif isinstance(eval_result, bool):
|
|
215
|
-
condition_passed = eval_result
|
|
216
|
-
if not condition_passed:
|
|
217
|
-
feedback = f"Callable condition '{getattr(callable_func, '__name__', 'anonymous')}' returned False."
|
|
218
|
-
else:
|
|
219
|
-
feedback = f"Condition callable '{getattr(callable_func, '__name__', 'anonymous')}' returned unexpected type: {type(eval_result)}."
|
|
220
|
-
logger.warning(feedback)
|
|
221
|
-
return False, feedback # Treat as failure
|
|
222
|
-
else:
|
|
223
|
-
feedback = f"Configured condition_callable '{cfg.condition_callable}' is not callable."
|
|
224
|
-
logger.error(feedback)
|
|
225
|
-
return False, feedback
|
|
226
|
-
|
|
227
|
-
# 2. String / Length Checks
|
|
228
|
-
elif (
|
|
229
|
-
cfg.expected_string is not None
|
|
230
|
-
or cfg.min_length is not None
|
|
231
|
-
or cfg.max_length is not None
|
|
232
|
-
):
|
|
233
|
-
condition_type = "string/length"
|
|
234
|
-
if not isinstance(value, str):
|
|
235
|
-
feedback = f"Cannot perform string/length check on non-string value: {type(value)}."
|
|
236
|
-
logger.warning(feedback)
|
|
237
|
-
return False, feedback
|
|
238
|
-
s_value = value
|
|
239
|
-
val_len = len(s_value)
|
|
240
|
-
length_passed = True
|
|
241
|
-
length_feedback = []
|
|
242
|
-
if cfg.min_length is not None and val_len < cfg.min_length:
|
|
243
|
-
length_passed = False
|
|
244
|
-
length_feedback.append(
|
|
245
|
-
f"length {val_len} is less than minimum {cfg.min_length}"
|
|
246
|
-
)
|
|
247
|
-
if cfg.max_length is not None and val_len > cfg.max_length:
|
|
248
|
-
length_passed = False
|
|
249
|
-
length_feedback.append(
|
|
250
|
-
f"length {val_len} is greater than maximum {cfg.max_length}"
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
content_passed = True
|
|
254
|
-
content_feedback = ""
|
|
255
|
-
if cfg.expected_string is not None:
|
|
256
|
-
expected = cfg.expected_string
|
|
257
|
-
s1 = s_value if not cfg.ignore_case else s_value.lower()
|
|
258
|
-
s2 = expected if not cfg.ignore_case else expected.lower()
|
|
259
|
-
mode = cfg.string_mode
|
|
260
|
-
if mode == "equals":
|
|
261
|
-
content_passed = s1 == s2
|
|
262
|
-
elif mode == "contains":
|
|
263
|
-
content_passed = s2 in s1
|
|
264
|
-
elif mode == "startswith":
|
|
265
|
-
content_passed = s1.startswith(s2)
|
|
266
|
-
elif mode == "endswith":
|
|
267
|
-
content_passed = s1.endswith(s2)
|
|
268
|
-
elif mode == "not_equals":
|
|
269
|
-
content_passed = s1 != s2
|
|
270
|
-
elif mode == "not_contains":
|
|
271
|
-
content_passed = s2 not in s1
|
|
272
|
-
elif mode == "regex":
|
|
273
|
-
content_passed = bool(re.search(expected, value))
|
|
274
|
-
else:
|
|
275
|
-
content_passed = False
|
|
276
|
-
if not content_passed:
|
|
277
|
-
content_feedback = f"String content check '{mode}' failed against expected '{expected}' (ignore_case={cfg.ignore_case})."
|
|
278
|
-
|
|
279
|
-
condition_passed = length_passed and content_passed
|
|
280
|
-
if not condition_passed:
|
|
281
|
-
feedback_parts = length_feedback + (
|
|
282
|
-
[content_feedback] if content_feedback else []
|
|
283
|
-
)
|
|
284
|
-
feedback = (
|
|
285
|
-
"; ".join(feedback_parts)
|
|
286
|
-
if feedback_parts
|
|
287
|
-
else "String/length condition failed."
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
# 3. Number Check
|
|
291
|
-
elif cfg.expected_number is not None:
|
|
292
|
-
condition_type = "number"
|
|
293
|
-
if not isinstance(value, (int, float)):
|
|
294
|
-
feedback = f"Cannot perform number check on non-numeric value: {type(value)}."
|
|
295
|
-
logger.warning(feedback)
|
|
296
|
-
return False, feedback
|
|
297
|
-
num_value = value
|
|
298
|
-
expected = cfg.expected_number
|
|
299
|
-
mode = cfg.number_mode
|
|
300
|
-
op_map = {
|
|
301
|
-
"<": lambda a, b: a < b,
|
|
302
|
-
"<=": lambda a, b: a <= b,
|
|
303
|
-
"==": lambda a, b: a == b,
|
|
304
|
-
"!=": lambda a, b: a != b,
|
|
305
|
-
">=": lambda a, b: a >= b,
|
|
306
|
-
">": lambda a, b: a > b,
|
|
307
|
-
}
|
|
308
|
-
if mode in op_map:
|
|
309
|
-
condition_passed = op_map[mode](num_value, expected)
|
|
310
|
-
if not condition_passed:
|
|
311
|
-
feedback = f"Number check failed: {num_value} {mode} {expected} is false."
|
|
312
|
-
else:
|
|
313
|
-
condition_passed = False
|
|
314
|
-
feedback = f"Invalid number comparison mode: {mode}"
|
|
315
|
-
|
|
316
|
-
# 4. List Size Check
|
|
317
|
-
elif cfg.min_items is not None or cfg.max_items is not None:
|
|
318
|
-
condition_type = "list size"
|
|
319
|
-
if not isinstance(value, list):
|
|
320
|
-
feedback = f"Cannot perform list size check on non-list value: {type(value)}."
|
|
321
|
-
logger.warning(feedback)
|
|
322
|
-
return False, feedback
|
|
323
|
-
list_len = len(value)
|
|
324
|
-
size_passed = True
|
|
325
|
-
size_feedback = []
|
|
326
|
-
if cfg.min_items is not None and list_len < cfg.min_items:
|
|
327
|
-
size_passed = False
|
|
328
|
-
size_feedback.append(
|
|
329
|
-
f"list size {list_len} is less than minimum {cfg.min_items}"
|
|
330
|
-
)
|
|
331
|
-
if cfg.max_items is not None and list_len > cfg.max_items:
|
|
332
|
-
size_passed = False
|
|
333
|
-
size_feedback.append(
|
|
334
|
-
f"list size {list_len} is greater than maximum {cfg.max_items}"
|
|
335
|
-
)
|
|
336
|
-
condition_passed = size_passed
|
|
337
|
-
if not condition_passed:
|
|
338
|
-
feedback = "; ".join(size_feedback)
|
|
339
|
-
|
|
340
|
-
# 5. Type Check
|
|
341
|
-
elif cfg.expected_type_name is not None:
|
|
342
|
-
condition_type = "type"
|
|
343
|
-
registry = get_registry()
|
|
344
|
-
try:
|
|
345
|
-
expected_type = registry.get_type(cfg.expected_type_name)
|
|
346
|
-
condition_passed = isinstance(value, expected_type)
|
|
347
|
-
if not condition_passed:
|
|
348
|
-
feedback = f"Type check failed: Value type '{type(value).__name__}' is not instance of expected '{cfg.expected_type_name}'."
|
|
349
|
-
except KeyError:
|
|
350
|
-
feedback = f"Expected type '{cfg.expected_type_name}' not found in registry."
|
|
351
|
-
logger.error(feedback)
|
|
352
|
-
return False, feedback
|
|
353
|
-
|
|
354
|
-
# 6. Boolean Check
|
|
355
|
-
elif cfg.expected_bool is not None:
|
|
356
|
-
condition_type = "boolean"
|
|
357
|
-
if not isinstance(value, bool):
|
|
358
|
-
feedback = f"Cannot perform boolean check on non-bool value: {type(value)}."
|
|
359
|
-
logger.warning(feedback)
|
|
360
|
-
return False, feedback
|
|
361
|
-
condition_passed = value == cfg.expected_bool
|
|
362
|
-
if not condition_passed:
|
|
363
|
-
feedback = f"Boolean check failed: Value '{value}' is not expected '{cfg.expected_bool}'."
|
|
364
|
-
|
|
365
|
-
logger.debug(
|
|
366
|
-
f"Condition check '{condition_type}' result: {condition_passed}"
|
|
367
|
-
)
|
|
368
|
-
return condition_passed, feedback if not condition_passed else None
|
|
369
|
-
|
|
370
|
-
except Exception as e:
|
|
371
|
-
feedback = (
|
|
372
|
-
f"Error evaluating condition type '{condition_type}': {e}"
|
|
373
|
-
)
|
|
374
|
-
logger.error(feedback, exc_info=True)
|
|
375
|
-
return (
|
|
376
|
-
False,
|
|
377
|
-
feedback,
|
|
378
|
-
) # Treat evaluation errors as condition failure
|
|
379
|
-
|
|
380
|
-
async def route(
|
|
381
|
-
self,
|
|
382
|
-
current_agent: FlockAgent,
|
|
383
|
-
result: dict[str, Any],
|
|
384
|
-
context: FlockContext,
|
|
385
|
-
) -> HandOffRequest:
|
|
386
|
-
cfg = self.config
|
|
387
|
-
condition_value = context.get_variable(cfg.condition_context_key, None)
|
|
388
|
-
feedback_value = context.get_variable(cfg.feedback_context_key, None)
|
|
389
|
-
|
|
390
|
-
logger.debug(
|
|
391
|
-
f"Routing based on condition key '{cfg.condition_context_key}', value: {str(condition_value)[:100]}..."
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
# Evaluate the condition and get feedback on failure
|
|
395
|
-
condition_passed, feedback_msg = self._evaluate_condition(
|
|
396
|
-
condition_value
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
if condition_passed:
|
|
400
|
-
# --- Success Path ---
|
|
401
|
-
logger.info(
|
|
402
|
-
f"Condition PASSED for agent '{current_agent.name}'. Routing to success path."
|
|
403
|
-
)
|
|
404
|
-
# Reset retry count if applicable
|
|
405
|
-
if cfg.retry_on_failure:
|
|
406
|
-
retry_key = (
|
|
407
|
-
f"{cfg.retry_count_context_key_prefix}{current_agent.name}"
|
|
408
|
-
)
|
|
409
|
-
if retry_key in context.state:
|
|
410
|
-
del context.state[retry_key]
|
|
411
|
-
logger.debug(
|
|
412
|
-
f"Reset retry count for agent '{current_agent.name}'."
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
# Clear feedback from context on success
|
|
416
|
-
if (
|
|
417
|
-
cfg.feedback_context_key
|
|
418
|
-
and cfg.feedback_context_key in context.state
|
|
419
|
-
):
|
|
420
|
-
del context.state[cfg.feedback_context_key]
|
|
421
|
-
logger.debug(
|
|
422
|
-
f"Cleared feedback key '{cfg.feedback_context_key}' on success."
|
|
423
|
-
)
|
|
424
|
-
|
|
425
|
-
next_agent = cfg.success_agent or "" # Stop chain if None
|
|
426
|
-
logger.debug(f"Success route target: '{next_agent}'")
|
|
427
|
-
return HandOffRequest(next_agent=next_agent)
|
|
428
|
-
|
|
429
|
-
else:
|
|
430
|
-
# --- Failure Path ---
|
|
431
|
-
logger.warning(
|
|
432
|
-
f"Condition FAILED for agent '{current_agent.name}'. Reason: {feedback_msg}"
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
if cfg.retry_on_failure:
|
|
436
|
-
# --- Retry Logic ---
|
|
437
|
-
retry_key = (
|
|
438
|
-
f"{cfg.retry_count_context_key_prefix}{current_agent.name}"
|
|
439
|
-
)
|
|
440
|
-
retry_count = context.get_variable(retry_key, 0)
|
|
441
|
-
|
|
442
|
-
if retry_count < cfg.max_retries:
|
|
443
|
-
next_retry_count = retry_count + 1
|
|
444
|
-
context.set_variable(retry_key, next_retry_count)
|
|
445
|
-
logger.info(
|
|
446
|
-
f"Routing back to agent '{current_agent.name}' for retry #{next_retry_count}/{cfg.max_retries}."
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
# Add specific feedback to context if retry is enabled
|
|
450
|
-
if cfg.feedback_context_key:
|
|
451
|
-
context.set_variable(
|
|
452
|
-
cfg.feedback_context_key,
|
|
453
|
-
feedback_msg or cfg.feedback_on_failure,
|
|
454
|
-
)
|
|
455
|
-
logger.debug(
|
|
456
|
-
f"Set feedback key '{cfg.feedback_context_key}': {feedback_msg or cfg.feedback_on_failure}"
|
|
457
|
-
)
|
|
458
|
-
|
|
459
|
-
return HandOffRequest(
|
|
460
|
-
next_agent=current_agent.name, # Route back to self
|
|
461
|
-
output_to_input_merge_strategy="add", # Make feedback available
|
|
462
|
-
)
|
|
463
|
-
else:
|
|
464
|
-
# --- Max Retries Exceeded ---
|
|
465
|
-
logger.error(
|
|
466
|
-
f"Max retries ({cfg.max_retries}) exceeded for agent '{current_agent.name}'."
|
|
467
|
-
)
|
|
468
|
-
if retry_key in context.state:
|
|
469
|
-
del context.state[retry_key] # Reset count
|
|
470
|
-
# Clear feedback before final failure route? Optional.
|
|
471
|
-
# if cfg.feedback_context_key in context.state: del context.state[cfg.feedback_context_key]
|
|
472
|
-
next_agent = cfg.failure_agent or ""
|
|
473
|
-
logger.debug(
|
|
474
|
-
f"Failure route target (after retries): '{next_agent}'"
|
|
475
|
-
)
|
|
476
|
-
return HandOffRequest(next_agent=next_agent)
|
|
477
|
-
else:
|
|
478
|
-
# --- No Retry Logic ---
|
|
479
|
-
next_agent = (
|
|
480
|
-
cfg.failure_agent or ""
|
|
481
|
-
) # Use failure agent or stop
|
|
482
|
-
logger.debug(f"Failure route target (no retry): '{next_agent}'")
|
|
483
|
-
# Optionally add feedback even if not retrying?
|
|
484
|
-
# if cfg.feedback_context_key:
|
|
485
|
-
# context.set_variable(cfg.feedback_context_key, feedback_msg or cfg.feedback_on_failure)
|
|
486
|
-
return HandOffRequest(next_agent=next_agent)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Default router implementation for the Flock framework."""
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"""Default router implementation for the Flock framework."""
|
|
2
|
-
|
|
3
|
-
from collections.abc import Callable
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from pydantic import Field
|
|
7
|
-
|
|
8
|
-
from flock.core.context.context import FlockContext
|
|
9
|
-
from flock.core.flock_agent import FlockAgent
|
|
10
|
-
from flock.core.flock_registry import flock_component
|
|
11
|
-
from flock.core.flock_router import (
|
|
12
|
-
FlockRouter,
|
|
13
|
-
FlockRouterConfig,
|
|
14
|
-
HandOffRequest,
|
|
15
|
-
)
|
|
16
|
-
from flock.core.logging.logging import get_logger
|
|
17
|
-
|
|
18
|
-
logger = get_logger("default_router")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class DefaultRouterConfig(FlockRouterConfig):
|
|
22
|
-
"""Configuration for the default router."""
|
|
23
|
-
|
|
24
|
-
hand_off: str | HandOffRequest | Callable[..., HandOffRequest] = Field(
|
|
25
|
-
default="", description="Next agent to hand off to"
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@flock_component(config_class=DefaultRouterConfig)
|
|
30
|
-
class DefaultRouter(FlockRouter):
|
|
31
|
-
"""Default router implementation.
|
|
32
|
-
|
|
33
|
-
This router simply uses the agent's hand_off property to determine the next agent.
|
|
34
|
-
It does not perform any dynamic routing.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
name: str = "default_router"
|
|
38
|
-
config: DefaultRouterConfig = Field(
|
|
39
|
-
default_factory=DefaultRouterConfig, description="Output configuration"
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
def __init__(
|
|
43
|
-
self,
|
|
44
|
-
name: str = "default_router",
|
|
45
|
-
config: DefaultRouterConfig | None = None,
|
|
46
|
-
):
|
|
47
|
-
"""Initialize the DefaultRouter.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
name: The name of the router
|
|
51
|
-
config: The router configuration
|
|
52
|
-
"""
|
|
53
|
-
super().__init__(
|
|
54
|
-
name=name, config=config or DefaultRouterConfig(name=name)
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
async def route(
|
|
58
|
-
self,
|
|
59
|
-
current_agent: FlockAgent,
|
|
60
|
-
result: dict[str, Any],
|
|
61
|
-
context: FlockContext,
|
|
62
|
-
) -> HandOffRequest:
|
|
63
|
-
"""Determine the next agent to hand off to based on the current agent's output.
|
|
64
|
-
|
|
65
|
-
Args:
|
|
66
|
-
current_agent: The agent that just completed execution
|
|
67
|
-
result: The output from the current agent
|
|
68
|
-
context: The global execution context
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
A HandOff object containing the next agent and input data
|
|
72
|
-
"""
|
|
73
|
-
handoff = self.config.hand_off
|
|
74
|
-
if callable(handoff):
|
|
75
|
-
handoff = handoff(context, result)
|
|
76
|
-
if isinstance(handoff, str):
|
|
77
|
-
handoff = HandOffRequest(
|
|
78
|
-
next_agent=handoff, output_to_input_merge_strategy="match"
|
|
79
|
-
)
|
|
80
|
-
return handoff
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# src/flock/routers/correction/correction_router.py (New File)
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from pydantic import Field
|
|
6
|
-
|
|
7
|
-
from flock.core.context.context import FlockContext
|
|
8
|
-
from flock.core.flock_agent import FlockAgent
|
|
9
|
-
from flock.core.flock_registry import flock_component
|
|
10
|
-
from flock.core.flock_router import (
|
|
11
|
-
FlockRouter,
|
|
12
|
-
FlockRouterConfig,
|
|
13
|
-
HandOffRequest,
|
|
14
|
-
)
|
|
15
|
-
from flock.core.logging.logging import get_logger
|
|
16
|
-
|
|
17
|
-
logger = get_logger("router.correction")
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class FeedbackRetryRouterConfig(FlockRouterConfig):
|
|
21
|
-
max_retries: int = Field(
|
|
22
|
-
default=1,
|
|
23
|
-
description="Maximum number of times to retry the same agent on failure.",
|
|
24
|
-
)
|
|
25
|
-
feedback_context_key: str = Field(
|
|
26
|
-
default="flock.assertion_feedback",
|
|
27
|
-
description="Context key containing feedback from AssertionCheckerModule.",
|
|
28
|
-
)
|
|
29
|
-
retry_count_context_key_prefix: str = Field(
|
|
30
|
-
default="flock.retry_count_",
|
|
31
|
-
description="Prefix for context key storing retry attempts per agent.",
|
|
32
|
-
)
|
|
33
|
-
fallback_agent: str | None = Field(
|
|
34
|
-
None, description="Agent to route to if max_retries is exceeded."
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@flock_component(config_class=FeedbackRetryRouterConfig)
|
|
39
|
-
class FeedbackRetryRouter(FlockRouter):
|
|
40
|
-
"""Routes based on assertion feedback in the context.
|
|
41
|
-
|
|
42
|
-
If feedback exists for the current agent and retries are not exhausted,
|
|
43
|
-
it routes back to the same agent, adding the feedback to its input.
|
|
44
|
-
Otherwise, it can route to a fallback agent or stop the chain.
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
name: str = "feedback_retry_router"
|
|
48
|
-
config: FeedbackRetryRouterConfig = Field(
|
|
49
|
-
default_factory=FeedbackRetryRouterConfig
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
async def route(
|
|
53
|
-
self,
|
|
54
|
-
current_agent: FlockAgent,
|
|
55
|
-
result: dict[str, Any],
|
|
56
|
-
context: FlockContext,
|
|
57
|
-
) -> HandOffRequest:
|
|
58
|
-
feedback = context.get_variable(self.config.feedback_context_key)
|
|
59
|
-
|
|
60
|
-
if feedback:
|
|
61
|
-
logger.warning(
|
|
62
|
-
f"Assertion feedback detected for agent '{current_agent.name}'. Attempting retry."
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
retry_key = f"{self.config.retry_count_context_key_prefix}{current_agent.name}"
|
|
66
|
-
retry_count = context.get_variable(retry_key, 0)
|
|
67
|
-
logger.warning(f"Feedback: {feedback} - Retry Count {retry_count}")
|
|
68
|
-
|
|
69
|
-
if retry_count < self.config.max_retries:
|
|
70
|
-
logger.info(
|
|
71
|
-
f"Routing back to agent '{current_agent.name}' for retry #{retry_count + 1}"
|
|
72
|
-
)
|
|
73
|
-
context.set_variable(retry_key, retry_count + 1)
|
|
74
|
-
context.set_variable(
|
|
75
|
-
f"{current_agent.name}_prev_result", result
|
|
76
|
-
)
|
|
77
|
-
# Add feedback to the *next* agent's input (which is the same agent)
|
|
78
|
-
# Requires the agent's signature to potentially accept a 'feedback' input field.
|
|
79
|
-
return HandOffRequest(
|
|
80
|
-
next_agent=current_agent.name,
|
|
81
|
-
output_to_input_merge_strategy="match", # Add feedback to existing context/previous results
|
|
82
|
-
add_input_fields=[
|
|
83
|
-
f"{self.config.feedback_context_key} | Feedback for prev result",
|
|
84
|
-
f"{current_agent.name}_prev_result | Previous Result",
|
|
85
|
-
],
|
|
86
|
-
add_description=f"Try to fix the previous result based on the feedback.",
|
|
87
|
-
override_context=None, # Context already updated with feedback and retry count
|
|
88
|
-
)
|
|
89
|
-
else:
|
|
90
|
-
logger.error(
|
|
91
|
-
f"Max retries ({self.config.max_retries}) exceeded for agent '{current_agent.name}'."
|
|
92
|
-
)
|
|
93
|
-
# Max retries exceeded, route to fallback or stop
|
|
94
|
-
if self.config.fallback_agent:
|
|
95
|
-
logger.info(
|
|
96
|
-
f"Routing to fallback agent '{self.config.fallback_agent}'"
|
|
97
|
-
)
|
|
98
|
-
# Clear feedback before going to fallback? Optional.
|
|
99
|
-
if self.config.feedback_context_key in context.state:
|
|
100
|
-
del context.state[self.config.feedback_context_key]
|
|
101
|
-
return HandOffRequest(next_agent=self.config.fallback_agent)
|
|
102
|
-
else:
|
|
103
|
-
logger.info("No fallback agent defined. Stopping workflow.")
|
|
104
|
-
return HandOffRequest(next_agent="") # Stop the chain
|
|
105
|
-
|
|
106
|
-
else:
|
|
107
|
-
# No feedback, assertions passed or module not configured for feedback
|
|
108
|
-
logger.debug(
|
|
109
|
-
f"No assertion feedback for agent '{current_agent.name}'. Proceeding normally."
|
|
110
|
-
)
|
|
111
|
-
# Default behavior: Stop the chain if no other routing is defined
|
|
112
|
-
# In a real system, you might chain this with another router (e.g., LLMRouter)
|
|
113
|
-
# to decide the *next different* agent if assertions passed.
|
|
114
|
-
return HandOffRequest(next_agent="") # Stop or pass to next router
|