kubiya-control-plane-api 0.9.15__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.
- control_plane_api/LICENSE +676 -0
- control_plane_api/README.md +350 -0
- control_plane_api/__init__.py +4 -0
- control_plane_api/__version__.py +8 -0
- control_plane_api/alembic/README +1 -0
- control_plane_api/alembic/env.py +121 -0
- control_plane_api/alembic/script.py.mako +28 -0
- control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
- control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
- control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
- control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
- control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
- control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
- control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
- control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
- control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
- control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
- control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
- control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
- control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
- control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
- control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
- control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
- control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
- control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
- control_plane_api/alembic.ini +148 -0
- control_plane_api/api/index.py +12 -0
- control_plane_api/app/__init__.py +11 -0
- control_plane_api/app/activities/__init__.py +20 -0
- control_plane_api/app/activities/agent_activities.py +384 -0
- control_plane_api/app/activities/plan_generation_activities.py +499 -0
- control_plane_api/app/activities/team_activities.py +424 -0
- control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
- control_plane_api/app/config/__init__.py +35 -0
- control_plane_api/app/config/api_config.py +469 -0
- control_plane_api/app/config/config_loader.py +224 -0
- control_plane_api/app/config/model_pricing.py +323 -0
- control_plane_api/app/config/storage_config.py +159 -0
- control_plane_api/app/config.py +115 -0
- control_plane_api/app/controllers/__init__.py +0 -0
- control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
- control_plane_api/app/database.py +135 -0
- control_plane_api/app/exceptions.py +408 -0
- control_plane_api/app/lib/__init__.py +11 -0
- control_plane_api/app/lib/environment.py +65 -0
- control_plane_api/app/lib/event_bus/__init__.py +17 -0
- control_plane_api/app/lib/event_bus/base.py +136 -0
- control_plane_api/app/lib/event_bus/manager.py +335 -0
- control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
- control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
- control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
- control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
- control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
- control_plane_api/app/lib/job_executor.py +330 -0
- control_plane_api/app/lib/kubiya_client.py +293 -0
- control_plane_api/app/lib/litellm_pricing.py +166 -0
- control_plane_api/app/lib/mcp_validation.py +163 -0
- control_plane_api/app/lib/nats/__init__.py +13 -0
- control_plane_api/app/lib/nats/credentials_manager.py +288 -0
- control_plane_api/app/lib/nats/listener.py +374 -0
- control_plane_api/app/lib/planning_prompt_builder.py +153 -0
- control_plane_api/app/lib/planning_tools/__init__.py +41 -0
- control_plane_api/app/lib/planning_tools/agents.py +409 -0
- control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
- control_plane_api/app/lib/planning_tools/base.py +119 -0
- control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
- control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
- control_plane_api/app/lib/planning_tools/environments.py +218 -0
- control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
- control_plane_api/app/lib/planning_tools/models.py +93 -0
- control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
- control_plane_api/app/lib/planning_tools/resources.py +242 -0
- control_plane_api/app/lib/planning_tools/teams.py +334 -0
- control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
- control_plane_api/app/lib/redis_client.py +803 -0
- control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
- control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
- control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
- control_plane_api/app/lib/storage/__init__.py +20 -0
- control_plane_api/app/lib/storage/base_provider.py +274 -0
- control_plane_api/app/lib/storage/provider_factory.py +157 -0
- control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
- control_plane_api/app/lib/supabase.py +71 -0
- control_plane_api/app/lib/supabase_utils.py +138 -0
- control_plane_api/app/lib/task_planning/__init__.py +138 -0
- control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
- control_plane_api/app/lib/task_planning/agents.py +389 -0
- control_plane_api/app/lib/task_planning/cache.py +218 -0
- control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
- control_plane_api/app/lib/task_planning/helpers.py +293 -0
- control_plane_api/app/lib/task_planning/hooks.py +474 -0
- control_plane_api/app/lib/task_planning/models.py +503 -0
- control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
- control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
- control_plane_api/app/lib/task_planning/runner.py +656 -0
- control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
- control_plane_api/app/lib/task_planning/workflow.py +424 -0
- control_plane_api/app/lib/templating/__init__.py +88 -0
- control_plane_api/app/lib/templating/compiler.py +278 -0
- control_plane_api/app/lib/templating/engine.py +178 -0
- control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
- control_plane_api/app/lib/templating/parsers/base.py +96 -0
- control_plane_api/app/lib/templating/parsers/env.py +85 -0
- control_plane_api/app/lib/templating/parsers/graph.py +112 -0
- control_plane_api/app/lib/templating/parsers/secret.py +87 -0
- control_plane_api/app/lib/templating/parsers/simple.py +81 -0
- control_plane_api/app/lib/templating/resolver.py +366 -0
- control_plane_api/app/lib/templating/types.py +214 -0
- control_plane_api/app/lib/templating/validator.py +201 -0
- control_plane_api/app/lib/temporal_client.py +232 -0
- control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
- control_plane_api/app/lib/temporal_credentials_service.py +203 -0
- control_plane_api/app/lib/validation/__init__.py +24 -0
- control_plane_api/app/lib/validation/runtime_validation.py +388 -0
- control_plane_api/app/main.py +531 -0
- control_plane_api/app/middleware/__init__.py +10 -0
- control_plane_api/app/middleware/auth.py +645 -0
- control_plane_api/app/middleware/exception_handler.py +267 -0
- control_plane_api/app/middleware/prometheus_middleware.py +173 -0
- control_plane_api/app/middleware/rate_limiting.py +384 -0
- control_plane_api/app/middleware/request_id.py +202 -0
- control_plane_api/app/models/__init__.py +40 -0
- control_plane_api/app/models/agent.py +90 -0
- control_plane_api/app/models/analytics.py +206 -0
- control_plane_api/app/models/associations.py +107 -0
- control_plane_api/app/models/auth_user.py +73 -0
- control_plane_api/app/models/context.py +161 -0
- control_plane_api/app/models/custom_integration.py +99 -0
- control_plane_api/app/models/environment.py +64 -0
- control_plane_api/app/models/execution.py +125 -0
- control_plane_api/app/models/execution_transition.py +50 -0
- control_plane_api/app/models/job.py +159 -0
- control_plane_api/app/models/llm_model.py +78 -0
- control_plane_api/app/models/orchestration.py +66 -0
- control_plane_api/app/models/plan_execution.py +102 -0
- control_plane_api/app/models/presence.py +49 -0
- control_plane_api/app/models/project.py +61 -0
- control_plane_api/app/models/project_management.py +85 -0
- control_plane_api/app/models/session.py +29 -0
- control_plane_api/app/models/skill.py +155 -0
- control_plane_api/app/models/system_tables.py +43 -0
- control_plane_api/app/models/task_planning.py +372 -0
- control_plane_api/app/models/team.py +86 -0
- control_plane_api/app/models/trace.py +257 -0
- control_plane_api/app/models/user_profile.py +54 -0
- control_plane_api/app/models/worker.py +221 -0
- control_plane_api/app/models/workflow.py +161 -0
- control_plane_api/app/models/workspace.py +50 -0
- control_plane_api/app/observability/__init__.py +177 -0
- control_plane_api/app/observability/context_logging.py +475 -0
- control_plane_api/app/observability/decorators.py +337 -0
- control_plane_api/app/observability/local_span_processor.py +702 -0
- control_plane_api/app/observability/metrics.py +303 -0
- control_plane_api/app/observability/middleware.py +246 -0
- control_plane_api/app/observability/optional.py +115 -0
- control_plane_api/app/observability/tracing.py +382 -0
- control_plane_api/app/policies/README.md +149 -0
- control_plane_api/app/policies/approved_users.rego +62 -0
- control_plane_api/app/policies/business_hours.rego +51 -0
- control_plane_api/app/policies/rate_limiting.rego +100 -0
- control_plane_api/app/policies/tool_enforcement/README.md +336 -0
- control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
- control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
- control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
- control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
- control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
- control_plane_api/app/policies/tool_restrictions.rego +86 -0
- control_plane_api/app/routers/__init__.py +4 -0
- control_plane_api/app/routers/agents.py +382 -0
- control_plane_api/app/routers/agents_v2.py +1598 -0
- control_plane_api/app/routers/analytics.py +1310 -0
- control_plane_api/app/routers/auth.py +59 -0
- control_plane_api/app/routers/client_config.py +57 -0
- control_plane_api/app/routers/context_graph.py +561 -0
- control_plane_api/app/routers/context_manager.py +577 -0
- control_plane_api/app/routers/custom_integrations.py +490 -0
- control_plane_api/app/routers/enforcer.py +132 -0
- control_plane_api/app/routers/environment_context.py +252 -0
- control_plane_api/app/routers/environments.py +761 -0
- control_plane_api/app/routers/execution_environment.py +847 -0
- control_plane_api/app/routers/executions/__init__.py +28 -0
- control_plane_api/app/routers/executions/router.py +286 -0
- control_plane_api/app/routers/executions/services/__init__.py +22 -0
- control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
- control_plane_api/app/routers/executions/services/status_service.py +420 -0
- control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
- control_plane_api/app/routers/executions/services/worker_health.py +514 -0
- control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
- control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
- control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
- control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
- control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
- control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
- control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
- control_plane_api/app/routers/executions.py +4888 -0
- control_plane_api/app/routers/health.py +165 -0
- control_plane_api/app/routers/health_v2.py +394 -0
- control_plane_api/app/routers/integration_templates.py +496 -0
- control_plane_api/app/routers/integrations.py +287 -0
- control_plane_api/app/routers/jobs.py +1809 -0
- control_plane_api/app/routers/metrics.py +517 -0
- control_plane_api/app/routers/models.py +82 -0
- control_plane_api/app/routers/models_v2.py +628 -0
- control_plane_api/app/routers/plan_executions.py +1481 -0
- control_plane_api/app/routers/plan_generation_async.py +304 -0
- control_plane_api/app/routers/policies.py +669 -0
- control_plane_api/app/routers/presence.py +234 -0
- control_plane_api/app/routers/projects.py +987 -0
- control_plane_api/app/routers/runners.py +379 -0
- control_plane_api/app/routers/runtimes.py +172 -0
- control_plane_api/app/routers/secrets.py +171 -0
- control_plane_api/app/routers/skills.py +1010 -0
- control_plane_api/app/routers/skills_definitions.py +140 -0
- control_plane_api/app/routers/storage.py +456 -0
- control_plane_api/app/routers/task_planning.py +611 -0
- control_plane_api/app/routers/task_queues.py +650 -0
- control_plane_api/app/routers/team_context.py +274 -0
- control_plane_api/app/routers/teams.py +1747 -0
- control_plane_api/app/routers/templates.py +248 -0
- control_plane_api/app/routers/traces.py +571 -0
- control_plane_api/app/routers/websocket_client.py +479 -0
- control_plane_api/app/routers/websocket_executions_status.py +437 -0
- control_plane_api/app/routers/websocket_gateway.py +323 -0
- control_plane_api/app/routers/websocket_traces.py +576 -0
- control_plane_api/app/routers/worker_queues.py +2555 -0
- control_plane_api/app/routers/worker_websocket.py +419 -0
- control_plane_api/app/routers/workers.py +1004 -0
- control_plane_api/app/routers/workflows.py +204 -0
- control_plane_api/app/runtimes/__init__.py +6 -0
- control_plane_api/app/runtimes/validation.py +344 -0
- control_plane_api/app/schemas/__init__.py +1 -0
- control_plane_api/app/schemas/job_schemas.py +302 -0
- control_plane_api/app/schemas/mcp_schemas.py +311 -0
- control_plane_api/app/schemas/template_schemas.py +133 -0
- control_plane_api/app/schemas/trace_schemas.py +168 -0
- control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
- control_plane_api/app/services/__init__.py +1 -0
- control_plane_api/app/services/agno_planning_strategy.py +233 -0
- control_plane_api/app/services/agno_service.py +838 -0
- control_plane_api/app/services/claude_code_planning_service.py +203 -0
- control_plane_api/app/services/context_graph_client.py +224 -0
- control_plane_api/app/services/custom_integration_service.py +415 -0
- control_plane_api/app/services/integration_resolution_service.py +345 -0
- control_plane_api/app/services/litellm_service.py +394 -0
- control_plane_api/app/services/plan_generator.py +79 -0
- control_plane_api/app/services/planning_strategy.py +66 -0
- control_plane_api/app/services/planning_strategy_factory.py +118 -0
- control_plane_api/app/services/policy_service.py +615 -0
- control_plane_api/app/services/state_transition_service.py +755 -0
- control_plane_api/app/services/storage_service.py +593 -0
- control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
- control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
- control_plane_api/app/services/trace_retention.py +354 -0
- control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
- control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
- control_plane_api/app/services/workflow_operations_service.py +611 -0
- control_plane_api/app/skills/__init__.py +100 -0
- control_plane_api/app/skills/base.py +239 -0
- control_plane_api/app/skills/builtin/__init__.py +37 -0
- control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
- control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
- control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
- control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
- control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
- control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
- control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
- control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
- control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
- control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
- control_plane_api/app/skills/builtin/docker/skill.py +104 -0
- control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
- control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
- control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
- control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
- control_plane_api/app/skills/builtin/python/__init__.py +4 -0
- control_plane_api/app/skills/builtin/python/skill.py +92 -0
- control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
- control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
- control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
- control_plane_api/app/skills/builtin/shell/skill.py +161 -0
- control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/app/skills/builtin/slack/skill.py +302 -0
- control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
- control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
- control_plane_api/app/skills/business_intelligence.py +189 -0
- control_plane_api/app/skills/config.py +63 -0
- control_plane_api/app/skills/loaders/__init__.py +14 -0
- control_plane_api/app/skills/loaders/base.py +73 -0
- control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
- control_plane_api/app/skills/registry.py +125 -0
- control_plane_api/app/utils/helpers.py +12 -0
- control_plane_api/app/utils/workflow_executor.py +354 -0
- control_plane_api/app/workflows/__init__.py +11 -0
- control_plane_api/app/workflows/agent_execution.py +520 -0
- control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
- control_plane_api/app/workflows/namespace_provisioning.py +326 -0
- control_plane_api/app/workflows/plan_generation.py +254 -0
- control_plane_api/app/workflows/team_execution.py +442 -0
- control_plane_api/scripts/seed_models.py +240 -0
- control_plane_api/scripts/validate_existing_tool_names.py +492 -0
- control_plane_api/shared/__init__.py +8 -0
- control_plane_api/shared/version.py +17 -0
- control_plane_api/test_deduplication.py +274 -0
- control_plane_api/test_executor_deduplication_e2e.py +309 -0
- control_plane_api/test_job_execution_e2e.py +283 -0
- control_plane_api/test_real_integration.py +193 -0
- control_plane_api/version.py +38 -0
- control_plane_api/worker/__init__.py +0 -0
- control_plane_api/worker/activities/__init__.py +0 -0
- control_plane_api/worker/activities/agent_activities.py +1585 -0
- control_plane_api/worker/activities/approval_activities.py +234 -0
- control_plane_api/worker/activities/job_activities.py +199 -0
- control_plane_api/worker/activities/runtime_activities.py +1167 -0
- control_plane_api/worker/activities/skill_activities.py +282 -0
- control_plane_api/worker/activities/team_activities.py +479 -0
- control_plane_api/worker/agent_runtime_server.py +370 -0
- control_plane_api/worker/binary_manager.py +333 -0
- control_plane_api/worker/config/__init__.py +31 -0
- control_plane_api/worker/config/worker_config.py +273 -0
- control_plane_api/worker/control_plane_client.py +1491 -0
- control_plane_api/worker/examples/analytics_integration_example.py +362 -0
- control_plane_api/worker/health_monitor.py +159 -0
- control_plane_api/worker/metrics.py +237 -0
- control_plane_api/worker/models/__init__.py +1 -0
- control_plane_api/worker/models/error_events.py +105 -0
- control_plane_api/worker/models/inputs.py +89 -0
- control_plane_api/worker/runtimes/__init__.py +35 -0
- control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
- control_plane_api/worker/runtimes/agno/__init__.py +34 -0
- control_plane_api/worker/runtimes/agno/config.py +248 -0
- control_plane_api/worker/runtimes/agno/hooks.py +385 -0
- control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
- control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
- control_plane_api/worker/runtimes/agno/utils.py +163 -0
- control_plane_api/worker/runtimes/base.py +979 -0
- control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
- control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
- control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
- control_plane_api/worker/runtimes/claude_code/config.py +829 -0
- control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
- control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
- control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
- control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
- control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
- control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
- control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
- control_plane_api/worker/runtimes/factory.py +173 -0
- control_plane_api/worker/runtimes/model_utils.py +107 -0
- control_plane_api/worker/runtimes/validation.py +93 -0
- control_plane_api/worker/services/__init__.py +1 -0
- control_plane_api/worker/services/agent_communication_tools.py +908 -0
- control_plane_api/worker/services/agent_executor.py +485 -0
- control_plane_api/worker/services/agent_executor_v2.py +793 -0
- control_plane_api/worker/services/analytics_collector.py +457 -0
- control_plane_api/worker/services/analytics_service.py +464 -0
- control_plane_api/worker/services/approval_tools.py +310 -0
- control_plane_api/worker/services/approval_tools_agno.py +207 -0
- control_plane_api/worker/services/cancellation_manager.py +177 -0
- control_plane_api/worker/services/code_ingestion_tools.py +465 -0
- control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
- control_plane_api/worker/services/data_visualization.py +834 -0
- control_plane_api/worker/services/event_publisher.py +531 -0
- control_plane_api/worker/services/jira_tools.py +257 -0
- control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
- control_plane_api/worker/services/runtime_analytics.py +328 -0
- control_plane_api/worker/services/session_service.py +365 -0
- control_plane_api/worker/services/skill_context_enhancement.py +181 -0
- control_plane_api/worker/services/skill_factory.py +471 -0
- control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
- control_plane_api/worker/services/team_executor.py +715 -0
- control_plane_api/worker/services/team_executor_v2.py +1866 -0
- control_plane_api/worker/services/tool_enforcement.py +254 -0
- control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
- control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
- control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
- control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
- control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
- control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/models.py +142 -0
- control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
- control_plane_api/worker/skills/__init__.py +12 -0
- control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
- control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
- control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
- control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
- control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
- control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
- control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
- control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
- control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
- control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
- control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
- control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
- control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
- control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
- control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
- control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
- control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
- control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
- control_plane_api/worker/skills/loaders/__init__.py +5 -0
- control_plane_api/worker/skills/loaders/base.py +23 -0
- control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
- control_plane_api/worker/skills/registry.py +208 -0
- control_plane_api/worker/tests/__init__.py +1 -0
- control_plane_api/worker/tests/conftest.py +12 -0
- control_plane_api/worker/tests/e2e/__init__.py +0 -0
- control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
- control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
- control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
- control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
- control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
- control_plane_api/worker/tests/integration/__init__.py +0 -0
- control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
- control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
- control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
- control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
- control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
- control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
- control_plane_api/worker/tests/unit/__init__.py +0 -0
- control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
- control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
- control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
- control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
- control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
- control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
- control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
- control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
- control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
- control_plane_api/worker/utils/__init__.py +1 -0
- control_plane_api/worker/utils/chunk_batcher.py +330 -0
- control_plane_api/worker/utils/environment.py +65 -0
- control_plane_api/worker/utils/error_publisher.py +260 -0
- control_plane_api/worker/utils/event_batcher.py +256 -0
- control_plane_api/worker/utils/logging_config.py +335 -0
- control_plane_api/worker/utils/logging_helper.py +326 -0
- control_plane_api/worker/utils/parameter_validator.py +120 -0
- control_plane_api/worker/utils/retry_utils.py +60 -0
- control_plane_api/worker/utils/streaming_utils.py +665 -0
- control_plane_api/worker/utils/tool_validation.py +332 -0
- control_plane_api/worker/utils/workspace_manager.py +163 -0
- control_plane_api/worker/websocket_client.py +393 -0
- control_plane_api/worker/worker.py +1297 -0
- control_plane_api/worker/workflows/__init__.py +0 -0
- control_plane_api/worker/workflows/agent_execution.py +909 -0
- control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
- control_plane_api/worker/workflows/team_execution.py +611 -0
- kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
- kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
- kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
- kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
- scripts/__init__.py +1 -0
- scripts/migrations.py +39 -0
- scripts/seed_worker_queues.py +128 -0
- scripts/setup_agent_runtime.py +142 -0
- worker_internal/__init__.py +1 -0
- worker_internal/planner/__init__.py +1 -0
- worker_internal/planner/activities.py +1499 -0
- worker_internal/planner/agent_tools.py +197 -0
- worker_internal/planner/event_models.py +148 -0
- worker_internal/planner/event_publisher.py +67 -0
- worker_internal/planner/models.py +199 -0
- worker_internal/planner/retry_logic.py +134 -0
- worker_internal/planner/worker.py +300 -0
- worker_internal/planner/workflows.py +970 -0
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agno Planning Toolkit - Synchronous tools for task planning with internal service integration
|
|
3
|
+
|
|
4
|
+
This module follows the Agno Toolkit pattern with internal service access:
|
|
5
|
+
- Tools are synchronous (not async) for proper Agno workflow compatibility
|
|
6
|
+
- Uses PlanningService with direct DB access (no HTTP self-calls)
|
|
7
|
+
- Returns structured JSON for reliable parsing
|
|
8
|
+
- Proper tool registration via Toolkit base class
|
|
9
|
+
|
|
10
|
+
Architecture:
|
|
11
|
+
- Agents/Teams: Direct DB queries via PlanningService (fast, no HTTP)
|
|
12
|
+
- Context Graph: HTTP to external graph.kubiya.ai (correct for external service)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import structlog
|
|
17
|
+
import asyncio
|
|
18
|
+
import hashlib
|
|
19
|
+
import redis
|
|
20
|
+
from typing import Optional
|
|
21
|
+
from functools import wraps
|
|
22
|
+
from sqlalchemy.orm import Session
|
|
23
|
+
from agno.tools.toolkit import Toolkit
|
|
24
|
+
|
|
25
|
+
from control_plane_api.app.lib.planning_tools.planning_service import PlanningService
|
|
26
|
+
from control_plane_api.app.config import settings
|
|
27
|
+
|
|
28
|
+
logger = structlog.get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
# Initialize Redis client for caching (lazy connection)
|
|
31
|
+
try:
|
|
32
|
+
redis_client = redis.from_url(settings.redis_url, decode_responses=True)
|
|
33
|
+
CACHE_ENABLED = True
|
|
34
|
+
except Exception as e:
|
|
35
|
+
logger.warning("redis_connection_failed", error=str(e), message="Tool caching disabled")
|
|
36
|
+
redis_client = None
|
|
37
|
+
CACHE_ENABLED = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def cache_tool_result(ttl: int = 60):
|
|
41
|
+
"""
|
|
42
|
+
Cache tool results in Redis for specified TTL.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
ttl: Time-to-live in seconds (default: 60)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Decorator function that wraps tool methods with caching
|
|
49
|
+
"""
|
|
50
|
+
def decorator(func):
|
|
51
|
+
@wraps(func)
|
|
52
|
+
def wrapper(self, *args, **kwargs):
|
|
53
|
+
# If caching disabled, call function directly
|
|
54
|
+
if not CACHE_ENABLED or redis_client is None:
|
|
55
|
+
return func(self, *args, **kwargs)
|
|
56
|
+
|
|
57
|
+
# Generate cache key from function name, organization, and args
|
|
58
|
+
cache_parts = [
|
|
59
|
+
func.__name__,
|
|
60
|
+
self.organization_id,
|
|
61
|
+
str(args),
|
|
62
|
+
str(sorted(kwargs.items()))
|
|
63
|
+
]
|
|
64
|
+
cache_string = "|".join(cache_parts)
|
|
65
|
+
cache_hash = hashlib.md5(cache_string.encode()).hexdigest()
|
|
66
|
+
cache_key = f"tool_cache:{func.__name__}:{cache_hash}"
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Try to get from cache
|
|
70
|
+
cached = redis_client.get(cache_key)
|
|
71
|
+
if cached:
|
|
72
|
+
logger.info("tool_cache_hit", tool=func.__name__, organization=self.organization_id[:8])
|
|
73
|
+
return cached
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.warning("redis_get_error", error=str(e), key=cache_key)
|
|
76
|
+
|
|
77
|
+
# Execute function
|
|
78
|
+
result = func(self, *args, **kwargs)
|
|
79
|
+
|
|
80
|
+
# Cache result
|
|
81
|
+
try:
|
|
82
|
+
redis_client.setex(cache_key, ttl, result)
|
|
83
|
+
logger.debug("tool_cache_set", tool=func.__name__, ttl=ttl)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.warning("redis_set_error", error=str(e), key=cache_key)
|
|
86
|
+
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
return wrapper
|
|
90
|
+
return decorator
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class PlanningToolkit(Toolkit):
|
|
94
|
+
"""
|
|
95
|
+
Custom toolkit for task planning with auto-registered tools.
|
|
96
|
+
|
|
97
|
+
CRITICAL: Service must be initialized BEFORE calling super().__init__()
|
|
98
|
+
so it's available when tools are registered.
|
|
99
|
+
|
|
100
|
+
This uses internal services directly (no HTTP self-calls).
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
db: Session,
|
|
106
|
+
organization_id: str,
|
|
107
|
+
api_token: str,
|
|
108
|
+
name: str = "planning_tools"
|
|
109
|
+
):
|
|
110
|
+
"""
|
|
111
|
+
Initialize planning toolkit with internal services.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
db: SQLAlchemy database session
|
|
115
|
+
organization_id: Organization ID for filtering
|
|
116
|
+
api_token: Org-scoped API token
|
|
117
|
+
name: Toolkit name for Agno
|
|
118
|
+
"""
|
|
119
|
+
# CRITICAL: Initialize service BEFORE calling super().__init__()
|
|
120
|
+
self.planning_service = PlanningService(
|
|
121
|
+
db=db,
|
|
122
|
+
organization_id=organization_id,
|
|
123
|
+
api_token=api_token
|
|
124
|
+
)
|
|
125
|
+
self.organization_id = organization_id
|
|
126
|
+
|
|
127
|
+
# Create tools list for auto-registration
|
|
128
|
+
tools = [
|
|
129
|
+
self.list_agents,
|
|
130
|
+
self.list_teams,
|
|
131
|
+
self.search_agents_by_capability,
|
|
132
|
+
self.search_teams_by_capability,
|
|
133
|
+
self.get_agent_details,
|
|
134
|
+
self.get_team_details,
|
|
135
|
+
self.list_environments,
|
|
136
|
+
self.list_worker_queues,
|
|
137
|
+
self.search_context_graph,
|
|
138
|
+
self.get_fallback_agent, # NEW: Fallback for when no perfect match found
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
# Pass to parent for auto-registration
|
|
142
|
+
# This registers each method in self.functions dict
|
|
143
|
+
super().__init__(name=name, tools=tools)
|
|
144
|
+
|
|
145
|
+
logger.info(
|
|
146
|
+
"planning_toolkit_initialized",
|
|
147
|
+
tool_count=len(tools),
|
|
148
|
+
organization_id=organization_id[:8]
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
@cache_tool_result(ttl=60) # Cache for 60 seconds
|
|
152
|
+
def list_agents(self, limit: int = 20) -> str:
|
|
153
|
+
"""List available agents in the organization.
|
|
154
|
+
|
|
155
|
+
Use this to discover what agents are available for executing tasks.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
limit: Maximum number of agents to return (default: 20)
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
JSON string with structured agent data and human-readable format
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
# Call service method (direct DB query)
|
|
165
|
+
agents = self.planning_service.list_agents(limit=limit)
|
|
166
|
+
|
|
167
|
+
# Format human-readable output
|
|
168
|
+
if not agents:
|
|
169
|
+
human_readable = "No active agents found in the organization."
|
|
170
|
+
else:
|
|
171
|
+
human_readable = f"Found {len(agents)} active agents:\n\n"
|
|
172
|
+
for i, agent in enumerate(agents, 1):
|
|
173
|
+
human_readable += f"Agent {i}:\n"
|
|
174
|
+
human_readable += f" ID: {agent.get('id')}\n"
|
|
175
|
+
human_readable += f" Name: {agent.get('name')}\n"
|
|
176
|
+
human_readable += f" Model: {agent.get('model_id', 'default')}\n"
|
|
177
|
+
human_readable += f" Description: {agent.get('description', 'N/A')}\n"
|
|
178
|
+
|
|
179
|
+
capabilities = agent.get('capabilities', [])
|
|
180
|
+
if capabilities:
|
|
181
|
+
human_readable += f" Capabilities: {', '.join(capabilities)}\n"
|
|
182
|
+
human_readable += "\n"
|
|
183
|
+
|
|
184
|
+
# Return structured JSON
|
|
185
|
+
result = {
|
|
186
|
+
"type": "tool_result",
|
|
187
|
+
"tool": "list_agents",
|
|
188
|
+
"success": True,
|
|
189
|
+
"data": {
|
|
190
|
+
"agents": agents,
|
|
191
|
+
"count": len(agents)
|
|
192
|
+
},
|
|
193
|
+
"human_readable": human_readable
|
|
194
|
+
}
|
|
195
|
+
return json.dumps(result, indent=2)
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
logger.error("list_agents_tool_error", error=str(e), exc_info=True)
|
|
199
|
+
return json.dumps({
|
|
200
|
+
"type": "tool_result",
|
|
201
|
+
"tool": "list_agents",
|
|
202
|
+
"success": False,
|
|
203
|
+
"error": str(e)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
@cache_tool_result(ttl=60) # Cache for 60 seconds
|
|
207
|
+
def list_teams(self, limit: int = 20) -> str:
|
|
208
|
+
"""List available teams in the organization.
|
|
209
|
+
|
|
210
|
+
Use this to discover what teams are available for executing multi-agent tasks.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
limit: Maximum number of teams to return (default: 20)
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
JSON string with structured team data and human-readable format
|
|
217
|
+
"""
|
|
218
|
+
try:
|
|
219
|
+
# Call service method (direct DB query)
|
|
220
|
+
teams = self.planning_service.list_teams(limit=limit)
|
|
221
|
+
|
|
222
|
+
# Format human-readable output
|
|
223
|
+
if not teams:
|
|
224
|
+
human_readable = "No active teams found in the organization."
|
|
225
|
+
else:
|
|
226
|
+
human_readable = f"Found {len(teams)} active teams:\n\n"
|
|
227
|
+
for i, team in enumerate(teams, 1):
|
|
228
|
+
human_readable += f"Team {i}:\n"
|
|
229
|
+
human_readable += f" ID: {team.get('id')}\n"
|
|
230
|
+
human_readable += f" Name: {team.get('name')}\n"
|
|
231
|
+
human_readable += f" Description: {team.get('description', 'N/A')}\n"
|
|
232
|
+
human_readable += f" Agent Count: {team.get('agent_count', 0)}\n"
|
|
233
|
+
|
|
234
|
+
agents = team.get('agents', [])
|
|
235
|
+
if agents:
|
|
236
|
+
agent_names = [a.get('name', 'Unknown') for a in agents[:5]]
|
|
237
|
+
human_readable += f" Members: {', '.join(agent_names)}\n"
|
|
238
|
+
human_readable += "\n"
|
|
239
|
+
|
|
240
|
+
# Return structured JSON
|
|
241
|
+
result = {
|
|
242
|
+
"type": "tool_result",
|
|
243
|
+
"tool": "list_teams",
|
|
244
|
+
"success": True,
|
|
245
|
+
"data": {
|
|
246
|
+
"teams": teams,
|
|
247
|
+
"count": len(teams)
|
|
248
|
+
},
|
|
249
|
+
"human_readable": human_readable
|
|
250
|
+
}
|
|
251
|
+
return json.dumps(result, indent=2)
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.error("list_teams_tool_error", error=str(e), exc_info=True)
|
|
255
|
+
return json.dumps({
|
|
256
|
+
"type": "tool_result",
|
|
257
|
+
"tool": "list_teams",
|
|
258
|
+
"success": False,
|
|
259
|
+
"error": str(e)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
def search_agents_by_capability(
|
|
263
|
+
self,
|
|
264
|
+
capability: str,
|
|
265
|
+
limit: int = 10
|
|
266
|
+
) -> str:
|
|
267
|
+
"""Search for agents that have a specific capability or skill.
|
|
268
|
+
|
|
269
|
+
Use this to find agents with required skills like 'kubernetes', 'aws', 'python', etc.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
capability: Skill or capability name to search for (required)
|
|
273
|
+
limit: Maximum number of agents to return (default: 10)
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
JSON string with structured agent data and human-readable format
|
|
277
|
+
"""
|
|
278
|
+
try:
|
|
279
|
+
# Call service method (direct DB query)
|
|
280
|
+
agents = self.planning_service.search_agents_by_capability(
|
|
281
|
+
capability=capability,
|
|
282
|
+
limit=limit
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Format human-readable output
|
|
286
|
+
if not agents:
|
|
287
|
+
human_readable = f"No agents found with capability '{capability}'."
|
|
288
|
+
else:
|
|
289
|
+
human_readable = f"Found {len(agents)} agents with '{capability}' capability:\n\n"
|
|
290
|
+
for i, agent in enumerate(agents, 1):
|
|
291
|
+
human_readable += f"Agent {i}:\n"
|
|
292
|
+
human_readable += f" ID: {agent.get('id')}\n"
|
|
293
|
+
human_readable += f" Name: {agent.get('name')}\n"
|
|
294
|
+
human_readable += f" Model: {agent.get('model_id', 'default')}\n"
|
|
295
|
+
human_readable += f" Description: {agent.get('description', 'N/A')}\n"
|
|
296
|
+
|
|
297
|
+
capabilities = agent.get('capabilities', [])
|
|
298
|
+
if capabilities:
|
|
299
|
+
human_readable += f" All Capabilities: {', '.join(capabilities)}\n"
|
|
300
|
+
human_readable += "\n"
|
|
301
|
+
|
|
302
|
+
# Return structured JSON
|
|
303
|
+
result = {
|
|
304
|
+
"type": "tool_result",
|
|
305
|
+
"tool": "search_agents_by_capability",
|
|
306
|
+
"success": True,
|
|
307
|
+
"data": {
|
|
308
|
+
"agents": agents,
|
|
309
|
+
"count": len(agents),
|
|
310
|
+
"query": {
|
|
311
|
+
"capability": capability
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
"human_readable": human_readable
|
|
315
|
+
}
|
|
316
|
+
return json.dumps(result, indent=2)
|
|
317
|
+
|
|
318
|
+
except Exception as e:
|
|
319
|
+
logger.error("search_agents_by_capability_tool_error", error=str(e), exc_info=True)
|
|
320
|
+
return json.dumps({
|
|
321
|
+
"type": "tool_result",
|
|
322
|
+
"tool": "search_agents_by_capability",
|
|
323
|
+
"success": False,
|
|
324
|
+
"error": str(e)
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
def search_teams_by_capability(
|
|
328
|
+
self,
|
|
329
|
+
capability: str,
|
|
330
|
+
limit: int = 5
|
|
331
|
+
) -> str:
|
|
332
|
+
"""Search for teams that have agents with a specific capability.
|
|
333
|
+
|
|
334
|
+
Use this to find teams for multi-agent tasks requiring specific skills.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
capability: Skill or capability name to search for (required)
|
|
338
|
+
limit: Maximum number of teams to return (default: 5)
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
JSON string with structured team data and human-readable format
|
|
342
|
+
"""
|
|
343
|
+
try:
|
|
344
|
+
# Call service method (direct DB query)
|
|
345
|
+
teams = self.planning_service.search_teams_by_capability(
|
|
346
|
+
capability=capability,
|
|
347
|
+
limit=limit
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# Format human-readable output
|
|
351
|
+
if not teams:
|
|
352
|
+
human_readable = f"No teams found with capability '{capability}'."
|
|
353
|
+
else:
|
|
354
|
+
human_readable = f"Found {len(teams)} teams with '{capability}' capability:\n\n"
|
|
355
|
+
for i, team in enumerate(teams, 1):
|
|
356
|
+
human_readable += f"Team {i}:\n"
|
|
357
|
+
human_readable += f" ID: {team.get('id')}\n"
|
|
358
|
+
human_readable += f" Name: {team.get('name')}\n"
|
|
359
|
+
human_readable += f" Description: {team.get('description', 'N/A')}\n"
|
|
360
|
+
human_readable += f" Agent Count: {team.get('agent_count', 0)}\n"
|
|
361
|
+
|
|
362
|
+
agents = team.get('agents', [])
|
|
363
|
+
if agents:
|
|
364
|
+
agent_names = [a.get('name', 'Unknown') for a in agents[:5]]
|
|
365
|
+
human_readable += f" Members: {', '.join(agent_names)}\n"
|
|
366
|
+
human_readable += "\n"
|
|
367
|
+
|
|
368
|
+
# Return structured JSON
|
|
369
|
+
result = {
|
|
370
|
+
"type": "tool_result",
|
|
371
|
+
"tool": "search_teams_by_capability",
|
|
372
|
+
"success": True,
|
|
373
|
+
"data": {
|
|
374
|
+
"teams": teams,
|
|
375
|
+
"count": len(teams),
|
|
376
|
+
"query": {
|
|
377
|
+
"capability": capability
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"human_readable": human_readable
|
|
381
|
+
}
|
|
382
|
+
return json.dumps(result, indent=2)
|
|
383
|
+
|
|
384
|
+
except Exception as e:
|
|
385
|
+
logger.error("search_teams_by_capability_tool_error", error=str(e), exc_info=True)
|
|
386
|
+
return json.dumps({
|
|
387
|
+
"type": "tool_result",
|
|
388
|
+
"tool": "search_teams_by_capability",
|
|
389
|
+
"success": False,
|
|
390
|
+
"error": str(e)
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
def get_agent_details(self, agent_id: str) -> str:
|
|
394
|
+
"""Get complete details for a specific agent.
|
|
395
|
+
|
|
396
|
+
Use this after finding relevant agents to get full execution environment details.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
agent_id: Agent ID to fetch (required)
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
JSON string with full agent details and human-readable format
|
|
403
|
+
"""
|
|
404
|
+
try:
|
|
405
|
+
if not agent_id:
|
|
406
|
+
return json.dumps({
|
|
407
|
+
"type": "tool_result",
|
|
408
|
+
"tool": "get_agent_details",
|
|
409
|
+
"success": False,
|
|
410
|
+
"error": "agent_id is required"
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
# Call service method (direct DB query)
|
|
414
|
+
agent = self.planning_service.get_agent_details(agent_id=agent_id)
|
|
415
|
+
|
|
416
|
+
if not agent:
|
|
417
|
+
return json.dumps({
|
|
418
|
+
"type": "tool_result",
|
|
419
|
+
"tool": "get_agent_details",
|
|
420
|
+
"success": False,
|
|
421
|
+
"error": f"Agent {agent_id} not found"
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
# Format human-readable output
|
|
425
|
+
human_readable = f"Agent Details:\n"
|
|
426
|
+
human_readable += f" ID: {agent.get('id')}\n"
|
|
427
|
+
human_readable += f" Name: {agent.get('name')}\n"
|
|
428
|
+
human_readable += f" Model: {agent.get('model_id', 'default')}\n"
|
|
429
|
+
human_readable += f" Status: {agent.get('status')}\n"
|
|
430
|
+
human_readable += f" Description: {agent.get('description', 'N/A')}\n"
|
|
431
|
+
|
|
432
|
+
capabilities = agent.get('capabilities', [])
|
|
433
|
+
if capabilities:
|
|
434
|
+
human_readable += f" Capabilities: {', '.join(capabilities)}\n"
|
|
435
|
+
|
|
436
|
+
skills = agent.get('skills', [])
|
|
437
|
+
if skills:
|
|
438
|
+
human_readable += f" Skills: {len(skills)} configured\n"
|
|
439
|
+
|
|
440
|
+
exec_env = agent.get('execution_environment', {})
|
|
441
|
+
if exec_env:
|
|
442
|
+
if exec_env.get('secrets'):
|
|
443
|
+
human_readable += f" Secrets: {', '.join(exec_env['secrets'].keys())}\n"
|
|
444
|
+
if exec_env.get('env_vars'):
|
|
445
|
+
human_readable += f" Env Vars: {', '.join(exec_env['env_vars'].keys())}\n"
|
|
446
|
+
|
|
447
|
+
# Return structured JSON
|
|
448
|
+
result = {
|
|
449
|
+
"type": "tool_result",
|
|
450
|
+
"tool": "get_agent_details",
|
|
451
|
+
"success": True,
|
|
452
|
+
"data": {
|
|
453
|
+
"agent": agent
|
|
454
|
+
},
|
|
455
|
+
"human_readable": human_readable
|
|
456
|
+
}
|
|
457
|
+
return json.dumps(result, indent=2)
|
|
458
|
+
|
|
459
|
+
except Exception as e:
|
|
460
|
+
logger.error("get_agent_details_tool_error", error=str(e), exc_info=True)
|
|
461
|
+
return json.dumps({
|
|
462
|
+
"type": "tool_result",
|
|
463
|
+
"tool": "get_agent_details",
|
|
464
|
+
"success": False,
|
|
465
|
+
"error": str(e)
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
def get_team_details(self, team_id: str) -> str:
|
|
469
|
+
"""Get complete details for a specific team.
|
|
470
|
+
|
|
471
|
+
Use this after finding relevant teams to get full team composition and capabilities.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
team_id: Team ID to fetch (required)
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
JSON string with full team details and human-readable format
|
|
478
|
+
"""
|
|
479
|
+
try:
|
|
480
|
+
if not team_id:
|
|
481
|
+
return json.dumps({
|
|
482
|
+
"type": "tool_result",
|
|
483
|
+
"tool": "get_team_details",
|
|
484
|
+
"success": False,
|
|
485
|
+
"error": "team_id is required"
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
# Call service method (direct DB query)
|
|
489
|
+
team = self.planning_service.get_team_details(team_id=team_id)
|
|
490
|
+
|
|
491
|
+
if not team:
|
|
492
|
+
return json.dumps({
|
|
493
|
+
"type": "tool_result",
|
|
494
|
+
"tool": "get_team_details",
|
|
495
|
+
"success": False,
|
|
496
|
+
"error": f"Team {team_id} not found"
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
# Format human-readable output
|
|
500
|
+
human_readable = f"Team Details:\n"
|
|
501
|
+
human_readable += f" ID: {team.get('id')}\n"
|
|
502
|
+
human_readable += f" Name: {team.get('name')}\n"
|
|
503
|
+
human_readable += f" Status: {team.get('status')}\n"
|
|
504
|
+
human_readable += f" Description: {team.get('description', 'N/A')}\n"
|
|
505
|
+
human_readable += f" Agent Count: {team.get('agent_count', 0)}\n"
|
|
506
|
+
|
|
507
|
+
agents = team.get('agents', [])
|
|
508
|
+
if agents:
|
|
509
|
+
human_readable += f"\n Team Members:\n"
|
|
510
|
+
for agent in agents[:10]: # Show first 10
|
|
511
|
+
human_readable += f" - {agent.get('name')} ({agent.get('model_id', 'default')})\n"
|
|
512
|
+
|
|
513
|
+
# Return structured JSON
|
|
514
|
+
result = {
|
|
515
|
+
"type": "tool_result",
|
|
516
|
+
"tool": "get_team_details",
|
|
517
|
+
"success": True,
|
|
518
|
+
"data": {
|
|
519
|
+
"team": team
|
|
520
|
+
},
|
|
521
|
+
"human_readable": human_readable
|
|
522
|
+
}
|
|
523
|
+
return json.dumps(result, indent=2)
|
|
524
|
+
|
|
525
|
+
except Exception as e:
|
|
526
|
+
logger.error("get_team_details_tool_error", error=str(e), exc_info=True)
|
|
527
|
+
return json.dumps({
|
|
528
|
+
"type": "tool_result",
|
|
529
|
+
"tool": "get_team_details",
|
|
530
|
+
"success": False,
|
|
531
|
+
"error": str(e)
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
def search_context_graph(
|
|
535
|
+
self,
|
|
536
|
+
query: str,
|
|
537
|
+
label: Optional[str] = None,
|
|
538
|
+
limit: int = 20
|
|
539
|
+
) -> str:
|
|
540
|
+
"""Search the context graph for relevant resources.
|
|
541
|
+
|
|
542
|
+
Use this to discover services, repositories, or other resources that might be relevant.
|
|
543
|
+
|
|
544
|
+
Args:
|
|
545
|
+
query: Search query text (required)
|
|
546
|
+
label: Optional label to filter by (e.g., 'Service', 'Repository')
|
|
547
|
+
limit: Maximum results to return (default: 20)
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
JSON string with search results and human-readable format
|
|
551
|
+
"""
|
|
552
|
+
try:
|
|
553
|
+
if not query:
|
|
554
|
+
return json.dumps({
|
|
555
|
+
"type": "tool_result",
|
|
556
|
+
"tool": "search_context_graph",
|
|
557
|
+
"success": False,
|
|
558
|
+
"error": "query is required"
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
# This method is async, need to wrap it
|
|
562
|
+
# Get or create event loop
|
|
563
|
+
try:
|
|
564
|
+
loop = asyncio.get_event_loop()
|
|
565
|
+
except RuntimeError:
|
|
566
|
+
loop = asyncio.new_event_loop()
|
|
567
|
+
asyncio.set_event_loop(loop)
|
|
568
|
+
|
|
569
|
+
# Run async method synchronously
|
|
570
|
+
results = loop.run_until_complete(
|
|
571
|
+
self.planning_service.search_context_graph(
|
|
572
|
+
query=query,
|
|
573
|
+
label=label,
|
|
574
|
+
limit=limit
|
|
575
|
+
)
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
nodes = results.get('nodes', []) if isinstance(results, dict) else []
|
|
579
|
+
|
|
580
|
+
# Format human-readable output
|
|
581
|
+
if not nodes:
|
|
582
|
+
human_readable = f"No resources found matching '{query}'."
|
|
583
|
+
else:
|
|
584
|
+
human_readable = f"Found {len(nodes)} resources matching '{query}':\n\n"
|
|
585
|
+
for i, node in enumerate(nodes, 1):
|
|
586
|
+
labels = node.get('labels', [])
|
|
587
|
+
props = node.get('properties', {})
|
|
588
|
+
|
|
589
|
+
human_readable += f"Resource {i}:\n"
|
|
590
|
+
human_readable += f" ID: {node.get('id')}\n"
|
|
591
|
+
human_readable += f" Type: {', '.join(labels) if labels else 'Unknown'}\n"
|
|
592
|
+
|
|
593
|
+
# Show key properties
|
|
594
|
+
if props:
|
|
595
|
+
human_readable += f" Properties:\n"
|
|
596
|
+
for key, value in list(props.items())[:5]: # Show first 5 props
|
|
597
|
+
human_readable += f" {key}: {value}\n"
|
|
598
|
+
human_readable += "\n"
|
|
599
|
+
|
|
600
|
+
# Return structured JSON
|
|
601
|
+
result = {
|
|
602
|
+
"type": "tool_result",
|
|
603
|
+
"tool": "search_context_graph",
|
|
604
|
+
"success": True,
|
|
605
|
+
"data": {
|
|
606
|
+
"nodes": nodes,
|
|
607
|
+
"count": len(nodes),
|
|
608
|
+
"query": {
|
|
609
|
+
"text": query,
|
|
610
|
+
"label": label
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
"human_readable": human_readable
|
|
614
|
+
}
|
|
615
|
+
return json.dumps(result, indent=2)
|
|
616
|
+
|
|
617
|
+
except Exception as e:
|
|
618
|
+
logger.error("search_context_graph_tool_error", error=str(e), exc_info=True)
|
|
619
|
+
return json.dumps({
|
|
620
|
+
"type": "tool_result",
|
|
621
|
+
"tool": "search_context_graph",
|
|
622
|
+
"success": False,
|
|
623
|
+
"error": str(e)
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
@cache_tool_result(ttl=60) # Cache for 60 seconds
|
|
627
|
+
def list_environments(self, status: str = "active", limit: int = 20) -> str:
|
|
628
|
+
"""List available execution environments in the organization.
|
|
629
|
+
|
|
630
|
+
Use this to discover where agents and teams can be executed.
|
|
631
|
+
Environments provide execution context like secrets, env vars, and worker queues.
|
|
632
|
+
|
|
633
|
+
Args:
|
|
634
|
+
status: Filter by status (default: "active")
|
|
635
|
+
limit: Maximum number of environments to return (default: 20)
|
|
636
|
+
|
|
637
|
+
Returns:
|
|
638
|
+
JSON string with structured environment data and human-readable format
|
|
639
|
+
"""
|
|
640
|
+
try:
|
|
641
|
+
# Call service method (direct DB query)
|
|
642
|
+
environments = self.planning_service.list_environments(
|
|
643
|
+
status=status,
|
|
644
|
+
limit=limit
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
# Format human-readable output
|
|
648
|
+
if not environments:
|
|
649
|
+
human_readable = "No active environments found in the organization."
|
|
650
|
+
else:
|
|
651
|
+
human_readable = f"Found {len(environments)} active environments:\n\n"
|
|
652
|
+
for i, env in enumerate(environments, 1):
|
|
653
|
+
human_readable += f"Environment {i}:\n"
|
|
654
|
+
human_readable += f" ID: {env.get('id')}\n"
|
|
655
|
+
human_readable += f" Name: {env.get('name')}\n"
|
|
656
|
+
human_readable += f" Display Name: {env.get('display_name')}\n"
|
|
657
|
+
human_readable += f" Status: {env.get('status')}\n"
|
|
658
|
+
if env.get('description'):
|
|
659
|
+
human_readable += f" Description: {env.get('description')}\n"
|
|
660
|
+
tags = env.get('tags', [])
|
|
661
|
+
if tags:
|
|
662
|
+
human_readable += f" Tags: {', '.join(tags)}\n"
|
|
663
|
+
human_readable += "\n"
|
|
664
|
+
|
|
665
|
+
# Return structured JSON
|
|
666
|
+
result = {
|
|
667
|
+
"type": "tool_result",
|
|
668
|
+
"tool": "list_environments",
|
|
669
|
+
"success": True,
|
|
670
|
+
"data": {
|
|
671
|
+
"environments": environments,
|
|
672
|
+
"count": len(environments)
|
|
673
|
+
},
|
|
674
|
+
"human_readable": human_readable
|
|
675
|
+
}
|
|
676
|
+
return json.dumps(result, indent=2)
|
|
677
|
+
|
|
678
|
+
except Exception as e:
|
|
679
|
+
logger.error("list_environments_tool_error", error=str(e), exc_info=True)
|
|
680
|
+
return json.dumps({
|
|
681
|
+
"type": "tool_result",
|
|
682
|
+
"tool": "list_environments",
|
|
683
|
+
"success": False,
|
|
684
|
+
"error": str(e)
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
@cache_tool_result(ttl=60) # Cache for 60 seconds
|
|
688
|
+
def list_worker_queues(
|
|
689
|
+
self,
|
|
690
|
+
environment_id: str = None,
|
|
691
|
+
status: str = "active",
|
|
692
|
+
limit: int = 20
|
|
693
|
+
) -> str:
|
|
694
|
+
"""List available worker queues where tasks can be executed.
|
|
695
|
+
|
|
696
|
+
Worker queues are execution targets within environments. Each queue can have
|
|
697
|
+
multiple active workers ready to process tasks. Use this to find the best
|
|
698
|
+
queue for executing a task.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
environment_id: Optional - filter by environment ID (default: None, returns all)
|
|
702
|
+
status: Filter by status (default: "active")
|
|
703
|
+
limit: Maximum number of queues to return (default: 20)
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
JSON string with structured worker queue data including active worker counts
|
|
707
|
+
"""
|
|
708
|
+
try:
|
|
709
|
+
# Call service method (direct DB query)
|
|
710
|
+
worker_queues = self.planning_service.list_worker_queues(
|
|
711
|
+
environment_id=environment_id,
|
|
712
|
+
status=status,
|
|
713
|
+
limit=limit
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
# Format human-readable output
|
|
717
|
+
if not worker_queues:
|
|
718
|
+
human_readable = "No active worker queues found"
|
|
719
|
+
if environment_id:
|
|
720
|
+
human_readable += f" in environment {environment_id}"
|
|
721
|
+
human_readable += "."
|
|
722
|
+
else:
|
|
723
|
+
human_readable = f"Found {len(worker_queues)} active worker queues:\n\n"
|
|
724
|
+
for i, queue in enumerate(worker_queues, 1):
|
|
725
|
+
human_readable += f"Queue {i}:\n"
|
|
726
|
+
human_readable += f" ID: {queue.get('id')}\n"
|
|
727
|
+
human_readable += f" Name: {queue.get('name')}\n"
|
|
728
|
+
human_readable += f" Display Name: {queue.get('display_name')}\n"
|
|
729
|
+
human_readable += f" Environment ID: {queue.get('environment_id')}\n"
|
|
730
|
+
human_readable += f" Status: {queue.get('status')}\n"
|
|
731
|
+
human_readable += f" Active Workers: {queue.get('active_workers', 0)}\n"
|
|
732
|
+
if queue.get('description'):
|
|
733
|
+
human_readable += f" Description: {queue.get('description')}\n"
|
|
734
|
+
human_readable += "\n"
|
|
735
|
+
|
|
736
|
+
# Return structured JSON
|
|
737
|
+
result = {
|
|
738
|
+
"type": "tool_result",
|
|
739
|
+
"tool": "list_worker_queues",
|
|
740
|
+
"success": True,
|
|
741
|
+
"data": {
|
|
742
|
+
"worker_queues": worker_queues,
|
|
743
|
+
"count": len(worker_queues)
|
|
744
|
+
},
|
|
745
|
+
"human_readable": human_readable
|
|
746
|
+
}
|
|
747
|
+
return json.dumps(result, indent=2)
|
|
748
|
+
|
|
749
|
+
except Exception as e:
|
|
750
|
+
logger.error("list_worker_queues_tool_error", error=str(e), exc_info=True)
|
|
751
|
+
return json.dumps({
|
|
752
|
+
"type": "tool_result",
|
|
753
|
+
"tool": "list_worker_queues",
|
|
754
|
+
"success": False,
|
|
755
|
+
"error": str(e)
|
|
756
|
+
})
|
|
757
|
+
|
|
758
|
+
@cache_tool_result(ttl=300) # Cache for 5 minutes (fallback rarely changes)
|
|
759
|
+
def get_fallback_agent(self) -> str:
|
|
760
|
+
"""Get a general-purpose fallback agent when no specific match is found.
|
|
761
|
+
|
|
762
|
+
**Use this tool when**:
|
|
763
|
+
- search_agents_by_capability returns empty results
|
|
764
|
+
- No agents match the task requirements
|
|
765
|
+
- You need to select SOMETHING (never return None)
|
|
766
|
+
|
|
767
|
+
This returns the most recently used general-purpose agent, ensuring
|
|
768
|
+
Step 1 ALWAYS has an agent to select.
|
|
769
|
+
|
|
770
|
+
Returns:
|
|
771
|
+
JSON string with fallback agent details
|
|
772
|
+
"""
|
|
773
|
+
try:
|
|
774
|
+
# Try to get a versatile, recently-used agent
|
|
775
|
+
agents = self.planning_service.list_agents(limit=50)
|
|
776
|
+
|
|
777
|
+
if not agents:
|
|
778
|
+
# Absolutely no agents - return error
|
|
779
|
+
return json.dumps({
|
|
780
|
+
"type": "tool_result",
|
|
781
|
+
"tool": "get_fallback_agent",
|
|
782
|
+
"success": False,
|
|
783
|
+
"error": "No agents available in organization. Cannot select fallback."
|
|
784
|
+
})
|
|
785
|
+
|
|
786
|
+
# Find best fallback: prefer general-purpose, recently used
|
|
787
|
+
fallback_agent = None
|
|
788
|
+
|
|
789
|
+
# Priority 1: Agent with "general" in name or description
|
|
790
|
+
for agent in agents:
|
|
791
|
+
name_lower = agent.get("name", "").lower()
|
|
792
|
+
desc_lower = agent.get("description", "").lower()
|
|
793
|
+
if "general" in name_lower or "general" in desc_lower:
|
|
794
|
+
fallback_agent = agent
|
|
795
|
+
break
|
|
796
|
+
|
|
797
|
+
# Priority 2: First agent in list (most recently used/created)
|
|
798
|
+
if not fallback_agent:
|
|
799
|
+
fallback_agent = agents[0]
|
|
800
|
+
|
|
801
|
+
# Format human-readable output
|
|
802
|
+
human_readable = f"Fallback Agent (no perfect match found):\n\n"
|
|
803
|
+
human_readable += f"ID: {fallback_agent.get('id')}\n"
|
|
804
|
+
human_readable += f"Name: {fallback_agent.get('name')}\n"
|
|
805
|
+
human_readable += f"Model: {fallback_agent.get('model_id', 'default')}\n"
|
|
806
|
+
human_readable += f"Description: {fallback_agent.get('description', 'General purpose agent')}\n"
|
|
807
|
+
|
|
808
|
+
capabilities = fallback_agent.get('capabilities', [])
|
|
809
|
+
if capabilities:
|
|
810
|
+
human_readable += f"Capabilities: {', '.join(capabilities)}\n"
|
|
811
|
+
|
|
812
|
+
human_readable += "\n⚠️ This is a fallback selection. "
|
|
813
|
+
human_readable += "Consider explaining in your reasoning why no perfect match was found."
|
|
814
|
+
|
|
815
|
+
# Return structured JSON
|
|
816
|
+
result = {
|
|
817
|
+
"type": "tool_result",
|
|
818
|
+
"tool": "get_fallback_agent",
|
|
819
|
+
"success": True,
|
|
820
|
+
"data": {
|
|
821
|
+
"agent": fallback_agent,
|
|
822
|
+
"is_fallback": True,
|
|
823
|
+
"note": "No perfect match - this is the best available general-purpose agent"
|
|
824
|
+
},
|
|
825
|
+
"human_readable": human_readable
|
|
826
|
+
}
|
|
827
|
+
return json.dumps(result, indent=2)
|
|
828
|
+
|
|
829
|
+
except Exception as e:
|
|
830
|
+
logger.error("get_fallback_agent_tool_error", error=str(e), exc_info=True)
|
|
831
|
+
return json.dumps({
|
|
832
|
+
"type": "tool_result",
|
|
833
|
+
"tool": "get_fallback_agent",
|
|
834
|
+
"success": False,
|
|
835
|
+
"error": str(e)
|
|
836
|
+
})
|