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,274 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Manual test script to verify message deduplication works correctly.
|
|
4
|
+
Tests the session_service.deduplicate_messages method.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
sys.path.insert(0, '/Users/shaked/projects/kubiya-stack/agent-control-plane/control_plane_api')
|
|
9
|
+
|
|
10
|
+
from worker.services.session_service import SessionService
|
|
11
|
+
from worker.control_plane_client import ControlPlaneClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_deduplicate_messages():
|
|
15
|
+
"""Test that deduplicate_messages correctly removes duplicates."""
|
|
16
|
+
|
|
17
|
+
# Create session service
|
|
18
|
+
control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
|
|
19
|
+
session_service = SessionService(control_plane)
|
|
20
|
+
|
|
21
|
+
# Test data with duplicates
|
|
22
|
+
messages_with_duplicates = [
|
|
23
|
+
{
|
|
24
|
+
"role": "user",
|
|
25
|
+
"content": "Hello",
|
|
26
|
+
"message_id": "exec_123_user_1",
|
|
27
|
+
"timestamp": "2025-12-08T10:00:00Z"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"role": "assistant",
|
|
31
|
+
"content": "Hi there!",
|
|
32
|
+
"message_id": "exec_123_assistant_1",
|
|
33
|
+
"timestamp": "2025-12-08T10:00:01Z"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"role": "user",
|
|
37
|
+
"content": "Hello", # DUPLICATE
|
|
38
|
+
"message_id": "exec_123_user_1", # SAME ID
|
|
39
|
+
"timestamp": "2025-12-08T10:00:00Z"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"role": "user",
|
|
43
|
+
"content": "How are you?",
|
|
44
|
+
"message_id": "exec_123_user_2",
|
|
45
|
+
"timestamp": "2025-12-08T10:00:02Z"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"role": "assistant",
|
|
49
|
+
"content": "Hi there!", # DUPLICATE
|
|
50
|
+
"message_id": "exec_123_assistant_1", # SAME ID
|
|
51
|
+
"timestamp": "2025-12-08T10:00:01Z"
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
print(f"📝 Original messages: {len(messages_with_duplicates)}")
|
|
56
|
+
print()
|
|
57
|
+
for i, msg in enumerate(messages_with_duplicates):
|
|
58
|
+
print(f" {i+1}. [{msg['role']}] {msg['content'][:30]:<30} | ID: {msg['message_id']}")
|
|
59
|
+
|
|
60
|
+
# Deduplicate
|
|
61
|
+
deduplicated = session_service.deduplicate_messages(messages_with_duplicates)
|
|
62
|
+
|
|
63
|
+
print(f"\n✅ Deduplicated messages: {len(deduplicated)}")
|
|
64
|
+
print()
|
|
65
|
+
for i, msg in enumerate(deduplicated):
|
|
66
|
+
print(f" {i+1}. [{msg['role']}] {msg['content'][:30]:<30} | ID: {msg['message_id']}")
|
|
67
|
+
|
|
68
|
+
# Verify results
|
|
69
|
+
print(f"\n📊 Results:")
|
|
70
|
+
print(f" Original count: {len(messages_with_duplicates)}")
|
|
71
|
+
print(f" Deduplicated count: {len(deduplicated)}")
|
|
72
|
+
print(f" Removed: {len(messages_with_duplicates) - len(deduplicated)}")
|
|
73
|
+
|
|
74
|
+
# Check correctness
|
|
75
|
+
expected_count = 3 # Only 3 unique message_ids
|
|
76
|
+
if len(deduplicated) == expected_count:
|
|
77
|
+
print(f"\n✅ TEST PASSED: Deduplication working correctly!")
|
|
78
|
+
return True
|
|
79
|
+
else:
|
|
80
|
+
print(f"\n❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated)}")
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_content_based_deduplication():
|
|
85
|
+
"""Test that messages with same content but different IDs are deduplicated."""
|
|
86
|
+
|
|
87
|
+
control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
|
|
88
|
+
session_service = SessionService(control_plane)
|
|
89
|
+
|
|
90
|
+
# Test data with duplicate content but different message_ids
|
|
91
|
+
messages = [
|
|
92
|
+
{
|
|
93
|
+
"role": "assistant",
|
|
94
|
+
"content": "Hello, how can I help you today?",
|
|
95
|
+
"message_id": "exec_123_assistant_1",
|
|
96
|
+
"timestamp": "2025-12-12T10:00:00Z"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"role": "assistant",
|
|
100
|
+
"content": "Hello, how can I help you today?", # SAME CONTENT
|
|
101
|
+
"message_id": "exec_123_1733990400123456", # DIFFERENT ID (timestamp-based)
|
|
102
|
+
"timestamp": "2025-12-12T10:00:01Z" # Close timestamp (1 second apart)
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"role": "user",
|
|
106
|
+
"content": "Can you help me with Python?",
|
|
107
|
+
"message_id": "exec_123_user_1",
|
|
108
|
+
"timestamp": "2025-12-12T10:00:02Z"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"role": "assistant",
|
|
112
|
+
"content": "Sure! I'd be happy to help.",
|
|
113
|
+
"message_id": "exec_123_assistant_2",
|
|
114
|
+
"timestamp": "2025-12-12T10:00:03Z"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"role": "assistant",
|
|
118
|
+
"content": "Sure! I'd be happy to help.", # SAME CONTENT
|
|
119
|
+
"message_id": "exec_123_1733990403789012", # DIFFERENT ID
|
|
120
|
+
"timestamp": "2025-12-12T10:00:04Z" # Close timestamp (1 second apart)
|
|
121
|
+
},
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
print(f"\n📝 Testing content-based deduplication...")
|
|
125
|
+
print(f" Original: {len(messages)} messages")
|
|
126
|
+
print()
|
|
127
|
+
for i, msg in enumerate(messages):
|
|
128
|
+
print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | ID: {msg['message_id'][:25]}")
|
|
129
|
+
|
|
130
|
+
# Deduplicate
|
|
131
|
+
deduplicated = session_service.deduplicate_messages(messages)
|
|
132
|
+
|
|
133
|
+
print(f"\n✅ Deduplicated messages: {len(deduplicated)}")
|
|
134
|
+
print()
|
|
135
|
+
for i, msg in enumerate(deduplicated):
|
|
136
|
+
print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | ID: {msg['message_id'][:25]}")
|
|
137
|
+
|
|
138
|
+
# Verify results
|
|
139
|
+
print(f"\n📊 Results:")
|
|
140
|
+
print(f" Original count: {len(messages)}")
|
|
141
|
+
print(f" Deduplicated count: {len(deduplicated)}")
|
|
142
|
+
print(f" Removed: {len(messages) - len(deduplicated)}")
|
|
143
|
+
|
|
144
|
+
# Should have 3 messages (2 duplicates removed)
|
|
145
|
+
expected_count = 3
|
|
146
|
+
if len(deduplicated) == expected_count:
|
|
147
|
+
print(f"\n✅ TEST PASSED: Content-based deduplication working!")
|
|
148
|
+
return True
|
|
149
|
+
else:
|
|
150
|
+
print(f"\n❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated)}")
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_content_deduplication_with_distant_timestamps():
|
|
155
|
+
"""Test that messages with same content but distant timestamps are NOT deduplicated."""
|
|
156
|
+
|
|
157
|
+
control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
|
|
158
|
+
session_service = SessionService(control_plane)
|
|
159
|
+
|
|
160
|
+
# Test data with duplicate content but timestamps > 5 seconds apart
|
|
161
|
+
messages = [
|
|
162
|
+
{
|
|
163
|
+
"role": "assistant",
|
|
164
|
+
"content": "Let me help you with that.",
|
|
165
|
+
"message_id": "exec_123_assistant_1",
|
|
166
|
+
"timestamp": "2025-12-12T10:00:00Z"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"role": "user",
|
|
170
|
+
"content": "Thanks!",
|
|
171
|
+
"message_id": "exec_123_user_1",
|
|
172
|
+
"timestamp": "2025-12-12T10:00:03Z"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"role": "assistant",
|
|
176
|
+
"content": "Let me help you with that.", # SAME CONTENT
|
|
177
|
+
"message_id": "exec_123_assistant_2",
|
|
178
|
+
"timestamp": "2025-12-12T10:00:10Z" # 10 seconds later (> 5 second threshold)
|
|
179
|
+
},
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
print(f"\n📝 Testing content deduplication with distant timestamps...")
|
|
183
|
+
print(f" Original: {len(messages)} messages")
|
|
184
|
+
print()
|
|
185
|
+
for i, msg in enumerate(messages):
|
|
186
|
+
print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | Time: {msg['timestamp'][-9:]}")
|
|
187
|
+
|
|
188
|
+
# Deduplicate
|
|
189
|
+
deduplicated = session_service.deduplicate_messages(messages)
|
|
190
|
+
|
|
191
|
+
print(f"\n✅ Deduplicated messages: {len(deduplicated)}")
|
|
192
|
+
print()
|
|
193
|
+
for i, msg in enumerate(deduplicated):
|
|
194
|
+
print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | Time: {msg['timestamp'][-9:]}")
|
|
195
|
+
|
|
196
|
+
# Verify results - should keep all 3 messages (timestamps too far apart)
|
|
197
|
+
print(f"\n📊 Results:")
|
|
198
|
+
print(f" Original count: {len(messages)}")
|
|
199
|
+
print(f" Deduplicated count: {len(deduplicated)}")
|
|
200
|
+
print(f" Removed: {len(messages) - len(deduplicated)}")
|
|
201
|
+
|
|
202
|
+
# Should have 3 messages (no duplicates removed due to timestamp distance)
|
|
203
|
+
expected_count = 3
|
|
204
|
+
if len(deduplicated) == expected_count:
|
|
205
|
+
print(f"\n✅ TEST PASSED: Distant timestamps not incorrectly deduplicated!")
|
|
206
|
+
return True
|
|
207
|
+
else:
|
|
208
|
+
print(f"\n❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated)}")
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def test_messages_without_ids():
|
|
213
|
+
"""Test that messages without IDs are handled correctly."""
|
|
214
|
+
|
|
215
|
+
control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
|
|
216
|
+
session_service = SessionService(control_plane)
|
|
217
|
+
|
|
218
|
+
messages = [
|
|
219
|
+
{
|
|
220
|
+
"role": "user",
|
|
221
|
+
"content": "Message 1",
|
|
222
|
+
# NO message_id
|
|
223
|
+
"timestamp": "2025-12-08T10:00:00Z"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"role": "assistant",
|
|
227
|
+
"content": "Response 1",
|
|
228
|
+
"message_id": "exec_123_assistant_1",
|
|
229
|
+
"timestamp": "2025-12-08T10:00:01Z"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"role": "user",
|
|
233
|
+
"content": "Message 2",
|
|
234
|
+
# NO message_id
|
|
235
|
+
"timestamp": "2025-12-08T10:00:02Z"
|
|
236
|
+
},
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
print(f"\n📝 Testing messages without IDs...")
|
|
240
|
+
print(f" Original: {len(messages)} messages")
|
|
241
|
+
|
|
242
|
+
deduplicated = session_service.deduplicate_messages(messages)
|
|
243
|
+
|
|
244
|
+
print(f" Deduplicated: {len(deduplicated)} messages")
|
|
245
|
+
|
|
246
|
+
# Messages without IDs should be kept (they get warning but are included)
|
|
247
|
+
if len(deduplicated) == len(messages):
|
|
248
|
+
print(f"✅ Messages without IDs handled correctly (kept with warning)")
|
|
249
|
+
return True
|
|
250
|
+
else:
|
|
251
|
+
print(f"❌ Messages without IDs were incorrectly filtered")
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
if __name__ == "__main__":
|
|
256
|
+
print("=" * 70)
|
|
257
|
+
print("MESSAGE DEDUPLICATION TEST")
|
|
258
|
+
print("=" * 70)
|
|
259
|
+
print()
|
|
260
|
+
|
|
261
|
+
# Run tests
|
|
262
|
+
test1_passed = test_deduplicate_messages()
|
|
263
|
+
test2_passed = test_content_based_deduplication()
|
|
264
|
+
test3_passed = test_content_deduplication_with_distant_timestamps()
|
|
265
|
+
test4_passed = test_messages_without_ids()
|
|
266
|
+
|
|
267
|
+
print()
|
|
268
|
+
print("=" * 70)
|
|
269
|
+
if test1_passed and test2_passed and test3_passed and test4_passed:
|
|
270
|
+
print("✅ ALL TESTS PASSED")
|
|
271
|
+
sys.exit(0)
|
|
272
|
+
else:
|
|
273
|
+
print("❌ SOME TESTS FAILED")
|
|
274
|
+
sys.exit(1)
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
End-to-end integration test for executor deduplication.
|
|
4
|
+
Tests the full flow: session_history + new_messages → deduplication → persistence
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
sys.path.insert(0, '/Users/shaked/projects/kubiya-stack/agent-control-plane/control_plane_api')
|
|
9
|
+
|
|
10
|
+
from worker.services.session_service import SessionService
|
|
11
|
+
from worker.control_plane_client import ControlPlaneClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_executor_deduplication_flow():
|
|
15
|
+
"""
|
|
16
|
+
Simulate the executor flow where messages are combined and deduplicated.
|
|
17
|
+
This tests the ACTUAL code path used by executors.
|
|
18
|
+
"""
|
|
19
|
+
print("=" * 70)
|
|
20
|
+
print("EXECUTOR DEDUPLICATION - END-TO-END TEST")
|
|
21
|
+
print("=" * 70)
|
|
22
|
+
print()
|
|
23
|
+
|
|
24
|
+
# Create session service
|
|
25
|
+
control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
|
|
26
|
+
session_service = SessionService(control_plane)
|
|
27
|
+
|
|
28
|
+
print("📋 Simulating Executor Flow:")
|
|
29
|
+
print(" 1. Load session_history (previous turns)")
|
|
30
|
+
print(" 2. Add new_messages (current turn)")
|
|
31
|
+
print(" 3. Add tool_messages (from streaming)")
|
|
32
|
+
print(" 4. Combine all messages")
|
|
33
|
+
print(" 5. Deduplicate using session_service.deduplicate_messages()")
|
|
34
|
+
print()
|
|
35
|
+
|
|
36
|
+
# Simulate session_history (messages from previous turns)
|
|
37
|
+
session_history = [
|
|
38
|
+
{
|
|
39
|
+
"role": "user",
|
|
40
|
+
"content": "Hello",
|
|
41
|
+
"message_id": "exec_123_user_1",
|
|
42
|
+
"timestamp": "2025-12-12T10:00:00Z"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"role": "assistant",
|
|
46
|
+
"content": "Hi! How can I help you?",
|
|
47
|
+
"message_id": "exec_123_assistant_1",
|
|
48
|
+
"timestamp": "2025-12-12T10:00:01Z"
|
|
49
|
+
},
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# Simulate new_messages (current turn)
|
|
53
|
+
# NOTE: These might accidentally duplicate content from session_history due to bugs
|
|
54
|
+
new_messages = [
|
|
55
|
+
{
|
|
56
|
+
"role": "user",
|
|
57
|
+
"content": "Can you help with Python?",
|
|
58
|
+
"message_id": "exec_123_user_2",
|
|
59
|
+
"timestamp": "2025-12-12T10:00:05Z"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"role": "assistant",
|
|
63
|
+
"content": "Sure! I'd be happy to help with Python.",
|
|
64
|
+
"message_id": "exec_123_assistant_2",
|
|
65
|
+
"timestamp": "2025-12-12T10:00:06Z"
|
|
66
|
+
},
|
|
67
|
+
# DUPLICATE: Same content as assistant_2 but different message_id (timing issue)
|
|
68
|
+
{
|
|
69
|
+
"role": "assistant",
|
|
70
|
+
"content": "Sure! I'd be happy to help with Python.",
|
|
71
|
+
"message_id": "exec_123_1733990406789012", # Timestamp-based ID
|
|
72
|
+
"timestamp": "2025-12-12T10:00:07Z" # 1 second later
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
# Simulate tool_messages (from streaming helper)
|
|
77
|
+
tool_messages = [
|
|
78
|
+
{
|
|
79
|
+
"role": "system",
|
|
80
|
+
"tool_name": "python_repl",
|
|
81
|
+
"tool_output": "Executed successfully",
|
|
82
|
+
"message_id": "exec_123_tool_python_1",
|
|
83
|
+
"timestamp": "2025-12-12T10:00:08Z"
|
|
84
|
+
},
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
print("📊 Message Counts:")
|
|
88
|
+
print(f" - session_history: {len(session_history)} messages")
|
|
89
|
+
print(f" - new_messages: {len(new_messages)} messages")
|
|
90
|
+
print(f" - tool_messages: {len(tool_messages)} messages")
|
|
91
|
+
print()
|
|
92
|
+
|
|
93
|
+
# STEP 1: Combine messages (what executors do)
|
|
94
|
+
complete_session = session_history + new_messages + tool_messages
|
|
95
|
+
print(f"🔗 Combined messages: {len(complete_session)} total")
|
|
96
|
+
print()
|
|
97
|
+
|
|
98
|
+
# STEP 2: Deduplicate (using enhanced session_service method)
|
|
99
|
+
print("🧹 Deduplicating messages...")
|
|
100
|
+
original_count = len(complete_session)
|
|
101
|
+
deduplicated_session = session_service.deduplicate_messages(complete_session)
|
|
102
|
+
|
|
103
|
+
print()
|
|
104
|
+
print("📋 Deduplicated messages:")
|
|
105
|
+
for i, msg in enumerate(deduplicated_session):
|
|
106
|
+
role = msg.get("role", "unknown")
|
|
107
|
+
content = msg.get("content", msg.get("tool_name", ""))
|
|
108
|
+
msg_id = msg.get("message_id", "no_id")
|
|
109
|
+
timestamp = msg.get("timestamp", "")[-9:]
|
|
110
|
+
print(f" {i+1}. [{role:<9}] {content[:40]:<40} | {timestamp}")
|
|
111
|
+
|
|
112
|
+
print()
|
|
113
|
+
print("📊 Final Results:")
|
|
114
|
+
print(f" Original count: {original_count}")
|
|
115
|
+
print(f" Deduplicated count: {len(deduplicated_session)}")
|
|
116
|
+
print(f" Duplicates removed: {original_count - len(deduplicated_session)}")
|
|
117
|
+
print()
|
|
118
|
+
|
|
119
|
+
# Verify results
|
|
120
|
+
expected_count = 5 # user_1, assistant_1, user_2, assistant_2 (deduplicated), tool_1
|
|
121
|
+
if len(deduplicated_session) == expected_count:
|
|
122
|
+
print("✅ TEST PASSED: Executor deduplication working correctly!")
|
|
123
|
+
print(f" Expected {expected_count} messages, got {len(deduplicated_session)}")
|
|
124
|
+
return True
|
|
125
|
+
else:
|
|
126
|
+
print(f"❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated_session)}")
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_multiple_content_duplicates():
|
|
131
|
+
"""Test deduplication with multiple content duplicates (stress test)."""
|
|
132
|
+
print()
|
|
133
|
+
print("=" * 70)
|
|
134
|
+
print("STRESS TEST: Multiple Content Duplicates")
|
|
135
|
+
print("=" * 70)
|
|
136
|
+
print()
|
|
137
|
+
|
|
138
|
+
control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
|
|
139
|
+
session_service = SessionService(control_plane)
|
|
140
|
+
|
|
141
|
+
# Create messages with many duplicates
|
|
142
|
+
messages = [
|
|
143
|
+
# Original message
|
|
144
|
+
{
|
|
145
|
+
"role": "assistant",
|
|
146
|
+
"content": "Let me help you with that task.",
|
|
147
|
+
"message_id": "exec_456_assistant_1",
|
|
148
|
+
"timestamp": "2025-12-12T10:00:00Z"
|
|
149
|
+
},
|
|
150
|
+
# Duplicate 1 (different ID, 1 second later)
|
|
151
|
+
{
|
|
152
|
+
"role": "assistant",
|
|
153
|
+
"content": "Let me help you with that task.",
|
|
154
|
+
"message_id": "exec_456_1733990400123456",
|
|
155
|
+
"timestamp": "2025-12-12T10:00:01Z"
|
|
156
|
+
},
|
|
157
|
+
# Duplicate 2 (different ID, 2 seconds later)
|
|
158
|
+
{
|
|
159
|
+
"role": "assistant",
|
|
160
|
+
"content": "Let me help you with that task.",
|
|
161
|
+
"message_id": "exec_456_1733990400234567",
|
|
162
|
+
"timestamp": "2025-12-12T10:00:02Z"
|
|
163
|
+
},
|
|
164
|
+
# Different message
|
|
165
|
+
{
|
|
166
|
+
"role": "user",
|
|
167
|
+
"content": "Thanks!",
|
|
168
|
+
"message_id": "exec_456_user_1",
|
|
169
|
+
"timestamp": "2025-12-12T10:00:05Z"
|
|
170
|
+
},
|
|
171
|
+
# Another original
|
|
172
|
+
{
|
|
173
|
+
"role": "assistant",
|
|
174
|
+
"content": "You're welcome!",
|
|
175
|
+
"message_id": "exec_456_assistant_2",
|
|
176
|
+
"timestamp": "2025-12-12T10:00:06Z"
|
|
177
|
+
},
|
|
178
|
+
# Duplicate of "You're welcome!" (different ID, 1 second later)
|
|
179
|
+
{
|
|
180
|
+
"role": "assistant",
|
|
181
|
+
"content": "You're welcome!",
|
|
182
|
+
"message_id": "exec_456_1733990406345678",
|
|
183
|
+
"timestamp": "2025-12-12T10:00:07Z"
|
|
184
|
+
},
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
print(f"📝 Testing with {len(messages)} messages (4 expected duplicates)...")
|
|
188
|
+
print()
|
|
189
|
+
|
|
190
|
+
deduplicated = session_service.deduplicate_messages(messages)
|
|
191
|
+
|
|
192
|
+
print(f"✅ Deduplicated to {len(deduplicated)} messages")
|
|
193
|
+
print()
|
|
194
|
+
for i, msg in enumerate(deduplicated):
|
|
195
|
+
content = msg.get("content", "")[:40]
|
|
196
|
+
msg_id = msg.get("message_id", "")[:30]
|
|
197
|
+
print(f" {i+1}. [{msg['role']:<9}] {content:<40} | {msg_id}")
|
|
198
|
+
|
|
199
|
+
# Should have 3 unique messages (2 assistant + 1 user)
|
|
200
|
+
expected = 3
|
|
201
|
+
duplicates_removed = len(messages) - len(deduplicated)
|
|
202
|
+
|
|
203
|
+
print()
|
|
204
|
+
print(f"📊 Results:")
|
|
205
|
+
print(f" Original: {len(messages)}")
|
|
206
|
+
print(f" Deduplicated: {len(deduplicated)}")
|
|
207
|
+
print(f" Removed: {duplicates_removed}")
|
|
208
|
+
print()
|
|
209
|
+
|
|
210
|
+
if len(deduplicated) == expected and duplicates_removed == 3:
|
|
211
|
+
print(f"✅ STRESS TEST PASSED: Correctly removed {duplicates_removed} content duplicates!")
|
|
212
|
+
return True
|
|
213
|
+
else:
|
|
214
|
+
print(f"❌ STRESS TEST FAILED: Expected {expected} messages, got {len(deduplicated)}")
|
|
215
|
+
return False
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_mixed_duplicates():
|
|
219
|
+
"""Test with both message_id duplicates AND content duplicates."""
|
|
220
|
+
print()
|
|
221
|
+
print("=" * 70)
|
|
222
|
+
print("MIXED TEST: Both ID and Content Duplicates")
|
|
223
|
+
print("=" * 70)
|
|
224
|
+
print()
|
|
225
|
+
|
|
226
|
+
control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
|
|
227
|
+
session_service = SessionService(control_plane)
|
|
228
|
+
|
|
229
|
+
messages = [
|
|
230
|
+
# Original
|
|
231
|
+
{
|
|
232
|
+
"role": "assistant",
|
|
233
|
+
"content": "Hello there!",
|
|
234
|
+
"message_id": "exec_789_assistant_1",
|
|
235
|
+
"timestamp": "2025-12-12T10:00:00Z"
|
|
236
|
+
},
|
|
237
|
+
# ID duplicate (same message_id)
|
|
238
|
+
{
|
|
239
|
+
"role": "assistant",
|
|
240
|
+
"content": "Hello there!",
|
|
241
|
+
"message_id": "exec_789_assistant_1", # SAME ID
|
|
242
|
+
"timestamp": "2025-12-12T10:00:00Z"
|
|
243
|
+
},
|
|
244
|
+
# Content duplicate (different ID)
|
|
245
|
+
{
|
|
246
|
+
"role": "assistant",
|
|
247
|
+
"content": "Hello there!",
|
|
248
|
+
"message_id": "exec_789_1733990400111111", # DIFFERENT ID
|
|
249
|
+
"timestamp": "2025-12-12T10:00:01Z"
|
|
250
|
+
},
|
|
251
|
+
# Unique message
|
|
252
|
+
{
|
|
253
|
+
"role": "user",
|
|
254
|
+
"content": "Hi!",
|
|
255
|
+
"message_id": "exec_789_user_1",
|
|
256
|
+
"timestamp": "2025-12-12T10:00:05Z"
|
|
257
|
+
},
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
print(f"📝 Testing with {len(messages)} messages:")
|
|
261
|
+
print(" - 1 unique assistant message")
|
|
262
|
+
print(" - 1 ID duplicate (same message_id)")
|
|
263
|
+
print(" - 1 content duplicate (different message_id)")
|
|
264
|
+
print(" - 1 unique user message")
|
|
265
|
+
print()
|
|
266
|
+
|
|
267
|
+
deduplicated = session_service.deduplicate_messages(messages)
|
|
268
|
+
|
|
269
|
+
print(f"✅ Deduplicated to {len(deduplicated)} messages")
|
|
270
|
+
print()
|
|
271
|
+
|
|
272
|
+
# Should have 2 messages (1 assistant + 1 user)
|
|
273
|
+
expected = 2
|
|
274
|
+
duplicates_removed = len(messages) - len(deduplicated)
|
|
275
|
+
|
|
276
|
+
print(f"📊 Results:")
|
|
277
|
+
print(f" Original: {len(messages)}")
|
|
278
|
+
print(f" Deduplicated: {len(deduplicated)}")
|
|
279
|
+
print(f" Removed: {duplicates_removed}")
|
|
280
|
+
print()
|
|
281
|
+
|
|
282
|
+
if len(deduplicated) == expected and duplicates_removed == 2:
|
|
283
|
+
print(f"✅ MIXED TEST PASSED: Correctly handled both ID and content duplicates!")
|
|
284
|
+
return True
|
|
285
|
+
else:
|
|
286
|
+
print(f"❌ MIXED TEST FAILED: Expected {expected} messages, got {len(deduplicated)}")
|
|
287
|
+
return False
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
if __name__ == "__main__":
|
|
291
|
+
# Run all tests
|
|
292
|
+
test1_passed = test_executor_deduplication_flow()
|
|
293
|
+
test2_passed = test_multiple_content_duplicates()
|
|
294
|
+
test3_passed = test_mixed_duplicates()
|
|
295
|
+
|
|
296
|
+
print()
|
|
297
|
+
print("=" * 70)
|
|
298
|
+
if test1_passed and test2_passed and test3_passed:
|
|
299
|
+
print("✅ ALL END-TO-END TESTS PASSED")
|
|
300
|
+
print()
|
|
301
|
+
print("🎉 Executor deduplication is working correctly!")
|
|
302
|
+
print(" - Content-based deduplication: ✅")
|
|
303
|
+
print(" - ID-based deduplication: ✅")
|
|
304
|
+
print(" - Mixed duplicates: ✅")
|
|
305
|
+
print(" - Stress test: ✅")
|
|
306
|
+
sys.exit(0)
|
|
307
|
+
else:
|
|
308
|
+
print("❌ SOME TESTS FAILED")
|
|
309
|
+
sys.exit(1)
|