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,808 @@
|
|
|
1
|
+
"""Context Graph Search skill implementation for all runtimes."""
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
import structlog
|
|
5
|
+
from typing import Optional, Dict, Any, List
|
|
6
|
+
import httpx
|
|
7
|
+
from agno.tools import Toolkit
|
|
8
|
+
from control_plane_api.worker.skills.builtin.schema_fix_mixin import SchemaFixMixin
|
|
9
|
+
|
|
10
|
+
logger = structlog.get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContextGraphSearchTools(SchemaFixMixin, Toolkit):
|
|
14
|
+
"""
|
|
15
|
+
Context Graph Search toolkit for querying Neo4j-based context graphs and managing memory.
|
|
16
|
+
|
|
17
|
+
Provides tools for:
|
|
18
|
+
- **Memory**: Store and recall information persistently
|
|
19
|
+
- **Graph Search**: Search nodes by properties and relationships
|
|
20
|
+
- **Text Search**: Find nodes by text patterns
|
|
21
|
+
- **Custom Queries**: Execute Cypher queries
|
|
22
|
+
- **Metadata**: Get available labels and relationship types
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
api_base: Optional[str] = None,
|
|
28
|
+
timeout: int = 120, # 120s timeout for sync memory operations (cognify can take 30-60s)
|
|
29
|
+
default_limit: int = 100,
|
|
30
|
+
**kwargs
|
|
31
|
+
):
|
|
32
|
+
"""
|
|
33
|
+
Initialize Context Graph Search tools.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
api_base: Context Graph API base URL (defaults to CONTEXT_GRAPH_API_BASE env var)
|
|
37
|
+
timeout: Request timeout in seconds (default: 120s for sync memory operations)
|
|
38
|
+
default_limit: Default result limit for queries
|
|
39
|
+
**kwargs: Additional configuration
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(name="context-graph-search")
|
|
42
|
+
|
|
43
|
+
# Get authentication
|
|
44
|
+
self.api_key = os.environ.get("KUBIYA_API_KEY")
|
|
45
|
+
self.org_id = os.environ.get("KUBIYA_ORG_ID")
|
|
46
|
+
|
|
47
|
+
# Get dataset name for memory scoping (defaults to "default")
|
|
48
|
+
self.dataset_name = os.environ.get("MEMORY_DATASET_NAME", "default")
|
|
49
|
+
self._dataset_id = None # Lazy-loaded and cached
|
|
50
|
+
|
|
51
|
+
if not self.api_key:
|
|
52
|
+
logger.warning("No KUBIYA_API_KEY provided - context graph queries will fail")
|
|
53
|
+
|
|
54
|
+
# Resolve API base URL
|
|
55
|
+
# Priority: 1) explicit param, 2) env var, 3) fetch from control plane, 4) fallback
|
|
56
|
+
if api_base:
|
|
57
|
+
self.api_base = api_base.rstrip("/")
|
|
58
|
+
elif os.environ.get("CONTEXT_GRAPH_API_BASE"):
|
|
59
|
+
self.api_base = os.environ.get("CONTEXT_GRAPH_API_BASE").rstrip("/")
|
|
60
|
+
else:
|
|
61
|
+
# Fetch context-graph-api URL from control plane's client config
|
|
62
|
+
self.api_base = self._fetch_graph_url_from_control_plane() or "https://context-graph-api.dev.kubiya.ai"
|
|
63
|
+
self.api_base = self.api_base.rstrip("/")
|
|
64
|
+
|
|
65
|
+
self.timeout = timeout
|
|
66
|
+
self.default_limit = default_limit
|
|
67
|
+
|
|
68
|
+
# Prepare headers
|
|
69
|
+
self.headers = {
|
|
70
|
+
"Authorization": f"UserKey {self.api_key}",
|
|
71
|
+
"Accept": "application/json",
|
|
72
|
+
"Content-Type": "application/json",
|
|
73
|
+
"X-Kubiya-Client": "agent-runtime-builtin-tool",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if self.org_id:
|
|
77
|
+
self.headers["X-Organization-ID"] = self.org_id
|
|
78
|
+
|
|
79
|
+
# Register all tool methods
|
|
80
|
+
# Memory tools (most important - register first)
|
|
81
|
+
self.register(self.store_memory)
|
|
82
|
+
self.register(self.recall_memory)
|
|
83
|
+
# Knowledge ingestion tools
|
|
84
|
+
self.register(self.ingest_knowledge)
|
|
85
|
+
self.register(self.process_dataset)
|
|
86
|
+
# Graph search tools
|
|
87
|
+
self.register(self.search_nodes)
|
|
88
|
+
self.register(self.get_node)
|
|
89
|
+
self.register(self.search_by_text)
|
|
90
|
+
# Advanced tools
|
|
91
|
+
self.register(self.get_relationships)
|
|
92
|
+
self.register(self.get_subgraph)
|
|
93
|
+
self.register(self.execute_query)
|
|
94
|
+
self.register(self.get_labels)
|
|
95
|
+
self.register(self.get_relationship_types)
|
|
96
|
+
|
|
97
|
+
logger.info(f"Initialized Context Graph Search tools with memory and ingestion support (api_base: {self.api_base}, dataset: {self.dataset_name})")
|
|
98
|
+
|
|
99
|
+
# Fix: Rebuild function schemas with proper parameters
|
|
100
|
+
self._rebuild_function_schemas()
|
|
101
|
+
def _fetch_graph_url_from_control_plane(self) -> Optional[str]:
|
|
102
|
+
"""
|
|
103
|
+
Fetch the context graph API URL from control plane's client config endpoint.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Context graph API URL or None if fetch fails
|
|
107
|
+
"""
|
|
108
|
+
control_plane_url = os.environ.get("CONTROL_PLANE_URL", "http://localhost:7777")
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
with httpx.Client(timeout=5.0) as client:
|
|
112
|
+
headers = {
|
|
113
|
+
"Authorization": f"UserKey {self.api_key}",
|
|
114
|
+
"Accept": "application/json",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
response = client.get(
|
|
118
|
+
f"{control_plane_url}/api/v1/client/config",
|
|
119
|
+
headers=headers
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if response.status_code == 200:
|
|
123
|
+
config = response.json()
|
|
124
|
+
graph_url = config.get("context_graph_api_base")
|
|
125
|
+
logger.info(f"Fetched graph URL from control plane: {graph_url}")
|
|
126
|
+
return graph_url
|
|
127
|
+
else:
|
|
128
|
+
logger.warning(f"Failed to fetch client config: HTTP {response.status_code}")
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.warning(f"Could not fetch client config from control plane: {e}")
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
def _make_request(
|
|
136
|
+
self,
|
|
137
|
+
method: str,
|
|
138
|
+
path: str,
|
|
139
|
+
params: Optional[Dict[str, Any]] = None,
|
|
140
|
+
body: Optional[Dict[str, Any]] = None,
|
|
141
|
+
) -> Dict[str, Any]:
|
|
142
|
+
"""
|
|
143
|
+
Make HTTP request to Context Graph API.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
method: HTTP method (GET, POST, etc.)
|
|
147
|
+
path: API path (e.g., "/api/v1/graph/nodes")
|
|
148
|
+
params: Query parameters
|
|
149
|
+
body: Request body for POST requests
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Response JSON
|
|
153
|
+
|
|
154
|
+
Raises:
|
|
155
|
+
Exception: If request fails
|
|
156
|
+
"""
|
|
157
|
+
url = f"{self.api_base}{path}"
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
with httpx.Client(timeout=self.timeout) as client:
|
|
161
|
+
if method == "GET":
|
|
162
|
+
response = client.get(url, headers=self.headers, params=params)
|
|
163
|
+
elif method == "POST":
|
|
164
|
+
response = client.post(url, headers=self.headers, params=params, json=body)
|
|
165
|
+
else:
|
|
166
|
+
raise ValueError(f"Unsupported HTTP method: {method}")
|
|
167
|
+
|
|
168
|
+
response.raise_for_status()
|
|
169
|
+
return response.json()
|
|
170
|
+
|
|
171
|
+
except httpx.TimeoutException:
|
|
172
|
+
raise Exception(f"Request timed out after {self.timeout}s: {method} {path}")
|
|
173
|
+
except httpx.HTTPStatusError as e:
|
|
174
|
+
raise Exception(f"HTTP {e.response.status_code}: {e.response.text}")
|
|
175
|
+
except Exception as e:
|
|
176
|
+
raise Exception(f"Request failed: {str(e)}")
|
|
177
|
+
|
|
178
|
+
def search_nodes(
|
|
179
|
+
self,
|
|
180
|
+
label: Optional[str] = None,
|
|
181
|
+
property_name: Optional[str] = None,
|
|
182
|
+
property_value: Optional[str] = None,
|
|
183
|
+
integration: Optional[str] = None,
|
|
184
|
+
skip: int = 0,
|
|
185
|
+
limit: Optional[int] = None,
|
|
186
|
+
) -> str:
|
|
187
|
+
"""
|
|
188
|
+
Search for nodes in the context graph by label and/or properties.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
label: Node label to filter by (e.g., "User", "Repository", "Service")
|
|
192
|
+
property_name: Property name to filter by
|
|
193
|
+
property_value: Property value to match
|
|
194
|
+
integration: Integration name to filter by
|
|
195
|
+
skip: Number of results to skip
|
|
196
|
+
limit: Maximum number of results to return
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
JSON string with search results
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
search_nodes(label="User", property_name="email", property_value="user@example.com")
|
|
203
|
+
search_nodes(label="Repository", integration="github")
|
|
204
|
+
"""
|
|
205
|
+
body = {}
|
|
206
|
+
if label:
|
|
207
|
+
body["label"] = label
|
|
208
|
+
if property_name:
|
|
209
|
+
body["property_name"] = property_name
|
|
210
|
+
if property_value:
|
|
211
|
+
body["property_value"] = property_value
|
|
212
|
+
|
|
213
|
+
params = {
|
|
214
|
+
"skip": skip,
|
|
215
|
+
"limit": limit or self.default_limit,
|
|
216
|
+
}
|
|
217
|
+
if integration:
|
|
218
|
+
params["integration"] = integration
|
|
219
|
+
|
|
220
|
+
result = self._make_request("POST", "/api/v1/graph/nodes/search", params=params, body=body)
|
|
221
|
+
return json.dumps(result, indent=2)
|
|
222
|
+
|
|
223
|
+
def get_node(
|
|
224
|
+
self,
|
|
225
|
+
node_id: str,
|
|
226
|
+
integration: Optional[str] = None,
|
|
227
|
+
) -> str:
|
|
228
|
+
"""
|
|
229
|
+
Get a specific node by its ID.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
node_id: The node ID to retrieve
|
|
233
|
+
integration: Optional integration name to filter by
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
JSON string with node details
|
|
237
|
+
|
|
238
|
+
Example:
|
|
239
|
+
get_node(node_id="abc123")
|
|
240
|
+
"""
|
|
241
|
+
params = {}
|
|
242
|
+
if integration:
|
|
243
|
+
params["integration"] = integration
|
|
244
|
+
|
|
245
|
+
result = self._make_request("GET", f"/api/v1/graph/nodes/{node_id}", params=params)
|
|
246
|
+
return json.dumps(result, indent=2)
|
|
247
|
+
|
|
248
|
+
def get_relationships(
|
|
249
|
+
self,
|
|
250
|
+
node_id: str,
|
|
251
|
+
direction: str = "both",
|
|
252
|
+
relationship_type: Optional[str] = None,
|
|
253
|
+
integration: Optional[str] = None,
|
|
254
|
+
skip: int = 0,
|
|
255
|
+
limit: Optional[int] = None,
|
|
256
|
+
) -> str:
|
|
257
|
+
"""
|
|
258
|
+
Get relationships for a specific node.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
node_id: The node ID to get relationships for
|
|
262
|
+
direction: Relationship direction ("incoming", "outgoing", or "both")
|
|
263
|
+
relationship_type: Optional relationship type to filter by
|
|
264
|
+
integration: Optional integration name to filter by
|
|
265
|
+
skip: Number of results to skip
|
|
266
|
+
limit: Maximum number of results to return
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
JSON string with relationships
|
|
270
|
+
|
|
271
|
+
Example:
|
|
272
|
+
get_relationships(node_id="abc123", direction="outgoing", relationship_type="OWNS")
|
|
273
|
+
"""
|
|
274
|
+
params = {
|
|
275
|
+
"direction": direction,
|
|
276
|
+
"skip": skip,
|
|
277
|
+
"limit": limit or self.default_limit,
|
|
278
|
+
}
|
|
279
|
+
if relationship_type:
|
|
280
|
+
params["relationship_type"] = relationship_type
|
|
281
|
+
if integration:
|
|
282
|
+
params["integration"] = integration
|
|
283
|
+
|
|
284
|
+
result = self._make_request("GET", f"/api/v1/graph/nodes/{node_id}/relationships", params=params)
|
|
285
|
+
return json.dumps(result, indent=2)
|
|
286
|
+
|
|
287
|
+
def get_subgraph(
|
|
288
|
+
self,
|
|
289
|
+
node_id: str,
|
|
290
|
+
depth: int = 1,
|
|
291
|
+
relationship_types: Optional[List[str]] = None,
|
|
292
|
+
integration: Optional[str] = None,
|
|
293
|
+
) -> str:
|
|
294
|
+
"""
|
|
295
|
+
Get a subgraph starting from a node.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
node_id: Starting node ID
|
|
299
|
+
depth: Traversal depth (1-5)
|
|
300
|
+
relationship_types: Optional list of relationship types to follow
|
|
301
|
+
integration: Optional integration name to filter by
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
JSON string with subgraph (nodes and relationships)
|
|
305
|
+
|
|
306
|
+
Example:
|
|
307
|
+
get_subgraph(node_id="abc123", depth=2, relationship_types=["OWNS", "MANAGES"])
|
|
308
|
+
"""
|
|
309
|
+
body = {
|
|
310
|
+
"node_id": node_id,
|
|
311
|
+
"depth": min(max(depth, 1), 5), # Clamp between 1 and 5
|
|
312
|
+
}
|
|
313
|
+
if relationship_types:
|
|
314
|
+
body["relationship_types"] = relationship_types
|
|
315
|
+
|
|
316
|
+
params = {}
|
|
317
|
+
if integration:
|
|
318
|
+
params["integration"] = integration
|
|
319
|
+
|
|
320
|
+
result = self._make_request("POST", "/api/v1/graph/subgraph", params=params, body=body)
|
|
321
|
+
return json.dumps(result, indent=2)
|
|
322
|
+
|
|
323
|
+
def search_by_text(
|
|
324
|
+
self,
|
|
325
|
+
property_name: str,
|
|
326
|
+
search_text: str,
|
|
327
|
+
label: Optional[str] = None,
|
|
328
|
+
integration: Optional[str] = None,
|
|
329
|
+
skip: int = 0,
|
|
330
|
+
limit: Optional[int] = None,
|
|
331
|
+
) -> str:
|
|
332
|
+
"""
|
|
333
|
+
Search nodes by text pattern in a property.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
property_name: Property name to search in
|
|
337
|
+
search_text: Text to search for (supports partial matching)
|
|
338
|
+
label: Optional node label to filter by
|
|
339
|
+
integration: Optional integration name to filter by
|
|
340
|
+
skip: Number of results to skip
|
|
341
|
+
limit: Maximum number of results to return
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
JSON string with search results
|
|
345
|
+
|
|
346
|
+
Example:
|
|
347
|
+
search_by_text(property_name="name", search_text="kubernetes", label="Service")
|
|
348
|
+
"""
|
|
349
|
+
body = {
|
|
350
|
+
"property_name": property_name,
|
|
351
|
+
"search_text": search_text,
|
|
352
|
+
}
|
|
353
|
+
if label:
|
|
354
|
+
body["label"] = label
|
|
355
|
+
|
|
356
|
+
params = {
|
|
357
|
+
"skip": skip,
|
|
358
|
+
"limit": limit or self.default_limit,
|
|
359
|
+
}
|
|
360
|
+
if integration:
|
|
361
|
+
params["integration"] = integration
|
|
362
|
+
|
|
363
|
+
result = self._make_request("POST", "/api/v1/graph/nodes/search/text", params=params, body=body)
|
|
364
|
+
return json.dumps(result, indent=2)
|
|
365
|
+
|
|
366
|
+
def execute_query(
|
|
367
|
+
self,
|
|
368
|
+
query: str,
|
|
369
|
+
) -> str:
|
|
370
|
+
"""
|
|
371
|
+
Execute a custom Cypher query (read-only).
|
|
372
|
+
|
|
373
|
+
The query will be automatically scoped to your organization's data.
|
|
374
|
+
All node patterns will have the organization label injected.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
query: Cypher query to execute (read-only)
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
JSON string with query results
|
|
381
|
+
|
|
382
|
+
Example:
|
|
383
|
+
execute_query(query="MATCH (u:User)-[:OWNS]->(r:Repository) RETURN u.name, r.name LIMIT 10")
|
|
384
|
+
"""
|
|
385
|
+
body = {"query": query}
|
|
386
|
+
|
|
387
|
+
result = self._make_request("POST", "/api/v1/graph/query", body=body)
|
|
388
|
+
return json.dumps(result, indent=2)
|
|
389
|
+
|
|
390
|
+
def get_labels(
|
|
391
|
+
self,
|
|
392
|
+
integration: Optional[str] = None,
|
|
393
|
+
skip: int = 0,
|
|
394
|
+
limit: Optional[int] = None,
|
|
395
|
+
) -> str:
|
|
396
|
+
"""
|
|
397
|
+
Get all node labels in the context graph.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
integration: Optional integration name to filter by
|
|
401
|
+
skip: Number of results to skip
|
|
402
|
+
limit: Maximum number of results to return
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
JSON string with available labels
|
|
406
|
+
|
|
407
|
+
Example:
|
|
408
|
+
get_labels()
|
|
409
|
+
get_labels(integration="github")
|
|
410
|
+
"""
|
|
411
|
+
params = {
|
|
412
|
+
"skip": skip,
|
|
413
|
+
"limit": limit or self.default_limit,
|
|
414
|
+
}
|
|
415
|
+
if integration:
|
|
416
|
+
params["integration"] = integration
|
|
417
|
+
|
|
418
|
+
result = self._make_request("GET", "/api/v1/graph/labels", params=params)
|
|
419
|
+
return json.dumps(result, indent=2)
|
|
420
|
+
|
|
421
|
+
def get_relationship_types(
|
|
422
|
+
self,
|
|
423
|
+
integration: Optional[str] = None,
|
|
424
|
+
skip: int = 0,
|
|
425
|
+
limit: Optional[int] = None,
|
|
426
|
+
) -> str:
|
|
427
|
+
"""
|
|
428
|
+
Get all relationship types in the context graph.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
integration: Optional integration name to filter by
|
|
432
|
+
skip: Number of results to skip
|
|
433
|
+
limit: Maximum number of results to return
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
JSON string with available relationship types
|
|
437
|
+
|
|
438
|
+
Example:
|
|
439
|
+
get_relationship_types()
|
|
440
|
+
get_relationship_types(integration="github")
|
|
441
|
+
"""
|
|
442
|
+
params = {
|
|
443
|
+
"skip": skip,
|
|
444
|
+
"limit": limit or self.default_limit,
|
|
445
|
+
}
|
|
446
|
+
if integration:
|
|
447
|
+
params["integration"] = integration
|
|
448
|
+
|
|
449
|
+
result = self._make_request("GET", "/api/v1/graph/relationship-types", params=params)
|
|
450
|
+
return json.dumps(result, indent=2)
|
|
451
|
+
|
|
452
|
+
def _get_or_create_dataset(self) -> str:
|
|
453
|
+
"""
|
|
454
|
+
Get or create dataset ID for memory operations (cached).
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
Dataset ID (UUID string)
|
|
458
|
+
"""
|
|
459
|
+
if self._dataset_id:
|
|
460
|
+
return self._dataset_id
|
|
461
|
+
|
|
462
|
+
try:
|
|
463
|
+
# List datasets
|
|
464
|
+
response = self._make_request("GET", "/api/v1/graph/datasets")
|
|
465
|
+
|
|
466
|
+
# API returns {"datasets": [...]} - extract the list
|
|
467
|
+
datasets = response.get("datasets", []) if isinstance(response, dict) else response
|
|
468
|
+
|
|
469
|
+
if isinstance(datasets, list):
|
|
470
|
+
for ds in datasets:
|
|
471
|
+
if ds.get("name") == self.dataset_name:
|
|
472
|
+
self._dataset_id = ds["id"]
|
|
473
|
+
logger.info(f"Found dataset: {self.dataset_name} ({self._dataset_id})")
|
|
474
|
+
return self._dataset_id
|
|
475
|
+
|
|
476
|
+
# Create dataset if not found
|
|
477
|
+
create_body = {
|
|
478
|
+
"name": self.dataset_name,
|
|
479
|
+
"description": f"Memory dataset for {self.dataset_name}",
|
|
480
|
+
"scope": "org",
|
|
481
|
+
}
|
|
482
|
+
result = self._make_request("POST", "/api/v1/graph/datasets", body=create_body)
|
|
483
|
+
self._dataset_id = result.get("id")
|
|
484
|
+
logger.info(f"Created dataset: {self.dataset_name} ({self._dataset_id})")
|
|
485
|
+
return self._dataset_id
|
|
486
|
+
|
|
487
|
+
except Exception as e:
|
|
488
|
+
logger.error(f"Failed to get/create dataset: {e}")
|
|
489
|
+
raise
|
|
490
|
+
|
|
491
|
+
def store_memory(
|
|
492
|
+
self,
|
|
493
|
+
content: str,
|
|
494
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
495
|
+
wait_for_completion: bool = False,
|
|
496
|
+
) -> str:
|
|
497
|
+
"""
|
|
498
|
+
Store information in persistent memory for later recall.
|
|
499
|
+
|
|
500
|
+
Use this to remember important information across conversations like:
|
|
501
|
+
- User preferences and context
|
|
502
|
+
- Task progress and decisions
|
|
503
|
+
- System configurations and credentials
|
|
504
|
+
- Important facts and observations
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
content: Information to remember (clear, descriptive text)
|
|
508
|
+
metadata: Optional metadata (e.g., {"category": "user_preference", "priority": "high"})
|
|
509
|
+
wait_for_completion: If True, wait for indexing to complete before returning (5-30s).
|
|
510
|
+
If False (default), return immediately with job_id for async processing.
|
|
511
|
+
Set to True when you need to recall the memory immediately after storage.
|
|
512
|
+
|
|
513
|
+
Returns:
|
|
514
|
+
Success message with memory ID (and job_id if async mode)
|
|
515
|
+
|
|
516
|
+
Example:
|
|
517
|
+
# Async mode (default) - returns immediately, indexing happens in background
|
|
518
|
+
store_memory("User prefers Python over JavaScript for scripting tasks")
|
|
519
|
+
|
|
520
|
+
# Sync mode - waits for indexing, memory immediately searchable
|
|
521
|
+
store_memory("Critical security policy", wait_for_completion=True)
|
|
522
|
+
|
|
523
|
+
# With metadata
|
|
524
|
+
store_memory("Production DB read-only on weekends",
|
|
525
|
+
metadata={"category": "policy"},
|
|
526
|
+
wait_for_completion=True)
|
|
527
|
+
"""
|
|
528
|
+
try:
|
|
529
|
+
dataset_id = self._get_or_create_dataset()
|
|
530
|
+
|
|
531
|
+
# API expects dataset_id and context in body, metadata optional in body
|
|
532
|
+
body = {
|
|
533
|
+
"dataset_id": dataset_id,
|
|
534
|
+
"context": {"content": content},
|
|
535
|
+
"metadata": metadata or {},
|
|
536
|
+
"sync": wait_for_completion, # Agent controls sync vs async
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
result = self._make_request("POST", "/api/v1/graph/memory/store", body=body)
|
|
540
|
+
memory_id = result.get("memory_id", "unknown")
|
|
541
|
+
|
|
542
|
+
if wait_for_completion:
|
|
543
|
+
# Sync mode - memory is indexed and searchable now
|
|
544
|
+
return f"â Memory stored and indexed successfully (ID: {memory_id}). Ready for immediate recall."
|
|
545
|
+
else:
|
|
546
|
+
# Async mode - memory is being processed in background
|
|
547
|
+
job_id = result.get("job_id", "unknown")
|
|
548
|
+
return f"â Memory storage initiated (ID: {memory_id}, Job: {job_id}). Indexing in progress (typically takes 10-30 seconds)."
|
|
549
|
+
|
|
550
|
+
except Exception as e:
|
|
551
|
+
logger.error(f"Failed to store memory: {e}")
|
|
552
|
+
return f"Error storing memory: {str(e)}"
|
|
553
|
+
|
|
554
|
+
def recall_memory(
|
|
555
|
+
self,
|
|
556
|
+
query: str,
|
|
557
|
+
limit: int = 5,
|
|
558
|
+
search_type: str = "GRAPH_COMPLETION",
|
|
559
|
+
) -> str:
|
|
560
|
+
"""
|
|
561
|
+
Search and retrieve previously stored memories using semantic search.
|
|
562
|
+
|
|
563
|
+
Use this to recall information from past conversations or stored context.
|
|
564
|
+
The search is semantic (vector similarity), so use natural language queries.
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
query: What you want to remember (natural language query)
|
|
568
|
+
limit: Maximum number of memories to return (default: 5, max: 100)
|
|
569
|
+
search_type: Search strategy (default: "GRAPH_COMPLETION")
|
|
570
|
+
- "GRAPH_COMPLETION": Best for general recall (default, recommended)
|
|
571
|
+
- "CHUNKS": Raw text chunks without graph context
|
|
572
|
+
- "RAG_COMPLETION": Retrieval-augmented generation format
|
|
573
|
+
- "TEMPORAL": Time-based recall (if available)
|
|
574
|
+
- "FEEDBACK": Feedback-enhanced results (if available)
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
Formatted list of relevant memories with metadata and relevance scores
|
|
578
|
+
|
|
579
|
+
Example:
|
|
580
|
+
recall_memory("What are the user's preferences?")
|
|
581
|
+
recall_memory("production database policies", limit=10)
|
|
582
|
+
recall_memory("recent kubernetes issues", search_type="TEMPORAL")
|
|
583
|
+
"""
|
|
584
|
+
try:
|
|
585
|
+
# Validate search_type
|
|
586
|
+
valid_types = ["GRAPH_COMPLETION", "CHUNKS", "RAG_COMPLETION", "TEMPORAL", "FEEDBACK"]
|
|
587
|
+
if search_type and search_type not in valid_types:
|
|
588
|
+
logger.warning(f"Invalid search_type '{search_type}', using GRAPH_COMPLETION")
|
|
589
|
+
search_type = "GRAPH_COMPLETION"
|
|
590
|
+
|
|
591
|
+
# Use semantic search endpoint (/api/v1/graph/search) which searches across all accessible memories
|
|
592
|
+
# This performs vector similarity search with optional graph context
|
|
593
|
+
body = {
|
|
594
|
+
"query": query,
|
|
595
|
+
"limit": min(max(limit, 1), 100), # Clamp between 1 and 100
|
|
596
|
+
"search_type": search_type,
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
result = self._make_request("POST", "/api/v1/graph/search", body=body)
|
|
600
|
+
|
|
601
|
+
# API returns {query: str, results: list, count: int}
|
|
602
|
+
results = result.get("results", [])
|
|
603
|
+
|
|
604
|
+
if not results or len(results) == 0:
|
|
605
|
+
return f"No memories found for query: '{query}'\n\nTry:\n- Using more specific keywords\n- Asking in a different way\n- Checking if memories were stored in this dataset"
|
|
606
|
+
|
|
607
|
+
# Format results with proper structure
|
|
608
|
+
formatted = f"Found {len(results)} relevant memories for '{query}':\n\n"
|
|
609
|
+
|
|
610
|
+
for i, item in enumerate(results, 1):
|
|
611
|
+
# Extract content from various possible structures
|
|
612
|
+
if isinstance(item, dict):
|
|
613
|
+
content = (
|
|
614
|
+
item.get('text') or
|
|
615
|
+
item.get('content') or
|
|
616
|
+
item.get('chunk_text') or
|
|
617
|
+
(str(item.get('data')) if item.get('data') else None) or
|
|
618
|
+
'No content available'
|
|
619
|
+
)
|
|
620
|
+
else:
|
|
621
|
+
content = str(item)
|
|
622
|
+
|
|
623
|
+
# Truncate long content but show full length in metadata
|
|
624
|
+
content_preview = content[:500]
|
|
625
|
+
if len(content) > 500:
|
|
626
|
+
content_preview += f"... ({len(content)} chars total)"
|
|
627
|
+
|
|
628
|
+
formatted += f"{i}. {content_preview}\n"
|
|
629
|
+
|
|
630
|
+
# Add relevance score (most important metadata)
|
|
631
|
+
if isinstance(item, dict):
|
|
632
|
+
score = item.get('score') or item.get('similarity_score')
|
|
633
|
+
if isinstance(item.get('distance'), (int, float)):
|
|
634
|
+
# Convert distance to similarity (0 distance = 100% similarity)
|
|
635
|
+
score = 1.0 - min(item.get('distance'), 1.0)
|
|
636
|
+
|
|
637
|
+
if score is not None:
|
|
638
|
+
score_pct = score * 100
|
|
639
|
+
quality = "excellent" if score >= 0.8 else "good" if score >= 0.6 else "moderate"
|
|
640
|
+
formatted += f" đ Relevance: {score_pct:.1f}% ({quality} match)\n"
|
|
641
|
+
|
|
642
|
+
# Add source/type if available
|
|
643
|
+
source_type = item.get('type') or item.get('node_type')
|
|
644
|
+
if source_type:
|
|
645
|
+
formatted += f" đˇī¸ Type: {source_type}\n"
|
|
646
|
+
|
|
647
|
+
# Add dataset info if available
|
|
648
|
+
if item.get('dataset_name'):
|
|
649
|
+
formatted += f" đ Dataset: {item['dataset_name']}\n"
|
|
650
|
+
|
|
651
|
+
# Add timestamp if available
|
|
652
|
+
if item.get('created_at'):
|
|
653
|
+
try:
|
|
654
|
+
from datetime import datetime
|
|
655
|
+
dt = datetime.fromisoformat(item['created_at'].replace('Z', '+00:00'))
|
|
656
|
+
formatted += f" đ
Created: {dt.strftime('%Y-%m-%d %H:%M')}\n"
|
|
657
|
+
except:
|
|
658
|
+
formatted += f" đ
Created: {item['created_at']}\n"
|
|
659
|
+
|
|
660
|
+
# Add custom metadata if present (excluding internal fields)
|
|
661
|
+
if item.get('metadata'):
|
|
662
|
+
meta = item['metadata']
|
|
663
|
+
if isinstance(meta, dict):
|
|
664
|
+
# Filter out internal/empty metadata
|
|
665
|
+
display_meta = {k: v for k, v in meta.items()
|
|
666
|
+
if v and k not in ['_internal', 'embedding', 'vector']}
|
|
667
|
+
if display_meta:
|
|
668
|
+
formatted += f" âšī¸ Metadata: {json.dumps(display_meta)}\n"
|
|
669
|
+
|
|
670
|
+
formatted += "\n"
|
|
671
|
+
|
|
672
|
+
return formatted
|
|
673
|
+
|
|
674
|
+
except Exception as e:
|
|
675
|
+
error_msg = str(e)
|
|
676
|
+
logger.error(f"Failed to recall memory: {error_msg}")
|
|
677
|
+
|
|
678
|
+
# Provide helpful error messages
|
|
679
|
+
if "404" in error_msg:
|
|
680
|
+
return f"Memory search endpoint not found. Please check API configuration."
|
|
681
|
+
elif "503" in error_msg:
|
|
682
|
+
return f"Cognitive memory features are not enabled. Contact your administrator."
|
|
683
|
+
elif "401" in error_msg or "403" in error_msg:
|
|
684
|
+
return f"Authentication failed. Check your API key and permissions."
|
|
685
|
+
else:
|
|
686
|
+
return f"Error recalling memory: {error_msg}"
|
|
687
|
+
|
|
688
|
+
def ingest_knowledge(
|
|
689
|
+
self,
|
|
690
|
+
text: str,
|
|
691
|
+
dataset_name: Optional[str] = None,
|
|
692
|
+
) -> str:
|
|
693
|
+
"""
|
|
694
|
+
Ingest text/knowledge into a dataset for later semantic search.
|
|
695
|
+
|
|
696
|
+
This adds raw text to a dataset which can then be processed with process_dataset()
|
|
697
|
+
to extract entities, relationships, and create embeddings for semantic search.
|
|
698
|
+
|
|
699
|
+
Use this to add:
|
|
700
|
+
- Documentation and guides
|
|
701
|
+
- Code snippets and explanations
|
|
702
|
+
- Meeting notes and decisions
|
|
703
|
+
- Technical specifications
|
|
704
|
+
- Any textual knowledge
|
|
705
|
+
|
|
706
|
+
Args:
|
|
707
|
+
text: Text content to ingest (any length - can be multiple paragraphs)
|
|
708
|
+
dataset_name: Target dataset name (optional, uses environment dataset if not specified)
|
|
709
|
+
|
|
710
|
+
Returns:
|
|
711
|
+
Success message with dataset info
|
|
712
|
+
|
|
713
|
+
Example:
|
|
714
|
+
ingest_knowledge("Kubernetes is a container orchestration platform...")
|
|
715
|
+
ingest_knowledge("Our API uses REST...", dataset_name="api-docs")
|
|
716
|
+
"""
|
|
717
|
+
try:
|
|
718
|
+
# Use environment dataset if not specified
|
|
719
|
+
target_dataset = dataset_name or self.dataset_name
|
|
720
|
+
|
|
721
|
+
body = {
|
|
722
|
+
"text": text,
|
|
723
|
+
"dataset_name": target_dataset,
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
result = self._make_request("POST", "/api/v1/graph/knowledge", body=body)
|
|
727
|
+
|
|
728
|
+
status = result.get("status")
|
|
729
|
+
message = result.get("message", "")
|
|
730
|
+
text_length = result.get("text_length", len(text))
|
|
731
|
+
|
|
732
|
+
if status == "success":
|
|
733
|
+
return f"â Knowledge ingested successfully into dataset '{target_dataset}'\n" \
|
|
734
|
+
f" đ {text_length} characters added\n" \
|
|
735
|
+
f" âšī¸ Run process_dataset('{target_dataset}') to make it searchable"
|
|
736
|
+
else:
|
|
737
|
+
return f"â ī¸ Knowledge ingestion completed with warnings: {message}"
|
|
738
|
+
|
|
739
|
+
except Exception as e:
|
|
740
|
+
error_msg = str(e)
|
|
741
|
+
logger.error(f"Failed to ingest knowledge: {error_msg}")
|
|
742
|
+
|
|
743
|
+
if "404" in error_msg:
|
|
744
|
+
return f"Dataset '{dataset_name or self.dataset_name}' not found. Create it first with store_memory()."
|
|
745
|
+
elif "503" in error_msg:
|
|
746
|
+
return "Cognitive features are not enabled. Contact your administrator."
|
|
747
|
+
else:
|
|
748
|
+
return f"Error ingesting knowledge: {error_msg}"
|
|
749
|
+
|
|
750
|
+
def process_dataset(
|
|
751
|
+
self,
|
|
752
|
+
dataset_name: Optional[str] = None,
|
|
753
|
+
) -> str:
|
|
754
|
+
"""
|
|
755
|
+
Process a dataset to extract knowledge and create searchable embeddings.
|
|
756
|
+
|
|
757
|
+
This transforms raw text added via ingest_knowledge() into a semantic knowledge graph:
|
|
758
|
+
1. Extracts entities and concepts
|
|
759
|
+
2. Identifies relationships
|
|
760
|
+
3. Creates vector embeddings
|
|
761
|
+
4. Makes content searchable via recall_memory()
|
|
762
|
+
|
|
763
|
+
**Important**: This operation can take 10-60 seconds depending on dataset size.
|
|
764
|
+
|
|
765
|
+
Args:
|
|
766
|
+
dataset_name: Dataset to process (optional, uses environment dataset if not specified)
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
Processing status and result info
|
|
770
|
+
|
|
771
|
+
Example:
|
|
772
|
+
process_dataset()
|
|
773
|
+
process_dataset("api-docs")
|
|
774
|
+
"""
|
|
775
|
+
try:
|
|
776
|
+
# Use environment dataset if not specified
|
|
777
|
+
target_dataset = dataset_name or self.dataset_name
|
|
778
|
+
|
|
779
|
+
body = {
|
|
780
|
+
"dataset_name": target_dataset,
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
result = self._make_request("POST", "/api/v1/graph/cognify", body=body)
|
|
784
|
+
|
|
785
|
+
status = result.get("status")
|
|
786
|
+
message = result.get("message", "")
|
|
787
|
+
|
|
788
|
+
if status == "success":
|
|
789
|
+
return f"â Dataset '{target_dataset}' processed successfully\n" \
|
|
790
|
+
f" đ Knowledge graph created and embeddings generated\n" \
|
|
791
|
+
f" đ Content is now searchable with recall_memory()\n" \
|
|
792
|
+
f" âšī¸ {message}"
|
|
793
|
+
else:
|
|
794
|
+
return f"â ī¸ Processing completed with status: {status}\n" \
|
|
795
|
+
f" Message: {message}"
|
|
796
|
+
|
|
797
|
+
except Exception as e:
|
|
798
|
+
error_msg = str(e)
|
|
799
|
+
logger.error(f"Failed to process dataset: {error_msg}")
|
|
800
|
+
|
|
801
|
+
if "404" in error_msg:
|
|
802
|
+
return f"Dataset '{dataset_name or self.dataset_name}' not found."
|
|
803
|
+
elif "400" in error_msg:
|
|
804
|
+
return f"Dataset '{dataset_name or self.dataset_name}' has no data. Add content with ingest_knowledge() first."
|
|
805
|
+
elif "503" in error_msg:
|
|
806
|
+
return "Cognitive features are not enabled. Contact your administrator."
|
|
807
|
+
else:
|
|
808
|
+
return f"Error processing dataset: {error_msg}"
|