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,394 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LiteLLM Service
|
|
3
|
+
|
|
4
|
+
This service provides a wrapper around LiteLLM for agent execution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Dict, List, Optional, Any
|
|
9
|
+
import litellm
|
|
10
|
+
from litellm import completion, get_valid_models
|
|
11
|
+
import logging
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
from control_plane_api.app.config import settings
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LiteLLMService:
|
|
20
|
+
"""Service for interacting with LiteLLM"""
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
"""Initialize LiteLLM service with configuration"""
|
|
24
|
+
# Set LiteLLM configuration
|
|
25
|
+
if settings.litellm_api_key:
|
|
26
|
+
os.environ["LITELLM_API_KEY"] = settings.litellm_api_key
|
|
27
|
+
litellm.api_base = settings.litellm_api_base
|
|
28
|
+
litellm.drop_params = True # Drop unsupported params instead of failing
|
|
29
|
+
|
|
30
|
+
# Configure timeout
|
|
31
|
+
litellm.request_timeout = settings.litellm_timeout
|
|
32
|
+
|
|
33
|
+
logger.info(f"LiteLLM Service initialized with base URL: {settings.litellm_api_base}")
|
|
34
|
+
|
|
35
|
+
async def fetch_available_models(self, base_url: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
36
|
+
"""
|
|
37
|
+
Fetch available models from the LiteLLM proxy server.
|
|
38
|
+
|
|
39
|
+
This method tries multiple approaches:
|
|
40
|
+
1. HTTP API call to /model/info endpoint (includes mode field)
|
|
41
|
+
2. Fallback to /v1/models endpoint with mode detection
|
|
42
|
+
3. Fallback to using LiteLLM SDK's get_valid_models() if available
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
base_url: Optional override for the LiteLLM base URL
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
List of model objects with mode field from the LiteLLM server
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
Exception if all methods fail
|
|
52
|
+
"""
|
|
53
|
+
# Use provided base_url or fall back to settings
|
|
54
|
+
api_base = base_url or settings.litellm_api_base
|
|
55
|
+
|
|
56
|
+
# Try Method 1: /model/info endpoint (includes mode)
|
|
57
|
+
try:
|
|
58
|
+
model_info_url = f"{api_base.rstrip('/')}/model/info"
|
|
59
|
+
logger.info(f"Fetching models from LiteLLM /model/info endpoint: {model_info_url}")
|
|
60
|
+
|
|
61
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
62
|
+
headers = {}
|
|
63
|
+
if settings.litellm_api_key:
|
|
64
|
+
headers["Authorization"] = f"Bearer {settings.litellm_api_key}"
|
|
65
|
+
|
|
66
|
+
response = await client.get(model_info_url, headers=headers)
|
|
67
|
+
response.raise_for_status()
|
|
68
|
+
|
|
69
|
+
data = response.json()
|
|
70
|
+
|
|
71
|
+
# Parse model_info response
|
|
72
|
+
models = []
|
|
73
|
+
for model_data in data.get("data", []):
|
|
74
|
+
models.append({
|
|
75
|
+
"id": model_data.get("model_name"),
|
|
76
|
+
"object": "model",
|
|
77
|
+
"created": 0,
|
|
78
|
+
"owned_by": model_data.get("litellm_provider", "unknown"),
|
|
79
|
+
"mode": model_data.get("mode", "completion") # Include mode from LiteLLM
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
logger.info(f"Successfully fetched {len(models)} models from LiteLLM /model/info with mode information")
|
|
83
|
+
return models
|
|
84
|
+
|
|
85
|
+
except httpx.HTTPStatusError as e:
|
|
86
|
+
logger.warning(
|
|
87
|
+
f"/model/info endpoint failed: {e.response.status_code}. "
|
|
88
|
+
f"Trying fallback to /v1/models..."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.warning(f"/model/info request failed: {str(e)}. Trying fallback to /v1/models...")
|
|
93
|
+
|
|
94
|
+
# Try Method 2: /v1/models endpoint (detect mode from model name)
|
|
95
|
+
try:
|
|
96
|
+
models_url = f"{api_base.rstrip('/')}/v1/models"
|
|
97
|
+
logger.info(f"Fetching models from LiteLLM server: {models_url}")
|
|
98
|
+
|
|
99
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
100
|
+
headers = {}
|
|
101
|
+
if settings.litellm_api_key:
|
|
102
|
+
headers["Authorization"] = f"Bearer {settings.litellm_api_key}"
|
|
103
|
+
|
|
104
|
+
response = await client.get(models_url, headers=headers)
|
|
105
|
+
response.raise_for_status()
|
|
106
|
+
|
|
107
|
+
data = response.json()
|
|
108
|
+
|
|
109
|
+
# LiteLLM returns models in OpenAI format: {"data": [...], "object": "list"}
|
|
110
|
+
raw_models = data.get("data", [])
|
|
111
|
+
|
|
112
|
+
# Add mode detection from model name
|
|
113
|
+
models = []
|
|
114
|
+
for model in raw_models:
|
|
115
|
+
model_id = model.get("id", "")
|
|
116
|
+
model_lower = model_id.lower()
|
|
117
|
+
|
|
118
|
+
# Detect mode from model name - expanded detection for embedding models
|
|
119
|
+
# Check for embedding keywords (including common embedding model patterns)
|
|
120
|
+
embedding_keywords = [
|
|
121
|
+
"embedding", "embed", "embeddings",
|
|
122
|
+
"ada-002", "text-embedding",
|
|
123
|
+
"bge-", "gte-", "e5-", # Common embedding model prefixes
|
|
124
|
+
"voyage-", "cohere-embed", # Provider-specific embedding models
|
|
125
|
+
"-embed-", "_embed_", "/embed", # Pattern matching
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
if any(keyword in model_lower for keyword in embedding_keywords):
|
|
129
|
+
mode = "embedding"
|
|
130
|
+
elif any(keyword in model_lower for keyword in ["gpt", "claude", "llama", "mistral", "gemini", "deepseek", "qwen"]):
|
|
131
|
+
mode = "chat"
|
|
132
|
+
else:
|
|
133
|
+
mode = "completion"
|
|
134
|
+
|
|
135
|
+
model["mode"] = mode
|
|
136
|
+
models.append(model)
|
|
137
|
+
|
|
138
|
+
logger.info(f"Successfully fetched {len(models)} models from LiteLLM server via /v1/models with mode detection")
|
|
139
|
+
return models
|
|
140
|
+
|
|
141
|
+
except httpx.HTTPStatusError as e:
|
|
142
|
+
logger.warning(
|
|
143
|
+
f"HTTP endpoint failed: {e.response.status_code}. "
|
|
144
|
+
f"Trying SDK fallback method..."
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.warning(f"HTTP request failed: {str(e)}. Trying SDK fallback method...")
|
|
149
|
+
|
|
150
|
+
# Try Method 3: Use LiteLLM Python SDK (synchronous, needs to be run in executor)
|
|
151
|
+
try:
|
|
152
|
+
import asyncio
|
|
153
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
154
|
+
|
|
155
|
+
def _get_models_sync():
|
|
156
|
+
"""Synchronous function to get models using LiteLLM SDK"""
|
|
157
|
+
try:
|
|
158
|
+
# Set environment for LiteLLM
|
|
159
|
+
if settings.litellm_api_key:
|
|
160
|
+
os.environ["LITELLM_API_KEY"] = settings.litellm_api_key
|
|
161
|
+
|
|
162
|
+
# Use get_valid_models with provider endpoint check
|
|
163
|
+
model_names = get_valid_models(check_provider_endpoint=True)
|
|
164
|
+
|
|
165
|
+
# Convert model strings to OpenAI-like format with mode detection
|
|
166
|
+
models = []
|
|
167
|
+
for model_name in model_names:
|
|
168
|
+
model_lower = model_name.lower()
|
|
169
|
+
|
|
170
|
+
# Detect mode from model name - expanded detection for embedding models
|
|
171
|
+
# Check for embedding keywords (including common embedding model patterns)
|
|
172
|
+
embedding_keywords = [
|
|
173
|
+
"embedding", "embed", "embeddings",
|
|
174
|
+
"ada-002", "text-embedding",
|
|
175
|
+
"bge-", "gte-", "e5-", # Common embedding model prefixes
|
|
176
|
+
"voyage-", "cohere-embed", # Provider-specific embedding models
|
|
177
|
+
"-embed-", "_embed_", "/embed", # Pattern matching
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
if any(keyword in model_lower for keyword in embedding_keywords):
|
|
181
|
+
mode = "embedding"
|
|
182
|
+
elif any(keyword in model_lower for keyword in ["gpt", "claude", "llama", "mistral", "gemini", "deepseek", "qwen"]):
|
|
183
|
+
mode = "chat"
|
|
184
|
+
else:
|
|
185
|
+
mode = "completion"
|
|
186
|
+
|
|
187
|
+
models.append({
|
|
188
|
+
"id": model_name,
|
|
189
|
+
"object": "model",
|
|
190
|
+
"created": 0,
|
|
191
|
+
"owned_by": "litellm",
|
|
192
|
+
"mode": mode
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
return models
|
|
196
|
+
except Exception as e:
|
|
197
|
+
logger.error(f"LiteLLM SDK get_valid_models failed: {str(e)}")
|
|
198
|
+
return []
|
|
199
|
+
|
|
200
|
+
# Run synchronous function in thread pool
|
|
201
|
+
loop = asyncio.get_event_loop()
|
|
202
|
+
with ThreadPoolExecutor() as executor:
|
|
203
|
+
models = await loop.run_in_executor(executor, _get_models_sync)
|
|
204
|
+
|
|
205
|
+
if models:
|
|
206
|
+
logger.info(f"Successfully fetched {len(models)} models using LiteLLM SDK with mode detection")
|
|
207
|
+
return models
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.error(f"LiteLLM SDK method failed: {str(e)}")
|
|
211
|
+
|
|
212
|
+
# If all methods failed, raise exception
|
|
213
|
+
raise Exception(
|
|
214
|
+
f"Failed to fetch models from LiteLLM server. "
|
|
215
|
+
f"Tried HTTP endpoint and SDK method. "
|
|
216
|
+
f"Please ensure LiteLLM proxy is running at {api_base}"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def validate_model(self, model_id: str) -> bool:
|
|
220
|
+
"""
|
|
221
|
+
Validate if a model ID is supported by the LiteLLM server.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
model_id: The model identifier to validate
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
True if the model is valid, False otherwise
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
# For now, we'll accept any model ID that follows the provider/model format
|
|
231
|
+
# More sophisticated validation can be added later by checking against
|
|
232
|
+
# the models list from the server
|
|
233
|
+
return bool(model_id and "/" in model_id)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.error(f"Error validating model {model_id}: {str(e)}")
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def execute_agent(
|
|
240
|
+
self,
|
|
241
|
+
prompt: str,
|
|
242
|
+
model: Optional[str] = None,
|
|
243
|
+
system_prompt: Optional[str] = None,
|
|
244
|
+
temperature: float = 0.7,
|
|
245
|
+
max_tokens: Optional[int] = None,
|
|
246
|
+
top_p: Optional[float] = None,
|
|
247
|
+
**kwargs: Any,
|
|
248
|
+
) -> Dict[str, Any]:
|
|
249
|
+
"""
|
|
250
|
+
Execute an agent with LiteLLM
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
prompt: The user prompt
|
|
254
|
+
model: Model identifier (defaults to configured default)
|
|
255
|
+
system_prompt: System prompt for the agent
|
|
256
|
+
temperature: Temperature for response generation
|
|
257
|
+
max_tokens: Maximum tokens to generate
|
|
258
|
+
top_p: Top-p sampling parameter
|
|
259
|
+
**kwargs: Additional parameters to pass to LiteLLM
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Dict containing the response and metadata
|
|
263
|
+
"""
|
|
264
|
+
try:
|
|
265
|
+
# Use default model if not specified
|
|
266
|
+
if not model:
|
|
267
|
+
model = settings.litellm_default_model
|
|
268
|
+
|
|
269
|
+
# Build messages
|
|
270
|
+
messages = []
|
|
271
|
+
if system_prompt:
|
|
272
|
+
messages.append({"role": "system", "content": system_prompt})
|
|
273
|
+
messages.append({"role": "user", "content": prompt})
|
|
274
|
+
|
|
275
|
+
# Prepare completion parameters
|
|
276
|
+
# For custom proxies, use openai/ prefix to force OpenAI-compatible mode
|
|
277
|
+
# This tells LiteLLM to use the base_url as an OpenAI-compatible endpoint
|
|
278
|
+
completion_params = {
|
|
279
|
+
"model": f"openai/{model}", # Use openai/ prefix for custom proxy
|
|
280
|
+
"messages": messages,
|
|
281
|
+
"temperature": temperature,
|
|
282
|
+
"api_key": settings.litellm_api_key or "dummy-key", # Fallback for when key is not set
|
|
283
|
+
"base_url": settings.litellm_api_base,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if max_tokens:
|
|
287
|
+
completion_params["max_tokens"] = max_tokens
|
|
288
|
+
if top_p:
|
|
289
|
+
completion_params["top_p"] = top_p
|
|
290
|
+
|
|
291
|
+
# Add any additional kwargs
|
|
292
|
+
completion_params.update(kwargs)
|
|
293
|
+
|
|
294
|
+
logger.info(f"Executing agent with model: {model} (using openai/{model})")
|
|
295
|
+
|
|
296
|
+
# Make the completion request
|
|
297
|
+
response = completion(**completion_params)
|
|
298
|
+
|
|
299
|
+
# Extract response content
|
|
300
|
+
result = {
|
|
301
|
+
"success": True,
|
|
302
|
+
"response": response.choices[0].message.content,
|
|
303
|
+
"model": model,
|
|
304
|
+
"usage": {
|
|
305
|
+
"prompt_tokens": response.usage.prompt_tokens,
|
|
306
|
+
"completion_tokens": response.usage.completion_tokens,
|
|
307
|
+
"total_tokens": response.usage.total_tokens,
|
|
308
|
+
},
|
|
309
|
+
"finish_reason": response.choices[0].finish_reason,
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
logger.info(f"Agent execution successful. Tokens used: {result['usage']['total_tokens']}")
|
|
313
|
+
return result
|
|
314
|
+
|
|
315
|
+
except Exception as e:
|
|
316
|
+
logger.error(f"Error executing agent: {str(e)}")
|
|
317
|
+
return {
|
|
318
|
+
"success": False,
|
|
319
|
+
"error": str(e),
|
|
320
|
+
"model": model or settings.litellm_default_model,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
def execute_agent_stream(
|
|
324
|
+
self,
|
|
325
|
+
prompt: str,
|
|
326
|
+
model: Optional[str] = None,
|
|
327
|
+
system_prompt: Optional[str] = None,
|
|
328
|
+
temperature: float = 0.7,
|
|
329
|
+
max_tokens: Optional[int] = None,
|
|
330
|
+
top_p: Optional[float] = None,
|
|
331
|
+
**kwargs: Any,
|
|
332
|
+
):
|
|
333
|
+
"""
|
|
334
|
+
Execute an agent with streaming response
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
prompt: The user prompt
|
|
338
|
+
model: Model identifier (defaults to configured default)
|
|
339
|
+
system_prompt: System prompt for the agent
|
|
340
|
+
temperature: Temperature for response generation
|
|
341
|
+
max_tokens: Maximum tokens to generate
|
|
342
|
+
top_p: Top-p sampling parameter
|
|
343
|
+
**kwargs: Additional parameters to pass to LiteLLM
|
|
344
|
+
|
|
345
|
+
Yields:
|
|
346
|
+
Response chunks as they arrive
|
|
347
|
+
"""
|
|
348
|
+
try:
|
|
349
|
+
# Use default model if not specified
|
|
350
|
+
if not model:
|
|
351
|
+
model = settings.litellm_default_model
|
|
352
|
+
|
|
353
|
+
# Build messages
|
|
354
|
+
messages = []
|
|
355
|
+
if system_prompt:
|
|
356
|
+
messages.append({"role": "system", "content": system_prompt})
|
|
357
|
+
messages.append({"role": "user", "content": prompt})
|
|
358
|
+
|
|
359
|
+
# Prepare completion parameters
|
|
360
|
+
# For custom proxies, use openai/ prefix to force OpenAI-compatible mode
|
|
361
|
+
# This tells LiteLLM to use the base_url as an OpenAI-compatible endpoint
|
|
362
|
+
completion_params = {
|
|
363
|
+
"model": f"openai/{model}", # Use openai/ prefix for custom proxy
|
|
364
|
+
"messages": messages,
|
|
365
|
+
"temperature": temperature,
|
|
366
|
+
"stream": True,
|
|
367
|
+
"api_key": settings.litellm_api_key or "dummy-key", # Fallback for when key is not set
|
|
368
|
+
"base_url": settings.litellm_api_base,
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if max_tokens:
|
|
372
|
+
completion_params["max_tokens"] = max_tokens
|
|
373
|
+
if top_p:
|
|
374
|
+
completion_params["top_p"] = top_p
|
|
375
|
+
|
|
376
|
+
# Add any additional kwargs
|
|
377
|
+
completion_params.update(kwargs)
|
|
378
|
+
|
|
379
|
+
logger.info(f"Executing agent (streaming) with model: {model} (using openai/{model})")
|
|
380
|
+
|
|
381
|
+
# Make the streaming completion request
|
|
382
|
+
response = completion(**completion_params)
|
|
383
|
+
|
|
384
|
+
for chunk in response:
|
|
385
|
+
if chunk.choices[0].delta.content:
|
|
386
|
+
yield chunk.choices[0].delta.content
|
|
387
|
+
|
|
388
|
+
except Exception as e:
|
|
389
|
+
logger.error(f"Error executing agent (streaming): {str(e)}")
|
|
390
|
+
yield f"Error: {str(e)}"
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# Singleton instance
|
|
394
|
+
litellm_service = LiteLLMService()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared plan generation service - used by both sync and async endpoints.
|
|
3
|
+
Clean code with zero duplication.
|
|
4
|
+
"""
|
|
5
|
+
from typing import Dict, Any, Callable
|
|
6
|
+
from sqlalchemy.orm import Session
|
|
7
|
+
import structlog
|
|
8
|
+
|
|
9
|
+
from control_plane_api.app.models.task_planning import TaskPlanRequest, TaskPlanResponse
|
|
10
|
+
from control_plane_api.app.lib.task_planning.planning_workflow import (
|
|
11
|
+
create_planning_workflow as create_multistep_workflow,
|
|
12
|
+
run_planning_workflow_stream,
|
|
13
|
+
)
|
|
14
|
+
from control_plane_api.app.lib.task_planning.entity_resolver import resolve_plan_entities
|
|
15
|
+
|
|
16
|
+
logger = structlog.get_logger()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def generate_plan(
|
|
20
|
+
request: TaskPlanRequest,
|
|
21
|
+
organization_id: str,
|
|
22
|
+
api_token: str,
|
|
23
|
+
db: Session,
|
|
24
|
+
event_callback: Callable[[Dict[str, Any]], None] = None,
|
|
25
|
+
) -> TaskPlanResponse:
|
|
26
|
+
"""
|
|
27
|
+
Generate a plan using the Agno workflow.
|
|
28
|
+
|
|
29
|
+
This is the SINGLE source of truth for plan generation,
|
|
30
|
+
used by both /tasks/plan/stream and async plan generation.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
request: Task plan request
|
|
34
|
+
organization_id: Organization ID
|
|
35
|
+
api_token: API token for authentication
|
|
36
|
+
db: Database session
|
|
37
|
+
event_callback: Optional callback for streaming events
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Generated TaskPlanResponse with resolved entities
|
|
41
|
+
"""
|
|
42
|
+
logger.info(
|
|
43
|
+
"generating_plan",
|
|
44
|
+
description=request.description[:100],
|
|
45
|
+
organization_id=organization_id,
|
|
46
|
+
quick_mode=request.quick_mode,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Create the 2-step planning workflow
|
|
50
|
+
workflow = create_multistep_workflow(
|
|
51
|
+
db=db,
|
|
52
|
+
organization_id=organization_id,
|
|
53
|
+
api_token=api_token,
|
|
54
|
+
quick_mode=request.quick_mode,
|
|
55
|
+
outer_context=None,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
logger.info("planning_workflow_created")
|
|
59
|
+
|
|
60
|
+
# Run the workflow with event streaming
|
|
61
|
+
plan = run_planning_workflow_stream(
|
|
62
|
+
workflow,
|
|
63
|
+
request,
|
|
64
|
+
event_callback or (lambda x: None), # No-op callback if not provided
|
|
65
|
+
request.quick_mode,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
logger.info("plan_generated", plan_title=plan.title)
|
|
69
|
+
|
|
70
|
+
# Resolve entity names to UUIDs
|
|
71
|
+
await resolve_plan_entities(
|
|
72
|
+
plan_response=plan,
|
|
73
|
+
organization_id=organization_id,
|
|
74
|
+
db=db,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
logger.info("plan_entities_resolved", plan_title=plan.title)
|
|
78
|
+
|
|
79
|
+
return plan
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Planning Strategy Pattern
|
|
3
|
+
|
|
4
|
+
Defines the interface for different task planning implementations (Agno, Claude Code SDK, etc.)
|
|
5
|
+
Like choosing transportation: train, walk, or flight - same destination, different approaches.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Dict, Any, AsyncIterator
|
|
10
|
+
from sqlalchemy.orm import Session
|
|
11
|
+
|
|
12
|
+
from control_plane_api.app.models.task_planning import TaskPlanResponse
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PlanningStrategy(ABC):
|
|
16
|
+
"""
|
|
17
|
+
Abstract base class for task planning strategies.
|
|
18
|
+
|
|
19
|
+
Each strategy implements the same interface but uses different underlying
|
|
20
|
+
technology (Agno, Claude Code SDK, etc.)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, db: Session, organization_id: str = None, api_token: str = None):
|
|
24
|
+
"""
|
|
25
|
+
Initialize strategy.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
db: Database session
|
|
29
|
+
organization_id: Organization ID
|
|
30
|
+
api_token: API token for authentication
|
|
31
|
+
"""
|
|
32
|
+
self.db = db
|
|
33
|
+
self.organization_id = organization_id
|
|
34
|
+
self.api_token = api_token
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def plan_task(self, planning_prompt: str) -> TaskPlanResponse:
|
|
38
|
+
"""
|
|
39
|
+
Generate a task plan (non-streaming).
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
planning_prompt: The complete planning prompt
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
TaskPlanResponse with the generated plan
|
|
46
|
+
"""
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
async def plan_task_stream(self, planning_prompt: str) -> AsyncIterator[Dict[str, Any]]:
|
|
51
|
+
"""
|
|
52
|
+
Generate a task plan with streaming events.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
planning_prompt: The complete planning prompt
|
|
56
|
+
|
|
57
|
+
Yields:
|
|
58
|
+
Dict with event type and data for SSE streaming
|
|
59
|
+
"""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def name(self) -> str:
|
|
65
|
+
"""Return the strategy name for logging"""
|
|
66
|
+
pass
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Planning Strategy Factory
|
|
3
|
+
|
|
4
|
+
Factory for creating the appropriate planning strategy based on configuration.
|
|
5
|
+
Like a travel agency - chooses the best transportation mode for you!
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import structlog
|
|
10
|
+
from typing import Literal
|
|
11
|
+
from sqlalchemy.orm import Session
|
|
12
|
+
|
|
13
|
+
from control_plane_api.app.services.planning_strategy import PlanningStrategy
|
|
14
|
+
from control_plane_api.app.services.claude_code_planning_service import ClaudeCodePlanningStrategy
|
|
15
|
+
from control_plane_api.app.services.agno_planning_strategy import AgnoPlanningStrategy
|
|
16
|
+
|
|
17
|
+
logger = structlog.get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
# Supported strategy types
|
|
20
|
+
StrategyType = Literal["claude_code_sdk", "agno"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_planning_strategy(
|
|
24
|
+
strategy_type: StrategyType = None,
|
|
25
|
+
db: Session = None,
|
|
26
|
+
organization_id: str = None,
|
|
27
|
+
api_token: str = None
|
|
28
|
+
) -> PlanningStrategy:
|
|
29
|
+
"""
|
|
30
|
+
Factory function to create the appropriate planning strategy.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
strategy_type: Which strategy to use ("claude_code_sdk" or "agno")
|
|
34
|
+
If None, uses PLANNING_STRATEGY env var (defaults to "agno")
|
|
35
|
+
db: Database session
|
|
36
|
+
organization_id: Organization ID
|
|
37
|
+
api_token: API token
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Concrete PlanningStrategy implementation
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
# Use Claude Code SDK (default)
|
|
44
|
+
strategy = get_planning_strategy(db=db)
|
|
45
|
+
|
|
46
|
+
# Explicitly use Agno
|
|
47
|
+
strategy = get_planning_strategy(strategy_type="agno", db=db)
|
|
48
|
+
|
|
49
|
+
# Use both in your code
|
|
50
|
+
plan = await strategy.plan_task(prompt) # Same interface!
|
|
51
|
+
"""
|
|
52
|
+
# Determine strategy from parameter or env var
|
|
53
|
+
if strategy_type is None:
|
|
54
|
+
strategy_type = os.getenv("PLANNING_STRATEGY", "agno").lower()
|
|
55
|
+
|
|
56
|
+
logger.info(
|
|
57
|
+
"creating_planning_strategy",
|
|
58
|
+
strategy_type=strategy_type,
|
|
59
|
+
has_db=db is not None,
|
|
60
|
+
has_org_id=organization_id is not None,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Validate strategy availability and detect CLI
|
|
64
|
+
if strategy_type == "claude_code_sdk":
|
|
65
|
+
# Check if SDK is available
|
|
66
|
+
try:
|
|
67
|
+
from claude_agent_sdk import ClaudeSDKClient
|
|
68
|
+
except ImportError:
|
|
69
|
+
logger.error(
|
|
70
|
+
"strategy_unavailable",
|
|
71
|
+
strategy_type=strategy_type,
|
|
72
|
+
message="claude-agent-sdk not available, falling back to Agno"
|
|
73
|
+
)
|
|
74
|
+
strategy_type = "agno" # Fallback
|
|
75
|
+
else:
|
|
76
|
+
# Check if Claude CLI binary is available
|
|
77
|
+
import shutil
|
|
78
|
+
claude_cli = shutil.which("claude")
|
|
79
|
+
if not claude_cli:
|
|
80
|
+
logger.warning(
|
|
81
|
+
"claude_cli_not_found",
|
|
82
|
+
message="Claude Code CLI binary not found in PATH. Falling back to Agno.",
|
|
83
|
+
hint="Install @anthropic-ai/claude-code npm package or use PLANNING_STRATEGY=agno"
|
|
84
|
+
)
|
|
85
|
+
strategy_type = "agno" # Auto-fallback
|
|
86
|
+
else:
|
|
87
|
+
logger.info(
|
|
88
|
+
"claude_cli_found",
|
|
89
|
+
cli_path=claude_cli,
|
|
90
|
+
message="Claude Code CLI available, using claude_code_sdk strategy"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Create the appropriate strategy
|
|
94
|
+
if strategy_type == "claude_code_sdk":
|
|
95
|
+
return ClaudeCodePlanningStrategy(
|
|
96
|
+
db=db,
|
|
97
|
+
organization_id=organization_id,
|
|
98
|
+
api_token=api_token
|
|
99
|
+
)
|
|
100
|
+
elif strategy_type == "agno":
|
|
101
|
+
return AgnoPlanningStrategy(
|
|
102
|
+
db=db,
|
|
103
|
+
organization_id=organization_id,
|
|
104
|
+
api_token=api_token
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
logger.error("unknown_strategy_type", strategy_type=strategy_type)
|
|
108
|
+
# Default to Claude Code SDK
|
|
109
|
+
logger.warning("defaulting_to_claude_code_sdk")
|
|
110
|
+
return ClaudeCodePlanningStrategy(
|
|
111
|
+
db=db,
|
|
112
|
+
organization_id=organization_id,
|
|
113
|
+
api_token=api_token
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# Convenience alias for backward compatibility
|
|
118
|
+
get_claude_code_planning_service = get_planning_strategy
|