ciris-agent 1.7.7__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.
- ciris_adapters/README.md +113 -0
- ciris_adapters/__init__.py +30 -0
- ciris_adapters/ciris_covenant_metrics/README.md +144 -0
- ciris_adapters/ciris_covenant_metrics/__init__.py +36 -0
- ciris_adapters/ciris_covenant_metrics/adapter.py +249 -0
- ciris_adapters/ciris_covenant_metrics/manifest.json +152 -0
- ciris_adapters/ciris_covenant_metrics/services.py +403 -0
- ciris_adapters/ciris_hosted_tools/__init__.py +24 -0
- ciris_adapters/ciris_hosted_tools/adapter.py +169 -0
- ciris_adapters/ciris_hosted_tools/manifest.json +94 -0
- ciris_adapters/ciris_hosted_tools/services.py +744 -0
- ciris_adapters/external_data_sql/README.md +559 -0
- ciris_adapters/external_data_sql/__init__.py +43 -0
- ciris_adapters/external_data_sql/adapter.py +144 -0
- ciris_adapters/external_data_sql/configurable.py +315 -0
- ciris_adapters/external_data_sql/dialects/__init__.py +37 -0
- ciris_adapters/external_data_sql/dialects/base.py +133 -0
- ciris_adapters/external_data_sql/dialects/mysql.py +63 -0
- ciris_adapters/external_data_sql/dialects/postgresql.py +59 -0
- ciris_adapters/external_data_sql/dialects/sqlite.py +62 -0
- ciris_adapters/external_data_sql/example_config.json +88 -0
- ciris_adapters/external_data_sql/example_privacy_schema.yaml +127 -0
- ciris_adapters/external_data_sql/manifest.json +195 -0
- ciris_adapters/external_data_sql/privacy_schema_loader.py +189 -0
- ciris_adapters/external_data_sql/protocol.py +101 -0
- ciris_adapters/external_data_sql/schemas.py +146 -0
- ciris_adapters/external_data_sql/service.py +1547 -0
- ciris_adapters/external_data_sql/service_old.py +492 -0
- ciris_adapters/home_assistant/__init__.py +63 -0
- ciris_adapters/home_assistant/adapter.py +201 -0
- ciris_adapters/home_assistant/communication_service.py +347 -0
- ciris_adapters/home_assistant/configurable.py +667 -0
- ciris_adapters/home_assistant/manifest.json +203 -0
- ciris_adapters/home_assistant/schemas.py +129 -0
- ciris_adapters/home_assistant/service.py +751 -0
- ciris_adapters/home_assistant/tool_service.py +441 -0
- ciris_adapters/mcp_client/__init__.py +82 -0
- ciris_adapters/mcp_client/adapter.py +847 -0
- ciris_adapters/mcp_client/config.py +280 -0
- ciris_adapters/mcp_client/configurable.py +422 -0
- ciris_adapters/mcp_client/manifest.json +185 -0
- ciris_adapters/mcp_client/mcp_communication_service.py +393 -0
- ciris_adapters/mcp_client/mcp_tool_service.py +463 -0
- ciris_adapters/mcp_client/mcp_wise_service.py +394 -0
- ciris_adapters/mcp_client/schemas.py +149 -0
- ciris_adapters/mcp_client/security.py +592 -0
- ciris_adapters/mcp_common/__init__.py +44 -0
- ciris_adapters/mcp_common/manifest.json +25 -0
- ciris_adapters/mcp_common/protocol.py +315 -0
- ciris_adapters/mcp_common/schemas.py +225 -0
- ciris_adapters/mcp_server/__init__.py +47 -0
- ciris_adapters/mcp_server/adapter.py +581 -0
- ciris_adapters/mcp_server/config.py +260 -0
- ciris_adapters/mcp_server/configurable.py +393 -0
- ciris_adapters/mcp_server/handlers.py +663 -0
- ciris_adapters/mcp_server/manifest.json +211 -0
- ciris_adapters/mcp_server/security.py +500 -0
- ciris_adapters/mock_llm/README.md +117 -0
- ciris_adapters/mock_llm/__init__.py +21 -0
- ciris_adapters/mock_llm/adapter.py +131 -0
- ciris_adapters/mock_llm/configurable.py +237 -0
- ciris_adapters/mock_llm/manifest.json +106 -0
- ciris_adapters/mock_llm/protocol.py +37 -0
- ciris_adapters/mock_llm/responses.py +520 -0
- ciris_adapters/mock_llm/responses_action_selection.py +1041 -0
- ciris_adapters/mock_llm/responses_epistemic.py +17 -0
- ciris_adapters/mock_llm/responses_feedback.py +27 -0
- ciris_adapters/mock_llm/schemas.py +35 -0
- ciris_adapters/mock_llm/service.py +294 -0
- ciris_adapters/navigation/__init__.py +21 -0
- ciris_adapters/navigation/adapter.py +129 -0
- ciris_adapters/navigation/configurable.py +239 -0
- ciris_adapters/navigation/manifest.json +104 -0
- ciris_adapters/navigation/service.py +487 -0
- ciris_adapters/reddit/README.md +132 -0
- ciris_adapters/reddit/REDDIT_ADAPTER_ANALYSIS.md +715 -0
- ciris_adapters/reddit/REDDIT_ADAPTER_SUMMARY.txt +278 -0
- ciris_adapters/reddit/REDDIT_ANALYSIS_INDEX.md +307 -0
- ciris_adapters/reddit/REDDIT_PRODUCTION_READINESS_PLAN.md +518 -0
- ciris_adapters/reddit/__init__.py +15 -0
- ciris_adapters/reddit/adapter.py +189 -0
- ciris_adapters/reddit/configurable.py +274 -0
- ciris_adapters/reddit/error_handler.py +307 -0
- ciris_adapters/reddit/manifest.json +218 -0
- ciris_adapters/reddit/observer.py +532 -0
- ciris_adapters/reddit/protocol.py +34 -0
- ciris_adapters/reddit/schemas.py +433 -0
- ciris_adapters/reddit/service.py +1471 -0
- ciris_adapters/sample_adapter/README.md +474 -0
- ciris_adapters/sample_adapter/__init__.py +45 -0
- ciris_adapters/sample_adapter/adapter.py +208 -0
- ciris_adapters/sample_adapter/configurable.py +469 -0
- ciris_adapters/sample_adapter/manifest.json +247 -0
- ciris_adapters/sample_adapter/services.py +486 -0
- ciris_adapters/weather/__init__.py +16 -0
- ciris_adapters/weather/adapter.py +130 -0
- ciris_adapters/weather/configurable.py +240 -0
- ciris_adapters/weather/manifest.json +156 -0
- ciris_adapters/weather/service.py +600 -0
- ciris_agent-1.7.7.dist-info/METADATA +284 -0
- ciris_agent-1.7.7.dist-info/RECORD +986 -0
- ciris_agent-1.7.7.dist-info/WHEEL +5 -0
- ciris_agent-1.7.7.dist-info/entry_points.txt +15 -0
- ciris_agent-1.7.7.dist-info/licenses/LICENSE +205 -0
- ciris_agent-1.7.7.dist-info/licenses/NOTICE +82 -0
- ciris_agent-1.7.7.dist-info/top_level.txt +4 -0
- ciris_engine/__init__.py +15 -0
- ciris_engine/ciris_templates/ally.yaml +632 -0
- ciris_engine/ciris_templates/default.yaml +411 -0
- ciris_engine/ciris_templates/echo-core.yaml +629 -0
- ciris_engine/ciris_templates/echo-speculative.yaml +764 -0
- ciris_engine/ciris_templates/echo.yaml +647 -0
- ciris_engine/ciris_templates/sage.yaml +332 -0
- ciris_engine/ciris_templates/scout.yaml +338 -0
- ciris_engine/ciris_templates/test.yaml +168 -0
- ciris_engine/cli.py +42 -0
- ciris_engine/config/CIRIS_SERVICES.json +19 -0
- ciris_engine/config/MODEL_CAPABILITIES.json +419 -0
- ciris_engine/config/PRICING_DATA.json +179 -0
- ciris_engine/config/__init__.py +50 -0
- ciris_engine/config/ciris_services.py +113 -0
- ciris_engine/config/model_capabilities.py +388 -0
- ciris_engine/config/pricing_models.py +276 -0
- ciris_engine/constants.py +35 -0
- ciris_engine/data/__init__.py +1 -0
- ciris_engine/data/covenant_1.0b.txt +978 -0
- ciris_engine/gui_static/11steps.svg +107 -0
- ciris_engine/gui_static/2x-schematics.png +0 -0
- ciris_engine/gui_static/404/index.html +1 -0
- ciris_engine/gui_static/404.html +1 -0
- ciris_engine/gui_static/_next/static/0edhkwDxd5UccTsCmtaBi/_buildManifest.js +1 -0
- ciris_engine/gui_static/_next/static/0edhkwDxd5UccTsCmtaBi/_ssgManifest.js +1 -0
- ciris_engine/gui_static/_next/static/U-3xTQao7hc2wnAi-Uekm/_buildManifest.js +1 -0
- ciris_engine/gui_static/_next/static/U-3xTQao7hc2wnAi-Uekm/_ssgManifest.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/3297-60e86ba0f8a7b040.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/3835-2aad4b7f5f8e4643.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4499-99a0bc47de0b8975.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4534-af88cd4ba6e99bff.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4541-84b455f9e0dc4cfe.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4789-61412711484754bb.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/6539-c6398bc9d7018430.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/704-8e827b26cc8c2d32.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/704-fb45d630f3192c6f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8072-de4952a2e6d2b33f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8315-b91d03a3949db0af.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8386-f93a83ccbd789bd9.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/87c73c54-781a7f35148d5433.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8903-fefea3339a02d41b.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/9090-e66485adf8d9d990.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/_not-found/page-a67d9808462c23b1.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/api-keys/page-2d7ee1583bbbd02e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/api-keys/page-6a3c2bae6fe92b7b.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/consent/page-2ed3a035136bc4e8.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/consent/page-b2f5c91844a32422.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/page-25b90f89af3ea58c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/page-b65d16c94ecaf69c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/privacy/page-675b6d05c8f9184f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/privacy/page-cbee2e1c8ab52145.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/settings/page-0f44da06697cf9f0.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/settings/page-563420253577edbf.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/adapters/page-1854631018bc32be.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/agents/page-8353752c176a7c70.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/agents/page-f61a529f110a6040.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/api-demo/page-7f19b9d20d39be28.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/api-demo/page-d1063938f249b8bd.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/audit/page-321b6728b8fff0bb.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/audit/page-ebac35ca961a1277.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/billing/page-6f3dc3bd02924f8e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/billing/page-fa4a469f814c821a.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/comms/page-0d4f734269addd8f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/comms/page-79227d426050089c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/config/page-018d21d683b6e5bc.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/config/page-2aa5a5363ca2a371.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/consent/page-198373205fd316e2.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/consent/page-f2ca39e7713b13f8.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/dashboard/page-1dd5a196f643c60d.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/dashboard/page-530a04d3abbb8cda.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/docs/page-3193b06d094ab654.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/docs/page-330e996dedb87aba.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/layout-0a70f5fc460298b1.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/layout-21f2f99dd5b336e9.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/login/page-33240e6c6034a49d.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/login/page-68ffab6d54a7fdcd.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/logs/page-8a6167aecc4a475c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/memory/page-9ca8c5d0056de3ff.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/memory/page-e961226941c18f81.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/page-6fdb065a787a4974.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/page-89f87d431be6064a.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/runtime/page-2e728b9c43aa164d.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/runtime/page-c7dd033dc40a72f0.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/services/page-ae9f0bdf11d01a95.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/services/page-b10feb79ca5d75e5.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/sessions/page-13ebe7ef1c16ae11.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/sessions/page-e6c82b16d617f785.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/setup/page-0beb5f5b5a5c20fc.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/setup/page-2595e729eae30c0e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/status-dashboard/page-1037c987aecc3653.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/status-dashboard/page-2ffd147f6d3162ff.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/system/page-2c5798d58cafcd91.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/system/page-505b1ba4eceb01c3.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-auth/page-b0cad31d5cb1b2fa.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-auth/page-f3ecd7a8012df230.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-login/page-f35117fdc4105801.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-login/page-fb583a7924114906.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-sdk/page-50f116fd76935563.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-sdk/page-c37d8aa5ba623a44.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/tools/page-429aec7a707777ef.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/tools/page-5f705aad60e0c04e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/users/page-13476b8b0f3808cc.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/users/page-7e500d154ed5bba4.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/wa/page-cc4a9d8a5cb44d08.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/wa/page-ec3e429efbc79230.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/framework-9d29490f5ba089ba.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/main-1f554952e47a82c4.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/main-app-26fa8aed029082e5.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/main-app-97b0486ef6bcef25.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/pages/_app-6ce685456e616eb2.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/pages/_error-d4bce98d93fe21e7.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/webpack-fcebd240b7f8477d.js +1 -0
- ciris_engine/gui_static/_next/static/css/16b94b1fe0cc6e37.css +3 -0
- ciris_engine/gui_static/_next/static/css/77a24ceaae86deff.css +3 -0
- ciris_engine/gui_static/_next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/747892c23ea88013-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/8d697b304b401681-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/9610d9e46709d722-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/d8298875641ec7d4-s.p.woff2 +0 -0
- ciris_engine/gui_static/account/api-keys/index.html +1 -0
- ciris_engine/gui_static/account/api-keys/index.txt +27 -0
- ciris_engine/gui_static/account/consent/index.html +1 -0
- ciris_engine/gui_static/account/consent/index.txt +27 -0
- ciris_engine/gui_static/account/index.html +1 -0
- ciris_engine/gui_static/account/index.txt +27 -0
- ciris_engine/gui_static/account/privacy/index.html +1 -0
- ciris_engine/gui_static/account/privacy/index.txt +27 -0
- ciris_engine/gui_static/account/settings/index.html +1 -0
- ciris_engine/gui_static/account/settings/index.txt +27 -0
- ciris_engine/gui_static/adapters/index.html +1 -0
- ciris_engine/gui_static/adapters/index.txt +27 -0
- ciris_engine/gui_static/agents/index.html +1 -0
- ciris_engine/gui_static/agents/index.txt +27 -0
- ciris_engine/gui_static/andrew-roberts-euBRXcx57T4-unsplash.jpg +0 -0
- ciris_engine/gui_static/api-demo/index.html +1 -0
- ciris_engine/gui_static/api-demo/index.txt +27 -0
- ciris_engine/gui_static/audit/index.html +1 -0
- ciris_engine/gui_static/audit/index.txt +27 -0
- ciris_engine/gui_static/billing/index.html +1 -0
- ciris_engine/gui_static/billing/index.txt +27 -0
- ciris_engine/gui_static/blurryinfo.png +0 -0
- ciris_engine/gui_static/chip-vincent-PkQDwfl9Flc-unsplash.jpg +0 -0
- ciris_engine/gui_static/ciris-architecture.svg +338 -0
- ciris_engine/gui_static/comms/index.html +1 -0
- ciris_engine/gui_static/comms/index.txt +27 -0
- ciris_engine/gui_static/config/index.html +1 -0
- ciris_engine/gui_static/config/index.txt +27 -0
- ciris_engine/gui_static/consent/index.html +1 -0
- ciris_engine/gui_static/consent/index.txt +27 -0
- ciris_engine/gui_static/dashboard/index.html +1 -0
- ciris_engine/gui_static/dashboard/index.txt +27 -0
- ciris_engine/gui_static/docs/index.html +1 -0
- ciris_engine/gui_static/docs/index.txt +27 -0
- ciris_engine/gui_static/eric.png +0 -0
- ciris_engine/gui_static/file.svg +1 -0
- ciris_engine/gui_static/globe.svg +1 -0
- ciris_engine/gui_static/index.html +1 -0
- ciris_engine/gui_static/index.txt +27 -0
- ciris_engine/gui_static/infogfx-1@2x.png +0 -0
- ciris_engine/gui_static/infogfx-2.png +0 -0
- ciris_engine/gui_static/infogfx-dark-1.png +0 -0
- ciris_engine/gui_static/kelly-vohs-soSTXmIxTDU-unsplash.jpg +0 -0
- ciris_engine/gui_static/login/index.html +1 -0
- ciris_engine/gui_static/login/index.txt +27 -0
- ciris_engine/gui_static/logs/index.html +1 -0
- ciris_engine/gui_static/logs/index.txt +27 -0
- ciris_engine/gui_static/memory/index.html +1 -0
- ciris_engine/gui_static/memory/index.txt +27 -0
- ciris_engine/gui_static/nathan-farrish-ArcTfEoBgzs-unsplash.jpg +0 -0
- ciris_engine/gui_static/next.svg +1 -0
- ciris_engine/gui_static/overview.svg +512 -0
- ciris_engine/gui_static/overview1.svg +407 -0
- ciris_engine/gui_static/overview2.svg +370 -0
- ciris_engine/gui_static/pipeline-visualization.svg +278 -0
- ciris_engine/gui_static/privacy-policy.html +160 -0
- ciris_engine/gui_static/runtime/index.html +8 -0
- ciris_engine/gui_static/runtime/index.txt +27 -0
- ciris_engine/gui_static/services/index.html +1 -0
- ciris_engine/gui_static/services/index.txt +27 -0
- ciris_engine/gui_static/sessions/index.html +1 -0
- ciris_engine/gui_static/sessions/index.txt +27 -0
- ciris_engine/gui_static/setup/index.html +1 -0
- ciris_engine/gui_static/setup/index.txt +27 -0
- ciris_engine/gui_static/status-dashboard/index.html +1 -0
- ciris_engine/gui_static/status-dashboard/index.txt +27 -0
- ciris_engine/gui_static/system/index.html +1 -0
- ciris_engine/gui_static/system/index.txt +27 -0
- ciris_engine/gui_static/terms-of-service.html +174 -0
- ciris_engine/gui_static/test-auth/index.html +1 -0
- ciris_engine/gui_static/test-auth/index.txt +27 -0
- ciris_engine/gui_static/test-login/index.html +1 -0
- ciris_engine/gui_static/test-login/index.txt +27 -0
- ciris_engine/gui_static/test-sdk/index.html +1 -0
- ciris_engine/gui_static/test-sdk/index.txt +27 -0
- ciris_engine/gui_static/tools/index.html +1 -0
- ciris_engine/gui_static/tools/index.txt +27 -0
- ciris_engine/gui_static/users/index.html +1 -0
- ciris_engine/gui_static/users/index.txt +27 -0
- ciris_engine/gui_static/vercel.svg +1 -0
- ciris_engine/gui_static/videos/video1.mp4 +0 -0
- ciris_engine/gui_static/videos/video3.mp4 +0 -0
- ciris_engine/gui_static/wa/index.html +1 -0
- ciris_engine/gui_static/wa/index.txt +27 -0
- ciris_engine/gui_static/window.svg +1 -0
- ciris_engine/logic/__init__.py +8 -0
- ciris_engine/logic/adapters/__init__.py +74 -0
- ciris_engine/logic/adapters/api/__init__.py +5 -0
- ciris_engine/logic/adapters/api/adapter.py +1037 -0
- ciris_engine/logic/adapters/api/api_communication.py +370 -0
- ciris_engine/logic/adapters/api/api_document.py +330 -0
- ciris_engine/logic/adapters/api/api_observer.py +24 -0
- ciris_engine/logic/adapters/api/api_runtime_control.py +388 -0
- ciris_engine/logic/adapters/api/api_tools.py +299 -0
- ciris_engine/logic/adapters/api/api_vision.py +215 -0
- ciris_engine/logic/adapters/api/app.py +272 -0
- ciris_engine/logic/adapters/api/auth.py +159 -0
- ciris_engine/logic/adapters/api/config.py +101 -0
- ciris_engine/logic/adapters/api/constants.py +55 -0
- ciris_engine/logic/adapters/api/dependencies/__init__.py +1 -0
- ciris_engine/logic/adapters/api/dependencies/auth.py +260 -0
- ciris_engine/logic/adapters/api/endpoints/__init__.py +1 -0
- ciris_engine/logic/adapters/api/endpoints/emergency.py +86 -0
- ciris_engine/logic/adapters/api/middleware/__init__.py +1 -0
- ciris_engine/logic/adapters/api/middleware/rate_limiter.py +302 -0
- ciris_engine/logic/adapters/api/models.py +29 -0
- ciris_engine/logic/adapters/api/routes/__init__.py +52 -0
- ciris_engine/logic/adapters/api/routes/agent.py +1762 -0
- ciris_engine/logic/adapters/api/routes/audit.py +707 -0
- ciris_engine/logic/adapters/api/routes/auth.py +1745 -0
- ciris_engine/logic/adapters/api/routes/billing.py +895 -0
- ciris_engine/logic/adapters/api/routes/config.py +329 -0
- ciris_engine/logic/adapters/api/routes/connectors.py +534 -0
- ciris_engine/logic/adapters/api/routes/consent.py +637 -0
- ciris_engine/logic/adapters/api/routes/dsar.py +637 -0
- ciris_engine/logic/adapters/api/routes/dsar_multi_source.py +484 -0
- ciris_engine/logic/adapters/api/routes/emergency.py +302 -0
- ciris_engine/logic/adapters/api/routes/memory.py +733 -0
- ciris_engine/logic/adapters/api/routes/memory_filters.py +230 -0
- ciris_engine/logic/adapters/api/routes/memory_models.py +112 -0
- ciris_engine/logic/adapters/api/routes/memory_queries.py +236 -0
- ciris_engine/logic/adapters/api/routes/memory_query_helpers.py +394 -0
- ciris_engine/logic/adapters/api/routes/memory_visualization.py +359 -0
- ciris_engine/logic/adapters/api/routes/memory_visualization_helpers.py +110 -0
- ciris_engine/logic/adapters/api/routes/partnership.py +541 -0
- ciris_engine/logic/adapters/api/routes/setup.py +1374 -0
- ciris_engine/logic/adapters/api/routes/system.py +3049 -0
- ciris_engine/logic/adapters/api/routes/system_extensions.py +952 -0
- ciris_engine/logic/adapters/api/routes/telemetry.py +1987 -0
- ciris_engine/logic/adapters/api/routes/telemetry_converters.py +141 -0
- ciris_engine/logic/adapters/api/routes/telemetry_helpers.py +111 -0
- ciris_engine/logic/adapters/api/routes/telemetry_logs_reader.py +280 -0
- ciris_engine/logic/adapters/api/routes/telemetry_metrics.py +131 -0
- ciris_engine/logic/adapters/api/routes/telemetry_models.py +190 -0
- ciris_engine/logic/adapters/api/routes/telemetry_otlp.py +878 -0
- ciris_engine/logic/adapters/api/routes/telemetry_resource_helpers.py +191 -0
- ciris_engine/logic/adapters/api/routes/tickets.py +541 -0
- ciris_engine/logic/adapters/api/routes/tools.py +556 -0
- ciris_engine/logic/adapters/api/routes/transparency.py +281 -0
- ciris_engine/logic/adapters/api/routes/users.py +981 -0
- ciris_engine/logic/adapters/api/routes/verification.py +373 -0
- ciris_engine/logic/adapters/api/routes/wa.py +369 -0
- ciris_engine/logic/adapters/api/service_configuration.py +177 -0
- ciris_engine/logic/adapters/api/services/__init__.py +1 -0
- ciris_engine/logic/adapters/api/services/auth_service.py +1417 -0
- ciris_engine/logic/adapters/api/services/oauth_security.py +68 -0
- ciris_engine/logic/adapters/base.py +141 -0
- ciris_engine/logic/adapters/base_adapter.py +73 -0
- ciris_engine/logic/adapters/base_observer.py +1141 -0
- ciris_engine/logic/adapters/base_vision.py +312 -0
- ciris_engine/logic/adapters/cirisnode_client.py +307 -0
- ciris_engine/logic/adapters/cli/__init__.py +3 -0
- ciris_engine/logic/adapters/cli/adapter.py +207 -0
- ciris_engine/logic/adapters/cli/cli_adapter.py +902 -0
- ciris_engine/logic/adapters/cli/cli_observer.py +268 -0
- ciris_engine/logic/adapters/cli/cli_tools.py +427 -0
- ciris_engine/logic/adapters/cli/cli_wa_service.py +134 -0
- ciris_engine/logic/adapters/cli/config.py +73 -0
- ciris_engine/logic/adapters/discord/__init__.py +3 -0
- ciris_engine/logic/adapters/discord/adapter.py +783 -0
- ciris_engine/logic/adapters/discord/ciris_discord_client.py +159 -0
- ciris_engine/logic/adapters/discord/config.py +177 -0
- ciris_engine/logic/adapters/discord/constants.py +185 -0
- ciris_engine/logic/adapters/discord/discord-stubs.pyi +50 -0
- ciris_engine/logic/adapters/discord/discord_adapter.py +1584 -0
- ciris_engine/logic/adapters/discord/discord_audit.py +150 -0
- ciris_engine/logic/adapters/discord/discord_channel_manager.py +351 -0
- ciris_engine/logic/adapters/discord/discord_connection_manager.py +313 -0
- ciris_engine/logic/adapters/discord/discord_embed_formatter.py +369 -0
- ciris_engine/logic/adapters/discord/discord_error_classifier.py +302 -0
- ciris_engine/logic/adapters/discord/discord_error_handler.py +316 -0
- ciris_engine/logic/adapters/discord/discord_guidance_handler.py +460 -0
- ciris_engine/logic/adapters/discord/discord_message_handler.py +207 -0
- ciris_engine/logic/adapters/discord/discord_observer.py +670 -0
- ciris_engine/logic/adapters/discord/discord_rate_limiter.py +249 -0
- ciris_engine/logic/adapters/discord/discord_reaction_handler.py +278 -0
- ciris_engine/logic/adapters/discord/discord_tool_handler.py +465 -0
- ciris_engine/logic/adapters/discord/discord_tool_service.py +790 -0
- ciris_engine/logic/adapters/discord/discord_tools.py +90 -0
- ciris_engine/logic/adapters/discord/discord_vision_helper.py +148 -0
- ciris_engine/logic/adapters/discord/py.typed +0 -0
- ciris_engine/logic/adapters/document_parser.py +320 -0
- ciris_engine/logic/audit/__init__.py +10 -0
- ciris_engine/logic/audit/hash_chain.py +313 -0
- ciris_engine/logic/audit/signature_manager.py +352 -0
- ciris_engine/logic/audit/verifier.py +408 -0
- ciris_engine/logic/buses/__init__.py +21 -0
- ciris_engine/logic/buses/base_bus.py +178 -0
- ciris_engine/logic/buses/bus_manager.py +121 -0
- ciris_engine/logic/buses/communication_bus.py +387 -0
- ciris_engine/logic/buses/llm_bus.py +722 -0
- ciris_engine/logic/buses/memory_bus.py +577 -0
- ciris_engine/logic/buses/prohibitions.py +502 -0
- ciris_engine/logic/buses/runtime_control_bus.py +539 -0
- ciris_engine/logic/buses/tool_bus.py +482 -0
- ciris_engine/logic/buses/wise_bus.py +684 -0
- ciris_engine/logic/config/__init__.py +25 -0
- ciris_engine/logic/config/bootstrap.py +255 -0
- ciris_engine/logic/config/config_accessor.py +202 -0
- ciris_engine/logic/config/db_paths.py +194 -0
- ciris_engine/logic/config/env_utils.py +39 -0
- ciris_engine/logic/conscience/__init__.py +16 -0
- ciris_engine/logic/conscience/build_deferral_package.py +0 -0
- ciris_engine/logic/conscience/core.py +688 -0
- ciris_engine/logic/conscience/interface.py +33 -0
- ciris_engine/logic/conscience/registry.py +76 -0
- ciris_engine/logic/conscience/thought_depth_guardrail.py +231 -0
- ciris_engine/logic/conscience/updated_status_conscience.py +156 -0
- ciris_engine/logic/context/__init__.py +10 -0
- ciris_engine/logic/context/batch_context.py +550 -0
- ciris_engine/logic/context/builder.py +149 -0
- ciris_engine/logic/context/channel_resolution.py +136 -0
- ciris_engine/logic/context/secrets_snapshot.py +52 -0
- ciris_engine/logic/context/system_snapshot.py +116 -0
- ciris_engine/logic/context/system_snapshot_helpers.py +1651 -0
- ciris_engine/logic/covenant/__init__.py +33 -0
- ciris_engine/logic/covenant/executor.py +303 -0
- ciris_engine/logic/covenant/extractor.py +382 -0
- ciris_engine/logic/covenant/handler.py +241 -0
- ciris_engine/logic/covenant/verifier.py +383 -0
- ciris_engine/logic/dma/__init__.py +15 -0
- ciris_engine/logic/dma/action_selection/__init__.py +11 -0
- ciris_engine/logic/dma/action_selection/action_instruction_generator.py +444 -0
- ciris_engine/logic/dma/action_selection/context_builder.py +508 -0
- ciris_engine/logic/dma/action_selection/faculty_integration.py +193 -0
- ciris_engine/logic/dma/action_selection/special_cases.py +132 -0
- ciris_engine/logic/dma/action_selection_pdma.py +365 -0
- ciris_engine/logic/dma/base_dma.py +335 -0
- ciris_engine/logic/dma/csdma.py +239 -0
- ciris_engine/logic/dma/dma_executor.py +575 -0
- ciris_engine/logic/dma/dsdma_base.py +410 -0
- ciris_engine/logic/dma/exceptions.py +4 -0
- ciris_engine/logic/dma/factory.py +150 -0
- ciris_engine/logic/dma/pdma.py +120 -0
- ciris_engine/logic/dma/prompt_loader.py +189 -0
- ciris_engine/logic/dma/prompts/action_selection_pdma.yml +58 -0
- ciris_engine/logic/dma/prompts/csdma_common_sense.yml +28 -0
- ciris_engine/logic/dma/prompts/dsdma_base.yml +17 -0
- ciris_engine/logic/dma/prompts/pdma_ethical.yml +42 -0
- ciris_engine/logic/formatters/__init__.py +26 -0
- ciris_engine/logic/formatters/crisis_resources.py +80 -0
- ciris_engine/logic/formatters/escalation.py +21 -0
- ciris_engine/logic/formatters/identity.py +224 -0
- ciris_engine/logic/formatters/prompt_blocks.py +64 -0
- ciris_engine/logic/formatters/system_snapshot.py +193 -0
- ciris_engine/logic/formatters/user_profiles.py +108 -0
- ciris_engine/logic/handlers/__init__.py +1 -0
- ciris_engine/logic/handlers/control/__init__.py +1 -0
- ciris_engine/logic/handlers/control/defer_handler.py +195 -0
- ciris_engine/logic/handlers/control/ponder_handler.py +154 -0
- ciris_engine/logic/handlers/control/reject_handler.py +81 -0
- ciris_engine/logic/handlers/external/__init__.py +1 -0
- ciris_engine/logic/handlers/external/observe_handler.py +154 -0
- ciris_engine/logic/handlers/external/speak_handler.py +250 -0
- ciris_engine/logic/handlers/external/tool_handler.py +148 -0
- ciris_engine/logic/handlers/memory/__init__.py +1 -0
- ciris_engine/logic/handlers/memory/forget_handler.py +107 -0
- ciris_engine/logic/handlers/memory/memorize_handler.py +391 -0
- ciris_engine/logic/handlers/memory/recall_handler.py +213 -0
- ciris_engine/logic/handlers/terminal/__init__.py +1 -0
- ciris_engine/logic/handlers/terminal/task_complete_handler.py +299 -0
- ciris_engine/logic/infrastructure/__init__.py +1 -0
- ciris_engine/logic/infrastructure/handlers/__init__.py +8 -0
- ciris_engine/logic/infrastructure/handlers/action_dispatcher.py +382 -0
- ciris_engine/logic/infrastructure/handlers/base_handler.py +450 -0
- ciris_engine/logic/infrastructure/handlers/exceptions.py +2 -0
- ciris_engine/logic/infrastructure/handlers/handler_registry.py +59 -0
- ciris_engine/logic/infrastructure/handlers/helpers.py +55 -0
- ciris_engine/logic/infrastructure/step_streaming.py +149 -0
- ciris_engine/logic/infrastructure/sub_services/__init__.py +1 -0
- ciris_engine/logic/infrastructure/sub_services/identity_variance_monitor.py +1035 -0
- ciris_engine/logic/infrastructure/sub_services/pattern_analysis_loop.py +758 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_bootstrap.py +229 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_display.py +176 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_oauth.py +404 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_wizard.py +181 -0
- ciris_engine/logic/persistence/__init__.py +130 -0
- ciris_engine/logic/persistence/analytics.py +97 -0
- ciris_engine/logic/persistence/db/__init__.py +28 -0
- ciris_engine/logic/persistence/db/core.py +520 -0
- ciris_engine/logic/persistence/db/dialect.py +380 -0
- ciris_engine/logic/persistence/db/execution_helpers.py +216 -0
- ciris_engine/logic/persistence/db/migration_runner.py +191 -0
- ciris_engine/logic/persistence/db/operations.py +313 -0
- ciris_engine/logic/persistence/db/query_builder.py +232 -0
- ciris_engine/logic/persistence/db/retry.py +154 -0
- ciris_engine/logic/persistence/db/setup.py +18 -0
- ciris_engine/logic/persistence/migrations/postgres/001_initial_schema.sql +4 -0
- ciris_engine/logic/persistence/migrations/postgres/002_add_retry_status.sql +3 -0
- ciris_engine/logic/persistence/migrations/postgres/003_add_task_update_tracking.sql +8 -0
- ciris_engine/logic/persistence/migrations/postgres/004_add_occurrence_id.sql +54 -0
- ciris_engine/logic/persistence/migrations/postgres/005_add_consolidation_locks.sql +22 -0
- ciris_engine/logic/persistence/migrations/postgres/006_add_correlation_id_unique_index.sql +16 -0
- ciris_engine/logic/persistence/migrations/postgres/007_add_dsar_tickets.sql +39 -0
- ciris_engine/logic/persistence/migrations/postgres/008_rename_to_tickets_add_sop.sql +123 -0
- ciris_engine/logic/persistence/migrations/postgres/009_add_ticket_status_columns.sql +39 -0
- ciris_engine/logic/persistence/migrations/postgres/010_add_images_to_tasks.sql +5 -0
- ciris_engine/logic/persistence/migrations/sqlite/001_initial_schema.sql +357 -0
- ciris_engine/logic/persistence/migrations/sqlite/002_add_retry_status.sql +3 -0
- ciris_engine/logic/persistence/migrations/sqlite/003_add_task_update_tracking.sql +8 -0
- ciris_engine/logic/persistence/migrations/sqlite/004_add_occurrence_id.sql +45 -0
- ciris_engine/logic/persistence/migrations/sqlite/005_add_consolidation_locks.sql +22 -0
- ciris_engine/logic/persistence/migrations/sqlite/006_add_correlation_id_unique_index.sql +16 -0
- ciris_engine/logic/persistence/migrations/sqlite/007_add_dsar_tickets.sql +39 -0
- ciris_engine/logic/persistence/migrations/sqlite/008_rename_to_tickets_add_sop.sql +120 -0
- ciris_engine/logic/persistence/migrations/sqlite/009_add_ticket_status_columns.sql +129 -0
- ciris_engine/logic/persistence/migrations/sqlite/010_add_images_to_tasks.sql +17 -0
- ciris_engine/logic/persistence/models/__init__.py +141 -0
- ciris_engine/logic/persistence/models/correlations.py +881 -0
- ciris_engine/logic/persistence/models/deferral.py +68 -0
- ciris_engine/logic/persistence/models/dsar.py +286 -0
- ciris_engine/logic/persistence/models/graph.py +362 -0
- ciris_engine/logic/persistence/models/identity.py +264 -0
- ciris_engine/logic/persistence/models/queue_status.py +139 -0
- ciris_engine/logic/persistence/models/tasks.py +1043 -0
- ciris_engine/logic/persistence/models/thoughts.py +400 -0
- ciris_engine/logic/persistence/models/tickets.py +518 -0
- ciris_engine/logic/persistence/stores/__init__.py +13 -0
- ciris_engine/logic/persistence/stores/auth_helpers.py +117 -0
- ciris_engine/logic/persistence/stores/authentication_store.py +414 -0
- ciris_engine/logic/persistence/utils.py +212 -0
- ciris_engine/logic/processors/__init__.py +30 -0
- ciris_engine/logic/processors/core/__init__.py +1 -0
- ciris_engine/logic/processors/core/base_processor.py +280 -0
- ciris_engine/logic/processors/core/main_processor.py +1777 -0
- ciris_engine/logic/processors/core/step_decorators.py +1583 -0
- ciris_engine/logic/processors/core/thought_processor/__init__.py +20 -0
- ciris_engine/logic/processors/core/thought_processor/action_execution.py +49 -0
- ciris_engine/logic/processors/core/thought_processor/conscience_execution.py +382 -0
- ciris_engine/logic/processors/core/thought_processor/finalize_action.py +66 -0
- ciris_engine/logic/processors/core/thought_processor/gather_context.py +120 -0
- ciris_engine/logic/processors/core/thought_processor/main.py +920 -0
- ciris_engine/logic/processors/core/thought_processor/perform_aspdma.py +86 -0
- ciris_engine/logic/processors/core/thought_processor/perform_dmas.py +106 -0
- ciris_engine/logic/processors/core/thought_processor/recursive_processing.py +237 -0
- ciris_engine/logic/processors/core/thought_processor/round_complete.py +52 -0
- ciris_engine/logic/processors/core/thought_processor/start_round.py +64 -0
- ciris_engine/logic/processors/exceptions.py +59 -0
- ciris_engine/logic/processors/states/__init__.py +1 -0
- ciris_engine/logic/processors/states/dream_processor.py +1381 -0
- ciris_engine/logic/processors/states/play_processor.py +141 -0
- ciris_engine/logic/processors/states/shutdown_processor.py +623 -0
- ciris_engine/logic/processors/states/solitude_processor.py +305 -0
- ciris_engine/logic/processors/states/wakeup_processor.py +802 -0
- ciris_engine/logic/processors/states/work_processor.py +742 -0
- ciris_engine/logic/processors/support/__init__.py +1 -0
- ciris_engine/logic/processors/support/dma_orchestrator.py +336 -0
- ciris_engine/logic/processors/support/processing_queue.py +133 -0
- ciris_engine/logic/processors/support/shutdown_condition_evaluator.py +294 -0
- ciris_engine/logic/processors/support/state_manager.py +358 -0
- ciris_engine/logic/processors/support/task_manager.py +303 -0
- ciris_engine/logic/processors/support/thought_escalation.py +116 -0
- ciris_engine/logic/processors/support/thought_manager.py +328 -0
- ciris_engine/logic/processors/support/thought_manager_enhanced.py +105 -0
- ciris_engine/logic/registries/__init__.py +34 -0
- ciris_engine/logic/registries/base.py +653 -0
- ciris_engine/logic/registries/circuit_breaker.py +275 -0
- ciris_engine/logic/registries/typed_registries.py +184 -0
- ciris_engine/logic/runtime/__init__.py +7 -0
- ciris_engine/logic/runtime/adapter_loader.py +261 -0
- ciris_engine/logic/runtime/adapter_manager.py +1053 -0
- ciris_engine/logic/runtime/ciris_runtime.py +2342 -0
- ciris_engine/logic/runtime/ciris_runtime_helpers.py +923 -0
- ciris_engine/logic/runtime/component_builder.py +361 -0
- ciris_engine/logic/runtime/identity_manager.py +219 -0
- ciris_engine/logic/runtime/module_loader.py +207 -0
- ciris_engine/logic/runtime/prevent_sideeffects.py +30 -0
- ciris_engine/logic/runtime/runtime_interface.py +23 -0
- ciris_engine/logic/runtime/service_initializer.py +1623 -0
- ciris_engine/logic/secrets/__init__.py +30 -0
- ciris_engine/logic/secrets/encryption.py +175 -0
- ciris_engine/logic/secrets/filter.py +295 -0
- ciris_engine/logic/secrets/service.py +652 -0
- ciris_engine/logic/secrets/store.py +669 -0
- ciris_engine/logic/services/__init__.py +1 -0
- ciris_engine/logic/services/adaptation/__init__.py +3 -0
- ciris_engine/logic/services/base_graph_service.py +142 -0
- ciris_engine/logic/services/base_infrastructure_service.py +69 -0
- ciris_engine/logic/services/base_scheduled_service.py +136 -0
- ciris_engine/logic/services/base_service.py +247 -0
- ciris_engine/logic/services/governance/__init__.py +3 -0
- ciris_engine/logic/services/governance/adaptive_filter/__init__.py +14 -0
- ciris_engine/logic/services/governance/adaptive_filter/service.py +818 -0
- ciris_engine/logic/services/governance/consent/__init__.py +53 -0
- ciris_engine/logic/services/governance/consent/air.py +403 -0
- ciris_engine/logic/services/governance/consent/decay.py +324 -0
- ciris_engine/logic/services/governance/consent/dsar_automation.py +589 -0
- ciris_engine/logic/services/governance/consent/exceptions.py +106 -0
- ciris_engine/logic/services/governance/consent/metrics.py +270 -0
- ciris_engine/logic/services/governance/consent/partnership.py +533 -0
- ciris_engine/logic/services/governance/consent/service.py +1256 -0
- ciris_engine/logic/services/governance/dsar/__init__.py +29 -0
- ciris_engine/logic/services/governance/dsar/orchestrator.py +977 -0
- ciris_engine/logic/services/governance/dsar/schemas.py +141 -0
- ciris_engine/logic/services/governance/dsar/signature_service.py +283 -0
- ciris_engine/logic/services/governance/self_observation/__init__.py +20 -0
- ciris_engine/logic/services/governance/self_observation/service.py +1153 -0
- ciris_engine/logic/services/governance/visibility/__init__.py +17 -0
- ciris_engine/logic/services/governance/visibility/service.py +512 -0
- ciris_engine/logic/services/governance/wise_authority/__init__.py +15 -0
- ciris_engine/logic/services/governance/wise_authority/service.py +827 -0
- ciris_engine/logic/services/graph/__init__.py +5 -0
- ciris_engine/logic/services/graph/audit_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/audit_service/service.py +1675 -0
- ciris_engine/logic/services/graph/base.py +208 -0
- ciris_engine/logic/services/graph/config_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/config_service/service.py +372 -0
- ciris_engine/logic/services/graph/incident_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/incident_service/service.py +803 -0
- ciris_engine/logic/services/graph/memory_service.py +1120 -0
- ciris_engine/logic/services/graph/telemetry_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/telemetry_service/exceptions.py +104 -0
- ciris_engine/logic/services/graph/telemetry_service/helpers.py +1337 -0
- ciris_engine/logic/services/graph/telemetry_service/service.py +2429 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/__init__.py +17 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/aggregation_helpers.py +355 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/cleanup_helpers.py +438 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/compressor.py +260 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/__init__.py +27 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/audit.py +326 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/conversation.py +291 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/memory.py +197 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/metrics.py +251 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/task.py +257 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/trace.py +363 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/data_converter.py +545 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/date_calculation_helpers.py +193 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/db_query_helpers.py +296 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/edge_helpers.py +92 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/edge_manager.py +896 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/extensive_helpers.py +322 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/period_manager.py +152 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/profound_helpers.py +277 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/query_manager.py +812 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/service.py +1692 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/sql_builders.py +363 -0
- ciris_engine/logic/services/infrastructure/__init__.py +1 -0
- ciris_engine/logic/services/infrastructure/authentication/__init__.py +5 -0
- ciris_engine/logic/services/infrastructure/authentication/service.py +1634 -0
- ciris_engine/logic/services/infrastructure/database_maintenance/__init__.py +15 -0
- ciris_engine/logic/services/infrastructure/database_maintenance/service.py +764 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/__init__.py +7 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/ciris_billing_provider.py +755 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/service.py +409 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/simple_credit_provider.py +129 -0
- ciris_engine/logic/services/lifecycle/__init__.py +3 -0
- ciris_engine/logic/services/lifecycle/initialization/__init__.py +10 -0
- ciris_engine/logic/services/lifecycle/initialization/service.py +312 -0
- ciris_engine/logic/services/lifecycle/scheduler/__init__.py +5 -0
- ciris_engine/logic/services/lifecycle/scheduler/service.py +607 -0
- ciris_engine/logic/services/lifecycle/shutdown/__init__.py +9 -0
- ciris_engine/logic/services/lifecycle/shutdown/service.py +378 -0
- ciris_engine/logic/services/lifecycle/time/__init__.py +15 -0
- ciris_engine/logic/services/lifecycle/time/service.py +259 -0
- ciris_engine/logic/services/memory_service/__init__.py +8 -0
- ciris_engine/logic/services/mixins/__init__.py +13 -0
- ciris_engine/logic/services/mixins/example_usage.py +200 -0
- ciris_engine/logic/services/mixins/request_metrics.py +179 -0
- ciris_engine/logic/services/runtime/__init__.py +3 -0
- ciris_engine/logic/services/runtime/adapter_configuration/__init__.py +16 -0
- ciris_engine/logic/services/runtime/adapter_configuration/service.py +674 -0
- ciris_engine/logic/services/runtime/adapter_configuration/session.py +67 -0
- ciris_engine/logic/services/runtime/control_service/__init__.py +5 -0
- ciris_engine/logic/services/runtime/control_service/service.py +2269 -0
- ciris_engine/logic/services/runtime/llm_service/__init__.py +14 -0
- ciris_engine/logic/services/runtime/llm_service/pricing_calculator.py +279 -0
- ciris_engine/logic/services/runtime/llm_service/service.py +930 -0
- ciris_engine/logic/services/tools/__init__.py +5 -0
- ciris_engine/logic/services/tools/core_tool_service/__init__.py +8 -0
- ciris_engine/logic/services/tools/core_tool_service/service.py +852 -0
- ciris_engine/logic/setup/__init__.py +1 -0
- ciris_engine/logic/setup/first_run.py +250 -0
- ciris_engine/logic/setup/wizard.py +327 -0
- ciris_engine/logic/telemetry/__init__.py +46 -0
- ciris_engine/logic/telemetry/core.py +239 -0
- ciris_engine/logic/telemetry/hot_cold_config.py +133 -0
- ciris_engine/logic/telemetry/log_collector.py +190 -0
- ciris_engine/logic/telemetry/resource_monitor.py +7 -0
- ciris_engine/logic/telemetry/security.py +79 -0
- ciris_engine/logic/utils/__init__.py +18 -0
- ciris_engine/logic/utils/channel_utils.py +75 -0
- ciris_engine/logic/utils/consent/__init__.py +1 -0
- ciris_engine/logic/utils/consent/partnership_utils.py +172 -0
- ciris_engine/logic/utils/constants.py +92 -0
- ciris_engine/logic/utils/context_utils.py +145 -0
- ciris_engine/logic/utils/directory_setup.py +533 -0
- ciris_engine/logic/utils/graphql_context_provider.py +152 -0
- ciris_engine/logic/utils/identity_resolution.py +843 -0
- ciris_engine/logic/utils/incident_capture_handler.py +303 -0
- ciris_engine/logic/utils/initialization_manager.py +74 -0
- ciris_engine/logic/utils/jsondict_helpers.py +290 -0
- ciris_engine/logic/utils/log_sanitizer.py +97 -0
- ciris_engine/logic/utils/logging_config.py +151 -0
- ciris_engine/logic/utils/observability_decorators.py +544 -0
- ciris_engine/logic/utils/occurrence_utils.py +155 -0
- ciris_engine/logic/utils/path_resolution.py +281 -0
- ciris_engine/logic/utils/platform_detection.py +286 -0
- ciris_engine/logic/utils/privacy.py +266 -0
- ciris_engine/logic/utils/profile_loader.py +124 -0
- ciris_engine/logic/utils/profile_manager.py +16 -0
- ciris_engine/logic/utils/runtime_utils.py +69 -0
- ciris_engine/logic/utils/shutdown_manager.py +107 -0
- ciris_engine/logic/utils/task_formatters.py +60 -0
- ciris_engine/logic/utils/task_thought_factory.py +404 -0
- ciris_engine/logic/utils/thought_utils.py +54 -0
- ciris_engine/logic/utils/user_utils.py +70 -0
- ciris_engine/protocols/__init__.py +0 -0
- ciris_engine/protocols/adapters/__init__.py +35 -0
- ciris_engine/protocols/adapters/base.py +149 -0
- ciris_engine/protocols/adapters/configurable.py +265 -0
- ciris_engine/protocols/adapters/message.py +90 -0
- ciris_engine/protocols/audit/__init__.py +1 -0
- ciris_engine/protocols/buses/__init__.py +1 -0
- ciris_engine/protocols/config/__init__.py +1 -0
- ciris_engine/protocols/conscience/__init__.py +1 -0
- ciris_engine/protocols/consent.py +88 -0
- ciris_engine/protocols/context/__init__.py +1 -0
- ciris_engine/protocols/data/__init__.py +1 -0
- ciris_engine/protocols/dma/__init__.py +1 -0
- ciris_engine/protocols/dma/base.py +107 -0
- ciris_engine/protocols/faculties.py +34 -0
- ciris_engine/protocols/formatters/__init__.py +1 -0
- ciris_engine/protocols/handlers/__init__.py +1 -0
- ciris_engine/protocols/infrastructure/__init__.py +25 -0
- ciris_engine/protocols/infrastructure/base.py +377 -0
- ciris_engine/protocols/persistence/__init__.py +1 -0
- ciris_engine/protocols/pipeline_control.py +609 -0
- ciris_engine/protocols/processors/__init__.py +19 -0
- ciris_engine/protocols/processors/agent.py +299 -0
- ciris_engine/protocols/processors/base.py +130 -0
- ciris_engine/protocols/processors/orchestration.py +62 -0
- ciris_engine/protocols/registries/__init__.py +1 -0
- ciris_engine/protocols/runtime/__init__.py +1 -0
- ciris_engine/protocols/runtime/base.py +163 -0
- ciris_engine/protocols/secrets/__init__.py +1 -0
- ciris_engine/protocols/services/__init__.py +80 -0
- ciris_engine/protocols/services/adaptation/__init__.py +7 -0
- ciris_engine/protocols/services/adaptation/self_observation.py +265 -0
- ciris_engine/protocols/services/governance/__init__.py +20 -0
- ciris_engine/protocols/services/governance/communication.py +58 -0
- ciris_engine/protocols/services/governance/filter.py +56 -0
- ciris_engine/protocols/services/governance/visibility.py +32 -0
- ciris_engine/protocols/services/governance/wa_auth.py +192 -0
- ciris_engine/protocols/services/governance/wise_authority.py +75 -0
- ciris_engine/protocols/services/graph/__init__.py +19 -0
- ciris_engine/protocols/services/graph/audit.py +92 -0
- ciris_engine/protocols/services/graph/config.py +54 -0
- ciris_engine/protocols/services/graph/incident_management.py +103 -0
- ciris_engine/protocols/services/graph/memory.py +110 -0
- ciris_engine/protocols/services/graph/telemetry.py +51 -0
- ciris_engine/protocols/services/graph/tsdb_consolidation.py +87 -0
- ciris_engine/protocols/services/infrastructure/__init__.py +11 -0
- ciris_engine/protocols/services/infrastructure/authentication.py +159 -0
- ciris_engine/protocols/services/infrastructure/credit_gate.py +46 -0
- ciris_engine/protocols/services/infrastructure/database_maintenance.py +25 -0
- ciris_engine/protocols/services/infrastructure/resource_monitor.py +83 -0
- ciris_engine/protocols/services/lifecycle/__init__.py +13 -0
- ciris_engine/protocols/services/lifecycle/initialization.py +41 -0
- ciris_engine/protocols/services/lifecycle/scheduler.py +42 -0
- ciris_engine/protocols/services/lifecycle/shutdown.py +50 -0
- ciris_engine/protocols/services/lifecycle/time.py +31 -0
- ciris_engine/protocols/services/runtime/__init__.py +13 -0
- ciris_engine/protocols/services/runtime/llm.py +50 -0
- ciris_engine/protocols/services/runtime/runtime_control.py +193 -0
- ciris_engine/protocols/services/runtime/secrets.py +100 -0
- ciris_engine/protocols/services/runtime/tool.py +123 -0
- ciris_engine/protocols/telemetry/__init__.py +1 -0
- ciris_engine/protocols/utils/__init__.py +1 -0
- ciris_engine/schemas/__init__.py +112 -0
- ciris_engine/schemas/actions/__init__.py +37 -0
- ciris_engine/schemas/actions/parameters.py +137 -0
- ciris_engine/schemas/adapters/__init__.py +13 -0
- ciris_engine/schemas/adapters/cirisnode.py +135 -0
- ciris_engine/schemas/adapters/cli.py +97 -0
- ciris_engine/schemas/adapters/cli_tools.py +98 -0
- ciris_engine/schemas/adapters/discord.py +125 -0
- ciris_engine/schemas/adapters/graphql_core.py +144 -0
- ciris_engine/schemas/adapters/registration.py +47 -0
- ciris_engine/schemas/adapters/runtime_context.py +48 -0
- ciris_engine/schemas/adapters/tool_execution.py +45 -0
- ciris_engine/schemas/adapters/tools.py +96 -0
- ciris_engine/schemas/api/__init__.py +1 -0
- ciris_engine/schemas/api/agent.py +50 -0
- ciris_engine/schemas/api/audit.py +38 -0
- ciris_engine/schemas/api/auth.py +351 -0
- ciris_engine/schemas/api/config_security.py +242 -0
- ciris_engine/schemas/api/emergency.py +111 -0
- ciris_engine/schemas/api/responses.py +72 -0
- ciris_engine/schemas/api/runtime.py +26 -0
- ciris_engine/schemas/api/telemetry.py +109 -0
- ciris_engine/schemas/api/wa.py +90 -0
- ciris_engine/schemas/audit/__init__.py +13 -0
- ciris_engine/schemas/audit/core.py +139 -0
- ciris_engine/schemas/audit/hash_chain.py +58 -0
- ciris_engine/schemas/audit/verification.py +131 -0
- ciris_engine/schemas/buses/__init__.py +1 -0
- ciris_engine/schemas/config/__init__.py +41 -0
- ciris_engine/schemas/config/agent.py +279 -0
- ciris_engine/schemas/config/cognitive_state_behaviors.py +194 -0
- ciris_engine/schemas/config/default_dsar_sops.py +178 -0
- ciris_engine/schemas/config/essential.py +195 -0
- ciris_engine/schemas/config/tickets.py +86 -0
- ciris_engine/schemas/conscience/__init__.py +25 -0
- ciris_engine/schemas/conscience/context.py +34 -0
- ciris_engine/schemas/conscience/core.py +145 -0
- ciris_engine/schemas/conscience/results.py +24 -0
- ciris_engine/schemas/consent/__init__.py +5 -0
- ciris_engine/schemas/consent/core.py +404 -0
- ciris_engine/schemas/context/__init__.py +1 -0
- ciris_engine/schemas/covenant.py +382 -0
- ciris_engine/schemas/data/__init__.py +1 -0
- ciris_engine/schemas/dma/__init__.py +16 -0
- ciris_engine/schemas/dma/core.py +199 -0
- ciris_engine/schemas/dma/faculty.py +192 -0
- ciris_engine/schemas/dma/prompts.py +172 -0
- ciris_engine/schemas/dma/results.py +103 -0
- ciris_engine/schemas/formatters/__init__.py +1 -0
- ciris_engine/schemas/handlers/__init__.py +10 -0
- ciris_engine/schemas/handlers/context.py +119 -0
- ciris_engine/schemas/handlers/contexts.py +100 -0
- ciris_engine/schemas/handlers/core.py +167 -0
- ciris_engine/schemas/handlers/memory_schemas.py +67 -0
- ciris_engine/schemas/handlers/schemas.py +95 -0
- ciris_engine/schemas/identity.py +149 -0
- ciris_engine/schemas/infrastructure/__init__.py +1 -0
- ciris_engine/schemas/infrastructure/base.py +256 -0
- ciris_engine/schemas/infrastructure/behavioral_patterns.py +129 -0
- ciris_engine/schemas/infrastructure/feedback_loop.py +57 -0
- ciris_engine/schemas/infrastructure/identity_variance.py +141 -0
- ciris_engine/schemas/infrastructure/oauth.py +175 -0
- ciris_engine/schemas/infrastructure/wa_cli_wizard.py +54 -0
- ciris_engine/schemas/persistence/__init__.py +34 -0
- ciris_engine/schemas/persistence/core.py +140 -0
- ciris_engine/schemas/persistence/correlations.py +73 -0
- ciris_engine/schemas/persistence/postgres/__init__.py +1 -0
- ciris_engine/schemas/persistence/postgres/tables.py +280 -0
- ciris_engine/schemas/persistence/sqlite/__init__.py +1 -0
- ciris_engine/schemas/persistence/sqlite/tables.py +281 -0
- ciris_engine/schemas/platform.py +149 -0
- ciris_engine/schemas/processors/__init__.py +26 -0
- ciris_engine/schemas/processors/base.py +130 -0
- ciris_engine/schemas/processors/cognitive.py +77 -0
- ciris_engine/schemas/processors/context.py +35 -0
- ciris_engine/schemas/processors/core.py +152 -0
- ciris_engine/schemas/processors/dma.py +105 -0
- ciris_engine/schemas/processors/error.py +122 -0
- ciris_engine/schemas/processors/main.py +109 -0
- ciris_engine/schemas/processors/phase_results.py +21 -0
- ciris_engine/schemas/processors/results.py +99 -0
- ciris_engine/schemas/processors/solitude.py +79 -0
- ciris_engine/schemas/processors/state.py +202 -0
- ciris_engine/schemas/processors/state_example.py +177 -0
- ciris_engine/schemas/processors/states.py +21 -0
- ciris_engine/schemas/processors/status.py +34 -0
- ciris_engine/schemas/registries/__init__.py +1 -0
- ciris_engine/schemas/registries/base.py +66 -0
- ciris_engine/schemas/resources/__init__.py +15 -0
- ciris_engine/schemas/resources/crisis.py +315 -0
- ciris_engine/schemas/runtime/__init__.py +42 -0
- ciris_engine/schemas/runtime/adapter_management.py +186 -0
- ciris_engine/schemas/runtime/api.py +58 -0
- ciris_engine/schemas/runtime/audit.py +50 -0
- ciris_engine/schemas/runtime/bootstrap.py +33 -0
- ciris_engine/schemas/runtime/contexts.py +61 -0
- ciris_engine/schemas/runtime/core.py +161 -0
- ciris_engine/schemas/runtime/enums.py +167 -0
- ciris_engine/schemas/runtime/extended.py +232 -0
- ciris_engine/schemas/runtime/manifest.py +311 -0
- ciris_engine/schemas/runtime/memory.py +60 -0
- ciris_engine/schemas/runtime/messages.py +108 -0
- ciris_engine/schemas/runtime/models.py +156 -0
- ciris_engine/schemas/runtime/processing_context.py +43 -0
- ciris_engine/schemas/runtime/protocols_core.py +96 -0
- ciris_engine/schemas/runtime/resources.py +33 -0
- ciris_engine/schemas/runtime/system_context.py +417 -0
- ciris_engine/schemas/secrets/__init__.py +1 -0
- ciris_engine/schemas/secrets/core.py +267 -0
- ciris_engine/schemas/secrets/service.py +95 -0
- ciris_engine/schemas/services/__init__.py +33 -0
- ciris_engine/schemas/services/audit_summary_node.py +172 -0
- ciris_engine/schemas/services/authority/__init__.py +39 -0
- ciris_engine/schemas/services/authority/jwt.py +158 -0
- ciris_engine/schemas/services/authority/wa_updates.py +138 -0
- ciris_engine/schemas/services/authority/wise_authority.py +163 -0
- ciris_engine/schemas/services/authority_core.py +370 -0
- ciris_engine/schemas/services/capabilities.py +72 -0
- ciris_engine/schemas/services/community_core.py +95 -0
- ciris_engine/schemas/services/context.py +111 -0
- ciris_engine/schemas/services/conversation_summary_node.py +189 -0
- ciris_engine/schemas/services/core/__init__.py +153 -0
- ciris_engine/schemas/services/core/runtime.py +262 -0
- ciris_engine/schemas/services/core/runtime_config.py +117 -0
- ciris_engine/schemas/services/core/secrets.py +65 -0
- ciris_engine/schemas/services/correlation_node.py +179 -0
- ciris_engine/schemas/services/credit_gate.py +92 -0
- ciris_engine/schemas/services/discord_nodes.py +299 -0
- ciris_engine/schemas/services/feedback_core.py +131 -0
- ciris_engine/schemas/services/filters_core.py +270 -0
- ciris_engine/schemas/services/governance.py +26 -0
- ciris_engine/schemas/services/graph/__init__.py +26 -0
- ciris_engine/schemas/services/graph/attributes.py +254 -0
- ciris_engine/schemas/services/graph/audit.py +98 -0
- ciris_engine/schemas/services/graph/consolidation.py +338 -0
- ciris_engine/schemas/services/graph/edge_types.py +43 -0
- ciris_engine/schemas/services/graph/edges.py +88 -0
- ciris_engine/schemas/services/graph/incident.py +312 -0
- ciris_engine/schemas/services/graph/memory.py +84 -0
- ciris_engine/schemas/services/graph/node_data.py +174 -0
- ciris_engine/schemas/services/graph/query_results.py +82 -0
- ciris_engine/schemas/services/graph/telemetry.py +250 -0
- ciris_engine/schemas/services/graph/tsdb_consolidation.py +27 -0
- ciris_engine/schemas/services/graph/tsdb_models.py +107 -0
- ciris_engine/schemas/services/graph_core.py +196 -0
- ciris_engine/schemas/services/graph_typed_nodes.py +194 -0
- ciris_engine/schemas/services/infrastructure/__init__.py +1 -0
- ciris_engine/schemas/services/infrastructure/resource_monitor.py +20 -0
- ciris_engine/schemas/services/lifecycle/__init__.py +9 -0
- ciris_engine/schemas/services/lifecycle/initialization.py +33 -0
- ciris_engine/schemas/services/lifecycle/time.py +50 -0
- ciris_engine/schemas/services/llm.py +187 -0
- ciris_engine/schemas/services/metadata.py +43 -0
- ciris_engine/schemas/services/nodes.py +704 -0
- ciris_engine/schemas/services/operations.py +126 -0
- ciris_engine/schemas/services/requests.py +128 -0
- ciris_engine/schemas/services/resources_core.py +182 -0
- ciris_engine/schemas/services/runtime_control.py +1010 -0
- ciris_engine/schemas/services/shutdown.py +88 -0
- ciris_engine/schemas/services/special/__init__.py +0 -0
- ciris_engine/schemas/services/special/self_observation.py +396 -0
- ciris_engine/schemas/services/trace_summary_node.py +199 -0
- ciris_engine/schemas/services/visibility.py +98 -0
- ciris_engine/schemas/streaming/__init__.py +10 -0
- ciris_engine/schemas/streaming/reasoning_stream.py +95 -0
- ciris_engine/schemas/telemetry/__init__.py +0 -0
- ciris_engine/schemas/telemetry/collector.py +67 -0
- ciris_engine/schemas/telemetry/core.py +252 -0
- ciris_engine/schemas/telemetry/unified.py +59 -0
- ciris_engine/schemas/tools.py +72 -0
- ciris_engine/schemas/types.py +47 -0
- ciris_engine/schemas/utils/__init__.py +1 -0
- ciris_engine/schemas/utils/config_validator.py +54 -0
- ciris_engine/utils/__init__.py +1 -0
- ciris_engine/utils/serialization.py +35 -0
- ciris_sdk/__init__.py +124 -0
- ciris_sdk/auth_store.py +261 -0
- ciris_sdk/client.py +261 -0
- ciris_sdk/exceptions.py +73 -0
- ciris_sdk/model_types.py +258 -0
- ciris_sdk/models.py +354 -0
- ciris_sdk/pagination.py +214 -0
- ciris_sdk/rate_limiter.py +188 -0
- ciris_sdk/setup.py +17 -0
- ciris_sdk/telemetry_models.py +257 -0
- ciris_sdk/telemetry_responses.py +199 -0
- ciris_sdk/transport.py +177 -0
- ciris_sdk/websocket.py +400 -0
- main.py +766 -0
|
@@ -0,0 +1,1374 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Setup wizard endpoints for CIRIS first-run and reconfiguration.
|
|
3
|
+
|
|
4
|
+
Provides GUI-based setup wizard accessible at /v1/setup/*.
|
|
5
|
+
Replaces the CLI wizard for pip-installed CIRIS agents.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
import secrets
|
|
12
|
+
import time
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, List, Optional
|
|
16
|
+
|
|
17
|
+
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
from ciris_engine.logic.config.db_paths import get_audit_db_full_path
|
|
21
|
+
from ciris_engine.logic.setup.first_run import get_default_config_path, is_first_run
|
|
22
|
+
from ciris_engine.logic.setup.wizard import create_env_file, generate_encryption_key
|
|
23
|
+
from ciris_engine.schemas.api.responses import SuccessResponse
|
|
24
|
+
|
|
25
|
+
from ..dependencies.auth import AuthContext, get_auth_context
|
|
26
|
+
|
|
27
|
+
router = APIRouter(prefix="/setup", tags=["setup"])
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
# Constants
|
|
31
|
+
FIELD_DESC_DISPLAY_NAME = "Display name"
|
|
32
|
+
|
|
33
|
+
# ============================================================================
|
|
34
|
+
# Request/Response Schemas
|
|
35
|
+
# ============================================================================
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LLMProvider(BaseModel):
|
|
39
|
+
"""LLM provider configuration."""
|
|
40
|
+
|
|
41
|
+
id: str = Field(..., description="Provider ID (openai, local, other)")
|
|
42
|
+
name: str = Field(..., description=FIELD_DESC_DISPLAY_NAME)
|
|
43
|
+
description: str = Field(..., description="Provider description")
|
|
44
|
+
requires_api_key: bool = Field(..., description="Whether API key is required")
|
|
45
|
+
requires_base_url: bool = Field(..., description="Whether base URL is required")
|
|
46
|
+
requires_model: bool = Field(..., description="Whether model name is required")
|
|
47
|
+
default_base_url: Optional[str] = Field(None, description="Default base URL if applicable")
|
|
48
|
+
default_model: Optional[str] = Field(None, description="Default model name if applicable")
|
|
49
|
+
examples: List[str] = Field(default_factory=list, description="Example configurations")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AgentTemplate(BaseModel):
|
|
53
|
+
"""Agent identity template."""
|
|
54
|
+
|
|
55
|
+
id: str = Field(..., description="Template ID")
|
|
56
|
+
name: str = Field(..., description=FIELD_DESC_DISPLAY_NAME)
|
|
57
|
+
description: str = Field(..., description="Template description")
|
|
58
|
+
identity: str = Field(..., description="Agent identity/purpose")
|
|
59
|
+
example_use_cases: List[str] = Field(default_factory=list, description="Example use cases")
|
|
60
|
+
supported_sops: List[str] = Field(
|
|
61
|
+
default_factory=list, description="Supported Standard Operating Procedures (SOPs) for ticket workflows"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Book VI Stewardship (REQUIRED for all templates)
|
|
65
|
+
stewardship_tier: int = Field(
|
|
66
|
+
..., ge=1, le=5, description="Book VI Stewardship Tier (1-5, higher = more oversight)"
|
|
67
|
+
)
|
|
68
|
+
creator_id: str = Field(..., description="Creator/team identifier who signed this template")
|
|
69
|
+
signature: str = Field(..., description="Cryptographic signature verifying template authenticity")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AdapterConfig(BaseModel):
|
|
73
|
+
"""Adapter configuration."""
|
|
74
|
+
|
|
75
|
+
id: str = Field(..., description="Adapter ID (api, cli, discord, reddit)")
|
|
76
|
+
name: str = Field(..., description=FIELD_DESC_DISPLAY_NAME)
|
|
77
|
+
description: str = Field(..., description="Adapter description")
|
|
78
|
+
enabled_by_default: bool = Field(False, description="Whether enabled by default")
|
|
79
|
+
required_env_vars: List[str] = Field(default_factory=list, description="Required environment variables")
|
|
80
|
+
optional_env_vars: List[str] = Field(default_factory=list, description="Optional environment variables")
|
|
81
|
+
platform_requirements: List[str] = Field(
|
|
82
|
+
default_factory=list, description="Platform requirements (e.g., 'android_play_integrity')"
|
|
83
|
+
)
|
|
84
|
+
platform_available: bool = Field(True, description="Whether available on current platform")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class SetupStatusResponse(BaseModel):
|
|
88
|
+
"""Setup status information."""
|
|
89
|
+
|
|
90
|
+
is_first_run: bool = Field(..., description="Whether this is first run")
|
|
91
|
+
config_exists: bool = Field(..., description="Whether config file exists")
|
|
92
|
+
config_path: Optional[str] = Field(None, description="Path to config file if exists")
|
|
93
|
+
setup_required: bool = Field(..., description="Whether setup is required")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class LLMValidationRequest(BaseModel):
|
|
97
|
+
"""Request to validate LLM configuration."""
|
|
98
|
+
|
|
99
|
+
provider: str = Field(..., description="Provider ID (openai, local, other)")
|
|
100
|
+
api_key: str = Field(..., description="API key")
|
|
101
|
+
base_url: Optional[str] = Field(None, description="Base URL for OpenAI-compatible endpoints")
|
|
102
|
+
model: Optional[str] = Field(None, description="Model name")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class LLMValidationResponse(BaseModel):
|
|
106
|
+
"""Response from LLM validation."""
|
|
107
|
+
|
|
108
|
+
valid: bool = Field(..., description="Whether configuration is valid")
|
|
109
|
+
message: str = Field(..., description="Validation message")
|
|
110
|
+
error: Optional[str] = Field(None, description="Error details if validation failed")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class SetupCompleteRequest(BaseModel):
|
|
114
|
+
"""Request to complete setup."""
|
|
115
|
+
|
|
116
|
+
# Primary LLM Configuration
|
|
117
|
+
llm_provider: str = Field(..., description="LLM provider ID")
|
|
118
|
+
llm_api_key: str = Field(..., description="LLM API key")
|
|
119
|
+
llm_base_url: Optional[str] = Field(None, description="LLM base URL")
|
|
120
|
+
llm_model: Optional[str] = Field(None, description="LLM model name")
|
|
121
|
+
|
|
122
|
+
# Backup/Secondary LLM Configuration (Optional)
|
|
123
|
+
backup_llm_api_key: Optional[str] = Field(None, description="Backup LLM API key (CIRIS_OPENAI_API_KEY_2)")
|
|
124
|
+
backup_llm_base_url: Optional[str] = Field(None, description="Backup LLM base URL (CIRIS_OPENAI_API_BASE_2)")
|
|
125
|
+
backup_llm_model: Optional[str] = Field(None, description="Backup LLM model name (CIRIS_OPENAI_MODEL_NAME_2)")
|
|
126
|
+
|
|
127
|
+
# Template Selection
|
|
128
|
+
template_id: str = Field(default="general", description="Agent template ID")
|
|
129
|
+
|
|
130
|
+
# Adapter Configuration
|
|
131
|
+
enabled_adapters: List[str] = Field(default=["api"], description="List of enabled adapters")
|
|
132
|
+
adapter_config: Dict[str, Any] = Field(default_factory=dict, description="Adapter-specific configuration")
|
|
133
|
+
|
|
134
|
+
# User Configuration - Dual Password Support
|
|
135
|
+
admin_username: str = Field(default="admin", description="New user's username")
|
|
136
|
+
admin_password: Optional[str] = Field(
|
|
137
|
+
None,
|
|
138
|
+
description="New user's password (min 8 characters). Optional for OAuth users - if not provided, a random password is generated and password auth is disabled for this user.",
|
|
139
|
+
)
|
|
140
|
+
system_admin_password: Optional[str] = Field(
|
|
141
|
+
None, description="System admin password to replace default (min 8 characters, optional)"
|
|
142
|
+
)
|
|
143
|
+
# OAuth indicator - frontend sets this when user authenticated via OAuth (Google, etc.)
|
|
144
|
+
oauth_provider: Optional[str] = Field(
|
|
145
|
+
None, description="OAuth provider used for authentication (e.g., 'google'). If set, local password is optional."
|
|
146
|
+
)
|
|
147
|
+
oauth_external_id: Optional[str] = Field(
|
|
148
|
+
None, description="OAuth external ID (e.g., Google user ID). Required if oauth_provider is set."
|
|
149
|
+
)
|
|
150
|
+
oauth_email: Optional[str] = Field(None, description="OAuth email address from the provider.")
|
|
151
|
+
|
|
152
|
+
# Application Configuration
|
|
153
|
+
agent_port: int = Field(default=8080, description="Agent API port")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class SetupConfigResponse(BaseModel):
|
|
157
|
+
"""Current setup configuration."""
|
|
158
|
+
|
|
159
|
+
# Primary LLM Configuration
|
|
160
|
+
llm_provider: Optional[str] = Field(None, description="Current LLM provider")
|
|
161
|
+
llm_base_url: Optional[str] = Field(None, description="Current LLM base URL")
|
|
162
|
+
llm_model: Optional[str] = Field(None, description="Current LLM model")
|
|
163
|
+
llm_api_key_set: bool = Field(False, description="Whether API key is configured")
|
|
164
|
+
|
|
165
|
+
# Backup/Secondary LLM Configuration
|
|
166
|
+
backup_llm_base_url: Optional[str] = Field(None, description="Backup LLM base URL")
|
|
167
|
+
backup_llm_model: Optional[str] = Field(None, description="Backup LLM model")
|
|
168
|
+
backup_llm_api_key_set: bool = Field(False, description="Whether backup API key is configured")
|
|
169
|
+
|
|
170
|
+
# Template
|
|
171
|
+
template_id: Optional[str] = Field(None, description="Current template ID")
|
|
172
|
+
|
|
173
|
+
# Adapters
|
|
174
|
+
enabled_adapters: List[str] = Field(default_factory=list, description="Currently enabled adapters")
|
|
175
|
+
|
|
176
|
+
# Application
|
|
177
|
+
agent_port: int = Field(default=8080, description="Current agent port")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class CreateUserRequest(BaseModel):
|
|
181
|
+
"""Request to create initial admin user."""
|
|
182
|
+
|
|
183
|
+
username: str = Field(..., description="Admin username")
|
|
184
|
+
password: str = Field(..., description="Admin password (min 8 characters)")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class ChangePasswordRequest(BaseModel):
|
|
188
|
+
"""Request to change admin password."""
|
|
189
|
+
|
|
190
|
+
old_password: str = Field(..., description="Current password")
|
|
191
|
+
new_password: str = Field(..., description="New password (min 8 characters)")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ============================================================================
|
|
195
|
+
# Helper Functions
|
|
196
|
+
# ============================================================================
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _is_setup_allowed_without_auth() -> bool:
|
|
200
|
+
"""Check if setup endpoints should be accessible without authentication.
|
|
201
|
+
|
|
202
|
+
Returns True during first-run (no config exists).
|
|
203
|
+
Returns False after setup (config exists, requires auth).
|
|
204
|
+
"""
|
|
205
|
+
return is_first_run()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _get_llm_providers() -> List[LLMProvider]:
|
|
209
|
+
"""Get list of supported LLM providers."""
|
|
210
|
+
return [
|
|
211
|
+
LLMProvider(
|
|
212
|
+
id="openai",
|
|
213
|
+
name="OpenAI",
|
|
214
|
+
description="Official OpenAI API (GPT-4, GPT-5.2, etc.)",
|
|
215
|
+
requires_api_key=True,
|
|
216
|
+
requires_base_url=False,
|
|
217
|
+
requires_model=True,
|
|
218
|
+
default_base_url=None,
|
|
219
|
+
default_model="gpt-5.2",
|
|
220
|
+
examples=[
|
|
221
|
+
"Standard OpenAI API",
|
|
222
|
+
"Azure OpenAI Service",
|
|
223
|
+
],
|
|
224
|
+
),
|
|
225
|
+
LLMProvider(
|
|
226
|
+
id="anthropic",
|
|
227
|
+
name="Anthropic",
|
|
228
|
+
description="Claude models (Claude 3.5 Sonnet, Opus, Haiku)",
|
|
229
|
+
requires_api_key=True,
|
|
230
|
+
requires_base_url=False,
|
|
231
|
+
requires_model=True,
|
|
232
|
+
default_base_url=None,
|
|
233
|
+
default_model="claude-3-5-sonnet-20241022",
|
|
234
|
+
examples=[
|
|
235
|
+
"Claude 3.5 Sonnet",
|
|
236
|
+
"Claude 3 Opus",
|
|
237
|
+
],
|
|
238
|
+
),
|
|
239
|
+
LLMProvider(
|
|
240
|
+
id="openrouter",
|
|
241
|
+
name="OpenRouter",
|
|
242
|
+
description="Access 100+ models via OpenRouter",
|
|
243
|
+
requires_api_key=True,
|
|
244
|
+
requires_base_url=False,
|
|
245
|
+
requires_model=True,
|
|
246
|
+
default_base_url="https://openrouter.ai/api/v1",
|
|
247
|
+
default_model="meta-llama/llama-4-maverick",
|
|
248
|
+
examples=[
|
|
249
|
+
"Llama 4 Maverick",
|
|
250
|
+
"GPT-4o via OpenRouter",
|
|
251
|
+
],
|
|
252
|
+
),
|
|
253
|
+
LLMProvider(
|
|
254
|
+
id="groq",
|
|
255
|
+
name="Groq",
|
|
256
|
+
description="Ultra-fast LPU inference (Llama 3.3, Mixtral)",
|
|
257
|
+
requires_api_key=True,
|
|
258
|
+
requires_base_url=False,
|
|
259
|
+
requires_model=True,
|
|
260
|
+
default_base_url="https://api.groq.com/openai/v1",
|
|
261
|
+
default_model="llama-3.3-70b-versatile",
|
|
262
|
+
examples=[
|
|
263
|
+
"Llama 3.3 70B Versatile",
|
|
264
|
+
"Llama 3.2 90B Vision",
|
|
265
|
+
],
|
|
266
|
+
),
|
|
267
|
+
LLMProvider(
|
|
268
|
+
id="together",
|
|
269
|
+
name="Together AI",
|
|
270
|
+
description="High-performance open models",
|
|
271
|
+
requires_api_key=True,
|
|
272
|
+
requires_base_url=False,
|
|
273
|
+
requires_model=True,
|
|
274
|
+
default_base_url="https://api.together.xyz/v1",
|
|
275
|
+
default_model="meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
276
|
+
examples=[
|
|
277
|
+
"Llama 3.3 70B Turbo",
|
|
278
|
+
"Llama Vision Free",
|
|
279
|
+
],
|
|
280
|
+
),
|
|
281
|
+
LLMProvider(
|
|
282
|
+
id="google",
|
|
283
|
+
name="Google AI",
|
|
284
|
+
description="Gemini models (Gemini 2.0, 1.5 Pro)",
|
|
285
|
+
requires_api_key=True,
|
|
286
|
+
requires_base_url=False,
|
|
287
|
+
requires_model=True,
|
|
288
|
+
default_base_url="https://generativelanguage.googleapis.com/v1beta",
|
|
289
|
+
default_model="gemini-2.0-flash-exp",
|
|
290
|
+
examples=[
|
|
291
|
+
"Gemini 2.0 Flash",
|
|
292
|
+
"Gemini 1.5 Pro",
|
|
293
|
+
],
|
|
294
|
+
),
|
|
295
|
+
LLMProvider(
|
|
296
|
+
id="local",
|
|
297
|
+
name="Local LLM",
|
|
298
|
+
description="Local LLM server (Ollama, LM Studio, vLLM, etc.)",
|
|
299
|
+
requires_api_key=False,
|
|
300
|
+
requires_base_url=True,
|
|
301
|
+
requires_model=True,
|
|
302
|
+
default_base_url="http://localhost:11434",
|
|
303
|
+
default_model="llama3",
|
|
304
|
+
examples=[
|
|
305
|
+
"Ollama: http://localhost:11434",
|
|
306
|
+
"LM Studio: http://localhost:1234/v1",
|
|
307
|
+
"vLLM: http://localhost:8000/v1",
|
|
308
|
+
"LocalAI: http://localhost:8080/v1",
|
|
309
|
+
],
|
|
310
|
+
),
|
|
311
|
+
LLMProvider(
|
|
312
|
+
id="other",
|
|
313
|
+
name="Other",
|
|
314
|
+
description="Any OpenAI-compatible API endpoint",
|
|
315
|
+
requires_api_key=True,
|
|
316
|
+
requires_base_url=True,
|
|
317
|
+
requires_model=True,
|
|
318
|
+
default_base_url=None,
|
|
319
|
+
default_model=None,
|
|
320
|
+
examples=[
|
|
321
|
+
"Custom endpoints",
|
|
322
|
+
"Private deployments",
|
|
323
|
+
],
|
|
324
|
+
),
|
|
325
|
+
]
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _get_agent_templates() -> List[AgentTemplate]:
|
|
329
|
+
"""Get list of available agent templates from ciris_templates directory.
|
|
330
|
+
|
|
331
|
+
Returns template metadata for GUI display including:
|
|
332
|
+
- 4 default DSAR SOPs for GDPR compliance
|
|
333
|
+
- Book VI Stewardship information with creator signature
|
|
334
|
+
"""
|
|
335
|
+
import yaml
|
|
336
|
+
|
|
337
|
+
from ciris_engine.logic.utils.path_resolution import get_template_directory
|
|
338
|
+
from ciris_engine.schemas.config.agent import AgentTemplate as ConfigAgentTemplate
|
|
339
|
+
|
|
340
|
+
templates: List[AgentTemplate] = []
|
|
341
|
+
template_dir = get_template_directory()
|
|
342
|
+
|
|
343
|
+
logger.info(f"[SETUP TEMPLATES] Loading templates from: {template_dir}")
|
|
344
|
+
logger.info(f"[SETUP TEMPLATES] Directory exists: {template_dir.exists()}")
|
|
345
|
+
|
|
346
|
+
# Skip test.yaml and backup files
|
|
347
|
+
skip_templates = {"test.yaml", "CIRIS_TEMPLATE_GUIDE.md"}
|
|
348
|
+
|
|
349
|
+
yaml_files = list(template_dir.glob("*.yaml"))
|
|
350
|
+
logger.info(f"[SETUP TEMPLATES] Found {len(yaml_files)} .yaml files: {[f.name for f in yaml_files]}")
|
|
351
|
+
|
|
352
|
+
for template_file in yaml_files:
|
|
353
|
+
if template_file.name in skip_templates or template_file.name.endswith(".backup"):
|
|
354
|
+
logger.info(f"[SETUP TEMPLATES] Skipping: {template_file.name}")
|
|
355
|
+
continue
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
logger.info(f"[SETUP TEMPLATES] Loading: {template_file.name}")
|
|
359
|
+
with open(template_file, "r") as f:
|
|
360
|
+
template_data = yaml.safe_load(f)
|
|
361
|
+
|
|
362
|
+
# Load and validate template
|
|
363
|
+
config_template = ConfigAgentTemplate(**template_data)
|
|
364
|
+
|
|
365
|
+
# Extract SOP names from tickets config
|
|
366
|
+
supported_sops: List[str] = []
|
|
367
|
+
if config_template.tickets and config_template.tickets.sops:
|
|
368
|
+
supported_sops = [sop.sop for sop in config_template.tickets.sops]
|
|
369
|
+
|
|
370
|
+
# Extract stewardship info
|
|
371
|
+
stewardship_tier = 3 # Default medium risk
|
|
372
|
+
creator_id = "Unknown"
|
|
373
|
+
signature = "unsigned"
|
|
374
|
+
|
|
375
|
+
if config_template.stewardship:
|
|
376
|
+
stewardship_tier = config_template.stewardship.stewardship_tier
|
|
377
|
+
creator_id = config_template.stewardship.creator_ledger_entry.creator_id
|
|
378
|
+
signature = config_template.stewardship.creator_ledger_entry.signature
|
|
379
|
+
|
|
380
|
+
# Create API response template
|
|
381
|
+
template = AgentTemplate(
|
|
382
|
+
id=template_file.stem, # Use filename without .yaml as ID
|
|
383
|
+
name=config_template.name,
|
|
384
|
+
description=config_template.description,
|
|
385
|
+
identity=config_template.role_description,
|
|
386
|
+
example_use_cases=[], # Can be added to template schema later
|
|
387
|
+
supported_sops=supported_sops,
|
|
388
|
+
stewardship_tier=stewardship_tier,
|
|
389
|
+
creator_id=creator_id,
|
|
390
|
+
signature=signature,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
templates.append(template)
|
|
394
|
+
logger.info(f"[SETUP TEMPLATES] Loaded: id={template.id}, name={template.name}")
|
|
395
|
+
|
|
396
|
+
except Exception as e:
|
|
397
|
+
logger.warning(f"[SETUP TEMPLATES] Failed to load template {template_file}: {e}")
|
|
398
|
+
continue
|
|
399
|
+
|
|
400
|
+
logger.info(f"[SETUP TEMPLATES] Total templates loaded: {len(templates)}")
|
|
401
|
+
logger.info(f"[SETUP TEMPLATES] Template IDs: {[t.id for t in templates]}")
|
|
402
|
+
return templates
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _get_available_adapters() -> List[AdapterConfig]:
|
|
406
|
+
"""Get list of available adapters."""
|
|
407
|
+
return [
|
|
408
|
+
AdapterConfig(
|
|
409
|
+
id="api",
|
|
410
|
+
name="Web API",
|
|
411
|
+
description="RESTful API server with built-in web interface",
|
|
412
|
+
enabled_by_default=True,
|
|
413
|
+
required_env_vars=[],
|
|
414
|
+
optional_env_vars=["CIRIS_API_PORT", "NEXT_PUBLIC_API_BASE_URL"],
|
|
415
|
+
),
|
|
416
|
+
AdapterConfig(
|
|
417
|
+
id="cli",
|
|
418
|
+
name="Command Line",
|
|
419
|
+
description="Interactive command-line interface",
|
|
420
|
+
enabled_by_default=False,
|
|
421
|
+
required_env_vars=[],
|
|
422
|
+
optional_env_vars=[],
|
|
423
|
+
),
|
|
424
|
+
AdapterConfig(
|
|
425
|
+
id="discord",
|
|
426
|
+
name="Discord Bot",
|
|
427
|
+
description="Discord bot integration for server moderation and interaction",
|
|
428
|
+
enabled_by_default=False,
|
|
429
|
+
required_env_vars=["DISCORD_BOT_TOKEN"],
|
|
430
|
+
optional_env_vars=["DISCORD_CHANNEL_ID", "DISCORD_GUILD_ID"],
|
|
431
|
+
),
|
|
432
|
+
AdapterConfig(
|
|
433
|
+
id="reddit",
|
|
434
|
+
name="Reddit Integration",
|
|
435
|
+
description="Reddit bot for r/ciris monitoring and interaction",
|
|
436
|
+
enabled_by_default=False,
|
|
437
|
+
required_env_vars=[
|
|
438
|
+
"CIRIS_REDDIT_CLIENT_ID",
|
|
439
|
+
"CIRIS_REDDIT_CLIENT_SECRET",
|
|
440
|
+
"CIRIS_REDDIT_USERNAME",
|
|
441
|
+
"CIRIS_REDDIT_PASSWORD",
|
|
442
|
+
],
|
|
443
|
+
optional_env_vars=["CIRIS_REDDIT_SUBREDDIT"],
|
|
444
|
+
),
|
|
445
|
+
]
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _validate_api_key_for_provider(config: LLMValidationRequest) -> Optional[LLMValidationResponse]:
|
|
449
|
+
"""Validate API key based on provider type.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
LLMValidationResponse if validation fails, None if valid
|
|
453
|
+
"""
|
|
454
|
+
if config.provider == "openai":
|
|
455
|
+
if not config.api_key or config.api_key == "your_openai_api_key_here":
|
|
456
|
+
return LLMValidationResponse(
|
|
457
|
+
valid=False,
|
|
458
|
+
message="Invalid API key",
|
|
459
|
+
error="OpenAI requires a valid API key starting with 'sk-'",
|
|
460
|
+
)
|
|
461
|
+
elif config.provider != "local" and not config.api_key:
|
|
462
|
+
# Other non-local providers need API key
|
|
463
|
+
return LLMValidationResponse(valid=False, message="API key required", error="This provider requires an API key")
|
|
464
|
+
return None
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _classify_llm_connection_error(error: Exception, base_url: Optional[str]) -> LLMValidationResponse:
|
|
468
|
+
"""Classify and format LLM connection errors.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
error: The exception that occurred
|
|
472
|
+
base_url: The base URL being connected to
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
Formatted error response
|
|
476
|
+
"""
|
|
477
|
+
error_str = str(error)
|
|
478
|
+
|
|
479
|
+
if "401" in error_str or "Unauthorized" in error_str:
|
|
480
|
+
return LLMValidationResponse(
|
|
481
|
+
valid=False,
|
|
482
|
+
message="Authentication failed",
|
|
483
|
+
error="Invalid API key. Please check your credentials.",
|
|
484
|
+
)
|
|
485
|
+
if "404" in error_str or "Not Found" in error_str:
|
|
486
|
+
return LLMValidationResponse(
|
|
487
|
+
valid=False,
|
|
488
|
+
message="Endpoint not found",
|
|
489
|
+
error=f"Could not reach {base_url}. Please check the URL.",
|
|
490
|
+
)
|
|
491
|
+
if "timeout" in error_str.lower():
|
|
492
|
+
return LLMValidationResponse(
|
|
493
|
+
valid=False,
|
|
494
|
+
message="Connection timeout",
|
|
495
|
+
error="Could not connect to LLM server. Please check if it's running.",
|
|
496
|
+
)
|
|
497
|
+
return LLMValidationResponse(valid=False, message="Connection failed", error=f"Error: {error_str}")
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
async def _validate_llm_connection(config: LLMValidationRequest) -> LLMValidationResponse:
|
|
501
|
+
"""Validate LLM configuration by attempting a connection.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
config: LLM configuration to validate
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
Validation response with success/failure status
|
|
508
|
+
"""
|
|
509
|
+
logger.info("[VALIDATE_LLM] " + "=" * 50)
|
|
510
|
+
logger.info(f"[VALIDATE_LLM] Starting validation for provider: {config.provider}")
|
|
511
|
+
logger.info(
|
|
512
|
+
f"[VALIDATE_LLM] API key provided: {bool(config.api_key)} (length: {len(config.api_key) if config.api_key else 0})"
|
|
513
|
+
)
|
|
514
|
+
logger.info(
|
|
515
|
+
f"[VALIDATE_LLM] API key prefix: {config.api_key[:20] + '...' if config.api_key and len(config.api_key) > 20 else config.api_key}"
|
|
516
|
+
)
|
|
517
|
+
logger.info(f"[VALIDATE_LLM] Base URL: {config.base_url}")
|
|
518
|
+
logger.info(f"[VALIDATE_LLM] Model: {config.model}")
|
|
519
|
+
|
|
520
|
+
try:
|
|
521
|
+
# Validate API key for provider type
|
|
522
|
+
api_key_error = _validate_api_key_for_provider(config)
|
|
523
|
+
if api_key_error:
|
|
524
|
+
logger.warning(f"[VALIDATE_LLM] API key validation FAILED: {api_key_error.error}")
|
|
525
|
+
return api_key_error
|
|
526
|
+
|
|
527
|
+
logger.info("[VALIDATE_LLM] API key format validation passed")
|
|
528
|
+
|
|
529
|
+
# Import OpenAI client
|
|
530
|
+
from openai import AsyncOpenAI
|
|
531
|
+
|
|
532
|
+
# Build client configuration
|
|
533
|
+
client_kwargs: Dict[str, Any] = {"api_key": config.api_key or "local"} # Local LLMs can use placeholder
|
|
534
|
+
if config.base_url:
|
|
535
|
+
client_kwargs["base_url"] = config.base_url
|
|
536
|
+
|
|
537
|
+
logger.info(f"[VALIDATE_LLM] Creating OpenAI client with base_url: {client_kwargs.get('base_url', 'default')}")
|
|
538
|
+
|
|
539
|
+
# Create client and test connection
|
|
540
|
+
client = AsyncOpenAI(**client_kwargs)
|
|
541
|
+
|
|
542
|
+
try:
|
|
543
|
+
logger.info("[VALIDATE_LLM] Attempting to list models from API...")
|
|
544
|
+
models = await client.models.list()
|
|
545
|
+
model_count = len(models.data) if hasattr(models, "data") else 0
|
|
546
|
+
|
|
547
|
+
logger.info(f"[VALIDATE_LLM] SUCCESS! Found {model_count} models")
|
|
548
|
+
return LLMValidationResponse(
|
|
549
|
+
valid=True,
|
|
550
|
+
message=f"Connection successful! Found {model_count} available models.",
|
|
551
|
+
error=None,
|
|
552
|
+
)
|
|
553
|
+
except Exception as e:
|
|
554
|
+
logger.error(f"[VALIDATE_LLM] API call FAILED: {type(e).__name__}: {e}")
|
|
555
|
+
result = _classify_llm_connection_error(e, config.base_url)
|
|
556
|
+
logger.error(f"[VALIDATE_LLM] Classified error - valid: {result.valid}, error: {result.error}")
|
|
557
|
+
return result
|
|
558
|
+
|
|
559
|
+
except Exception as e:
|
|
560
|
+
logger.error(f"[VALIDATE_LLM] Unexpected error: {type(e).__name__}: {e}")
|
|
561
|
+
return LLMValidationResponse(valid=False, message="Validation error", error=str(e))
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
# =============================================================================
|
|
565
|
+
# SETUP USER HELPER FUNCTIONS (extracted for cognitive complexity reduction)
|
|
566
|
+
# =============================================================================
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
async def _link_oauth_identity_to_wa(auth_service: Any, setup: "SetupCompleteRequest", wa_cert: Any) -> Any:
|
|
570
|
+
"""Link OAuth identity to WA, handling existing links gracefully.
|
|
571
|
+
|
|
572
|
+
Returns the WA cert to use (may be updated if existing link found).
|
|
573
|
+
"""
|
|
574
|
+
from ciris_engine.schemas.services.authority_core import WARole
|
|
575
|
+
|
|
576
|
+
logger.debug("CIRIS_SETUP_DEBUG *** ENTERING OAuth linking block ***")
|
|
577
|
+
logger.debug( # NOSONAR - provider:external_id is not a secret, it's a provider-assigned ID
|
|
578
|
+
f"CIRIS_SETUP_DEBUG Linking OAuth identity: {setup.oauth_provider}:{setup.oauth_external_id} to WA {wa_cert.wa_id}"
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
try:
|
|
582
|
+
# First check if OAuth identity is already linked to another WA
|
|
583
|
+
existing_wa = await auth_service.get_wa_by_oauth(setup.oauth_provider, setup.oauth_external_id)
|
|
584
|
+
if existing_wa and existing_wa.wa_id != wa_cert.wa_id:
|
|
585
|
+
logger.info(f"CIRIS_SETUP_DEBUG OAuth identity already linked to WA {existing_wa.wa_id}")
|
|
586
|
+
logger.info(
|
|
587
|
+
"CIRIS_SETUP_DEBUG During first-run setup, we'll update the existing WA to be ROOT instead of creating new"
|
|
588
|
+
)
|
|
589
|
+
# Update the existing WA to have ROOT role and update its name
|
|
590
|
+
await auth_service.update_wa(
|
|
591
|
+
wa_id=existing_wa.wa_id,
|
|
592
|
+
name=setup.admin_username,
|
|
593
|
+
role=WARole.ROOT,
|
|
594
|
+
)
|
|
595
|
+
logger.info(f"CIRIS_SETUP_DEBUG ✅ Updated existing WA {existing_wa.wa_id} to ROOT role")
|
|
596
|
+
return existing_wa
|
|
597
|
+
|
|
598
|
+
# No existing link or same WA - safe to link
|
|
599
|
+
await auth_service.link_oauth_identity(
|
|
600
|
+
wa_id=wa_cert.wa_id,
|
|
601
|
+
provider=setup.oauth_provider,
|
|
602
|
+
external_id=setup.oauth_external_id,
|
|
603
|
+
account_name=setup.admin_username,
|
|
604
|
+
metadata={"email": setup.oauth_email} if setup.oauth_email else None,
|
|
605
|
+
primary=True,
|
|
606
|
+
)
|
|
607
|
+
logger.debug( # NOSONAR - provider:external_id is not a secret
|
|
608
|
+
f"CIRIS_SETUP_DEBUG ✅ SUCCESS: Linked OAuth {setup.oauth_provider}:{setup.oauth_external_id} to WA {wa_cert.wa_id}"
|
|
609
|
+
)
|
|
610
|
+
except Exception as e:
|
|
611
|
+
logger.error(f"CIRIS_SETUP_DEBUG ❌ FAILED to link OAuth identity: {e}", exc_info=True)
|
|
612
|
+
# Don't fail setup if OAuth linking fails - user can still use password
|
|
613
|
+
|
|
614
|
+
return wa_cert
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def _log_oauth_linking_skip(setup: "SetupCompleteRequest") -> None:
|
|
618
|
+
"""Log debug information when OAuth linking is skipped."""
|
|
619
|
+
logger.info("CIRIS_SETUP_DEBUG *** SKIPPING OAuth linking block - condition not met ***")
|
|
620
|
+
if not setup.oauth_provider:
|
|
621
|
+
logger.info("CIRIS_SETUP_DEBUG Reason: oauth_provider is falsy/empty")
|
|
622
|
+
if not setup.oauth_external_id:
|
|
623
|
+
logger.info("CIRIS_SETUP_DEBUG Reason: oauth_external_id is falsy/empty")
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
async def _update_system_admin_password(auth_service: Any, setup: "SetupCompleteRequest", exclude_wa_id: str) -> None:
|
|
627
|
+
"""Update the default admin password if specified."""
|
|
628
|
+
if not setup.system_admin_password:
|
|
629
|
+
return
|
|
630
|
+
|
|
631
|
+
logger.info("Updating default admin password...")
|
|
632
|
+
all_was = await auth_service.list_was(active_only=True)
|
|
633
|
+
admin_wa = next((wa for wa in all_was if wa.name == "admin" and wa.wa_id != exclude_wa_id), None)
|
|
634
|
+
|
|
635
|
+
if admin_wa:
|
|
636
|
+
admin_password_hash = auth_service.hash_password(setup.system_admin_password)
|
|
637
|
+
await auth_service.update_wa(wa_id=admin_wa.wa_id, password_hash=admin_password_hash)
|
|
638
|
+
logger.info("✅ Updated admin password")
|
|
639
|
+
else:
|
|
640
|
+
logger.warning("⚠️ Default admin WA not found")
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
async def _check_existing_oauth_wa(auth_service: Any, setup: "SetupCompleteRequest") -> tuple[Optional[Any], bool]:
|
|
644
|
+
"""Check if OAuth user already exists and update to ROOT if found.
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
Tuple of (wa_cert, was_found) where wa_cert is the WA certificate and
|
|
648
|
+
was_found indicates if an existing WA was found and updated.
|
|
649
|
+
"""
|
|
650
|
+
from ciris_engine.schemas.services.authority_core import WARole
|
|
651
|
+
|
|
652
|
+
if not (setup.oauth_provider and setup.oauth_external_id):
|
|
653
|
+
return None, False
|
|
654
|
+
|
|
655
|
+
logger.debug( # NOSONAR - provider:external_id is not a secret
|
|
656
|
+
f"CIRIS_USER_CREATE: Checking for existing OAuth user: {setup.oauth_provider}:{setup.oauth_external_id}"
|
|
657
|
+
)
|
|
658
|
+
existing_wa = await auth_service.get_wa_by_oauth(setup.oauth_provider, setup.oauth_external_id)
|
|
659
|
+
|
|
660
|
+
if not existing_wa:
|
|
661
|
+
logger.info("CIRIS_USER_CREATE: No existing WA found for OAuth user - will create new")
|
|
662
|
+
return None, False
|
|
663
|
+
|
|
664
|
+
logger.info(f"CIRIS_USER_CREATE: ✓ Found existing WA for OAuth user: {existing_wa.wa_id}")
|
|
665
|
+
logger.info(f"CIRIS_USER_CREATE: Current role: {existing_wa.role}")
|
|
666
|
+
logger.info(f"CIRIS_USER_CREATE: Current name: {existing_wa.name}")
|
|
667
|
+
|
|
668
|
+
# Update existing WA to ROOT role instead of creating new one
|
|
669
|
+
logger.info(
|
|
670
|
+
f"CIRIS_USER_CREATE: Updating existing WA {existing_wa.wa_id} to ROOT role (keeping name: {existing_wa.name})"
|
|
671
|
+
)
|
|
672
|
+
await auth_service.update_wa(wa_id=existing_wa.wa_id, role=WARole.ROOT)
|
|
673
|
+
logger.info(f"CIRIS_USER_CREATE: ✅ Updated existing OAuth WA to ROOT: {existing_wa.wa_id}")
|
|
674
|
+
|
|
675
|
+
return existing_wa, True
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
async def _create_new_wa(auth_service: Any, setup: "SetupCompleteRequest") -> Any:
|
|
679
|
+
"""Create a new WA certificate for the setup user.
|
|
680
|
+
|
|
681
|
+
Returns:
|
|
682
|
+
WA certificate for the newly created user
|
|
683
|
+
"""
|
|
684
|
+
from ciris_engine.schemas.services.authority_core import WARole
|
|
685
|
+
|
|
686
|
+
logger.info(f"CIRIS_USER_CREATE: Creating NEW user: {setup.admin_username} with role: {WARole.ROOT}")
|
|
687
|
+
|
|
688
|
+
# Use OAuth email if available, otherwise generate local email
|
|
689
|
+
user_email = setup.oauth_email or f"{setup.admin_username}@local"
|
|
690
|
+
masked_email = (user_email[:3] + "***@" + user_email.split("@")[-1]) if "@" in user_email else user_email
|
|
691
|
+
logger.debug(f"CIRIS_USER_CREATE: User email: {masked_email}") # NOSONAR - email masked
|
|
692
|
+
|
|
693
|
+
# List existing WAs before creation for debugging
|
|
694
|
+
existing_was = await auth_service.list_was(active_only=False)
|
|
695
|
+
logger.info(f"CIRIS_USER_CREATE: Existing WAs before creation: {len(existing_was)}")
|
|
696
|
+
for wa in existing_was:
|
|
697
|
+
logger.info(f"CIRIS_USER_CREATE: - {wa.wa_id}: name={wa.name}, role={wa.role}")
|
|
698
|
+
|
|
699
|
+
# Create WA certificate
|
|
700
|
+
wa_cert = await auth_service.create_wa(
|
|
701
|
+
name=setup.admin_username,
|
|
702
|
+
email=user_email,
|
|
703
|
+
scopes=["read:any", "write:any"], # ROOT gets full scopes
|
|
704
|
+
role=WARole.ROOT,
|
|
705
|
+
)
|
|
706
|
+
logger.info(f"CIRIS_USER_CREATE: ✅ Created NEW WA: {wa_cert.wa_id}")
|
|
707
|
+
|
|
708
|
+
return wa_cert
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
async def _set_password_for_wa(auth_service: Any, setup: "SetupCompleteRequest", wa_cert: Any) -> None:
|
|
712
|
+
"""Set password hash for non-OAuth users."""
|
|
713
|
+
is_oauth_setup = bool(setup.oauth_provider and setup.oauth_external_id)
|
|
714
|
+
|
|
715
|
+
if is_oauth_setup:
|
|
716
|
+
logger.info(f"CIRIS_USER_CREATE: Skipping password hash for OAuth user: {wa_cert.wa_id}")
|
|
717
|
+
return
|
|
718
|
+
|
|
719
|
+
# Hash password and update WA (admin_password is guaranteed set by validation above)
|
|
720
|
+
assert setup.admin_password is not None, "admin_password should be set by validation"
|
|
721
|
+
password_hash = auth_service.hash_password(setup.admin_password)
|
|
722
|
+
await auth_service.update_wa(wa_id=wa_cert.wa_id, password_hash=password_hash)
|
|
723
|
+
logger.info(f"CIRIS_USER_CREATE: Password hash set for WA: {wa_cert.wa_id}")
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
async def _ensure_system_wa(auth_service: Any) -> None:
|
|
727
|
+
"""Ensure system WA exists for signing system tasks."""
|
|
728
|
+
system_wa_id = await auth_service.ensure_system_wa_exists()
|
|
729
|
+
if system_wa_id:
|
|
730
|
+
logger.info(f"✅ System WA ready: {system_wa_id}")
|
|
731
|
+
else:
|
|
732
|
+
logger.warning("⚠️ Could not create system WA - deferral handling may not work")
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
async def _log_wa_list(auth_service: Any, phase: str) -> None:
|
|
736
|
+
"""Log list of WAs for debugging purposes."""
|
|
737
|
+
was = await auth_service.list_was(active_only=False)
|
|
738
|
+
logger.info(f"CIRIS_USER_CREATE: WAs {phase}: {len(was)}")
|
|
739
|
+
for wa in was:
|
|
740
|
+
logger.info(f"CIRIS_USER_CREATE: - {wa.wa_id}: name={wa.name}, role={wa.role}")
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
async def _create_setup_users(setup: SetupCompleteRequest, auth_db_path: str) -> None:
|
|
744
|
+
"""Create users immediately during setup completion.
|
|
745
|
+
|
|
746
|
+
This is called during setup completion to create users without waiting for restart.
|
|
747
|
+
Creates users directly in the database using authentication store functions.
|
|
748
|
+
|
|
749
|
+
IMPORTANT: For OAuth users, we check if they already exist and update to ROOT instead
|
|
750
|
+
of creating a duplicate WA. This prevents multiple ROOT users from being created.
|
|
751
|
+
|
|
752
|
+
Args:
|
|
753
|
+
setup: Setup configuration with user details
|
|
754
|
+
auth_db_path: Path to the audit database (from running application)
|
|
755
|
+
"""
|
|
756
|
+
from ciris_engine.logic.services.infrastructure.authentication.service import AuthenticationService
|
|
757
|
+
from ciris_engine.logic.services.lifecycle.time.service import TimeService
|
|
758
|
+
|
|
759
|
+
logger.info("=" * 70)
|
|
760
|
+
logger.info("CIRIS_USER_CREATE: _create_setup_users() called")
|
|
761
|
+
logger.info("=" * 70)
|
|
762
|
+
logger.info(f"CIRIS_USER_CREATE: auth_db_path = {auth_db_path}")
|
|
763
|
+
logger.info(f"CIRIS_USER_CREATE: admin_username = {setup.admin_username}")
|
|
764
|
+
logger.info(f"CIRIS_USER_CREATE: oauth_provider = {repr(setup.oauth_provider)}")
|
|
765
|
+
logger.info(f"CIRIS_USER_CREATE: oauth_external_id = {repr(setup.oauth_external_id)}")
|
|
766
|
+
logger.info(f"CIRIS_USER_CREATE: oauth_email = {repr(setup.oauth_email)}")
|
|
767
|
+
|
|
768
|
+
# Create temporary authentication service for user creation
|
|
769
|
+
time_service = TimeService()
|
|
770
|
+
await time_service.start()
|
|
771
|
+
|
|
772
|
+
auth_service = AuthenticationService(
|
|
773
|
+
db_path=auth_db_path, time_service=time_service, key_dir=None # Use default ~/.ciris/
|
|
774
|
+
)
|
|
775
|
+
await auth_service.start()
|
|
776
|
+
|
|
777
|
+
try:
|
|
778
|
+
# Check if OAuth user already exists and update to ROOT if found
|
|
779
|
+
wa_cert, _ = await _check_existing_oauth_wa(auth_service, setup)
|
|
780
|
+
|
|
781
|
+
# Create new WA if we didn't find an existing OAuth user
|
|
782
|
+
if wa_cert is None:
|
|
783
|
+
wa_cert = await _create_new_wa(auth_service, setup)
|
|
784
|
+
|
|
785
|
+
# Set password for non-OAuth users
|
|
786
|
+
await _set_password_for_wa(auth_service, setup, wa_cert)
|
|
787
|
+
|
|
788
|
+
# Log WAs after creation for debugging
|
|
789
|
+
await _log_wa_list(auth_service, "after setup")
|
|
790
|
+
|
|
791
|
+
# Ensure system WA exists
|
|
792
|
+
await _ensure_system_wa(auth_service)
|
|
793
|
+
|
|
794
|
+
# CIRIS_SETUP_DEBUG: Log OAuth linking decision
|
|
795
|
+
logger.debug("CIRIS_SETUP_DEBUG _create_setup_users() OAuth linking check:")
|
|
796
|
+
logger.debug(f"CIRIS_SETUP_DEBUG setup.oauth_provider = {repr(setup.oauth_provider)}")
|
|
797
|
+
logger.debug(f"CIRIS_SETUP_DEBUG setup.oauth_external_id = {repr(setup.oauth_external_id)}")
|
|
798
|
+
logger.debug(f"CIRIS_SETUP_DEBUG bool(setup.oauth_provider) = {bool(setup.oauth_provider)}")
|
|
799
|
+
logger.debug(f"CIRIS_SETUP_DEBUG bool(setup.oauth_external_id) = {bool(setup.oauth_external_id)}")
|
|
800
|
+
oauth_link_condition = bool(setup.oauth_provider) and bool(setup.oauth_external_id)
|
|
801
|
+
logger.debug(f"CIRIS_SETUP_DEBUG Condition (provider AND external_id) = {oauth_link_condition}")
|
|
802
|
+
|
|
803
|
+
# Link OAuth identity if provided - THIS IS CRITICAL for OAuth login to work
|
|
804
|
+
if setup.oauth_provider and setup.oauth_external_id:
|
|
805
|
+
wa_cert = await _link_oauth_identity_to_wa(auth_service, setup, wa_cert)
|
|
806
|
+
else:
|
|
807
|
+
_log_oauth_linking_skip(setup)
|
|
808
|
+
|
|
809
|
+
# Update default admin password if specified
|
|
810
|
+
assert wa_cert is not None, "wa_cert should be set by create_wa or existing WA lookup"
|
|
811
|
+
await _update_system_admin_password(auth_service, setup, wa_cert.wa_id)
|
|
812
|
+
|
|
813
|
+
finally:
|
|
814
|
+
await auth_service.stop()
|
|
815
|
+
await time_service.stop()
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _save_pending_users(setup: SetupCompleteRequest, config_dir: Path) -> None:
|
|
819
|
+
"""Save pending user creation info for initialization service.
|
|
820
|
+
|
|
821
|
+
Args:
|
|
822
|
+
setup: Setup configuration with user info
|
|
823
|
+
config_dir: Directory where .env file is saved
|
|
824
|
+
"""
|
|
825
|
+
pending_users_file = config_dir / ".ciris_pending_users.json"
|
|
826
|
+
|
|
827
|
+
# Prepare user creation data
|
|
828
|
+
users_data = {
|
|
829
|
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
830
|
+
"new_user": {
|
|
831
|
+
"username": setup.admin_username,
|
|
832
|
+
"password": setup.admin_password, # Will be hashed by auth service
|
|
833
|
+
"role": "ADMIN", # New user gets admin role
|
|
834
|
+
},
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
# Add system admin password update if provided
|
|
838
|
+
if setup.system_admin_password:
|
|
839
|
+
users_data["system_admin"] = {
|
|
840
|
+
"username": "admin", # Default system admin username
|
|
841
|
+
"password": setup.system_admin_password, # Will be hashed by auth service
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
# Save to JSON file
|
|
845
|
+
with open(pending_users_file, "w") as f:
|
|
846
|
+
json.dump(users_data, f, indent=2)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def _validate_setup_passwords(setup: SetupCompleteRequest, is_oauth_user: bool) -> str:
|
|
850
|
+
"""Validate and potentially generate admin password for setup.
|
|
851
|
+
|
|
852
|
+
For OAuth users without a password, generates a secure random password.
|
|
853
|
+
For non-OAuth users, validates password requirements.
|
|
854
|
+
|
|
855
|
+
Args:
|
|
856
|
+
setup: Setup configuration request
|
|
857
|
+
is_oauth_user: Whether user is authenticating via OAuth
|
|
858
|
+
|
|
859
|
+
Returns:
|
|
860
|
+
Validated or generated admin password
|
|
861
|
+
|
|
862
|
+
Raises:
|
|
863
|
+
HTTPException: If password validation fails
|
|
864
|
+
"""
|
|
865
|
+
admin_password = setup.admin_password
|
|
866
|
+
|
|
867
|
+
if not admin_password or len(admin_password) == 0:
|
|
868
|
+
if is_oauth_user:
|
|
869
|
+
# Generate a secure random password for OAuth users
|
|
870
|
+
# They won't use this password - they'll authenticate via OAuth
|
|
871
|
+
admin_password = secrets.token_urlsafe(32)
|
|
872
|
+
logger.info("[Setup Complete] Generated random password for OAuth user (password auth disabled)")
|
|
873
|
+
else:
|
|
874
|
+
# Non-OAuth users MUST provide a password
|
|
875
|
+
raise HTTPException(
|
|
876
|
+
status_code=status.HTTP_400_BAD_REQUEST, detail="New user password must be at least 8 characters"
|
|
877
|
+
)
|
|
878
|
+
elif len(admin_password) < 8:
|
|
879
|
+
# If a password was provided, it must meet minimum requirements
|
|
880
|
+
raise HTTPException(
|
|
881
|
+
status_code=status.HTTP_400_BAD_REQUEST, detail="New user password must be at least 8 characters"
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
# Validate system admin password strength if provided
|
|
885
|
+
if setup.system_admin_password and len(setup.system_admin_password) < 8:
|
|
886
|
+
raise HTTPException(
|
|
887
|
+
status_code=status.HTTP_400_BAD_REQUEST, detail="System admin password must be at least 8 characters"
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
return admin_password
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
def _save_and_reload_config(setup: SetupCompleteRequest) -> Path:
|
|
894
|
+
"""Save setup configuration to .env and reload environment variables.
|
|
895
|
+
|
|
896
|
+
Args:
|
|
897
|
+
setup: Setup configuration request
|
|
898
|
+
|
|
899
|
+
Returns:
|
|
900
|
+
Path to the saved configuration file
|
|
901
|
+
"""
|
|
902
|
+
from dotenv import load_dotenv
|
|
903
|
+
|
|
904
|
+
from ciris_engine.logic.utils.path_resolution import get_ciris_home, is_android, is_development_mode
|
|
905
|
+
|
|
906
|
+
logger.info("[Setup Complete] Path resolution:")
|
|
907
|
+
logger.info(f"[Setup Complete] is_android(): {is_android()}")
|
|
908
|
+
logger.info(f"[Setup Complete] is_development_mode(): {is_development_mode()}")
|
|
909
|
+
logger.info(f"[Setup Complete] get_ciris_home(): {get_ciris_home()}")
|
|
910
|
+
|
|
911
|
+
config_path = get_default_config_path()
|
|
912
|
+
config_dir = config_path.parent
|
|
913
|
+
logger.info(f"[Setup Complete] config_path: {config_path}")
|
|
914
|
+
logger.info(f"[Setup Complete] config_dir: {config_dir}")
|
|
915
|
+
|
|
916
|
+
# Ensure directory exists
|
|
917
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
918
|
+
logger.info(f"[Setup Complete] Directory ensured: {config_dir}")
|
|
919
|
+
|
|
920
|
+
# Save configuration
|
|
921
|
+
logger.info(f"[Setup Complete] Saving configuration to: {config_path}")
|
|
922
|
+
_save_setup_config(setup, config_path)
|
|
923
|
+
logger.info("[Setup Complete] Configuration saved successfully!")
|
|
924
|
+
|
|
925
|
+
# Verify the file was written
|
|
926
|
+
if config_path.exists():
|
|
927
|
+
file_size = config_path.stat().st_size
|
|
928
|
+
logger.info(f"[Setup Complete] Verified: .env exists ({file_size} bytes)")
|
|
929
|
+
else:
|
|
930
|
+
logger.error(f"[Setup Complete] ERROR: .env file NOT found at {config_path} after save!")
|
|
931
|
+
|
|
932
|
+
# Reload environment variables from the new .env file
|
|
933
|
+
load_dotenv(config_path, override=True)
|
|
934
|
+
logger.info(f"[Setup Complete] Reloaded environment variables from {config_path}")
|
|
935
|
+
|
|
936
|
+
# Verify key env vars were loaded
|
|
937
|
+
openai_key = os.getenv("OPENAI_API_KEY")
|
|
938
|
+
openai_base = os.getenv("OPENAI_API_BASE")
|
|
939
|
+
logger.info(f"[Setup Complete] After reload - OPENAI_API_KEY: {openai_key[:20] if openai_key else '(not set)'}...")
|
|
940
|
+
logger.info(f"[Setup Complete] After reload - OPENAI_API_BASE: {openai_base}")
|
|
941
|
+
|
|
942
|
+
return config_path
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
def _save_setup_config(setup: SetupCompleteRequest, config_path: Path) -> None:
|
|
946
|
+
"""Save setup configuration to .env file.
|
|
947
|
+
|
|
948
|
+
Args:
|
|
949
|
+
setup: Setup configuration
|
|
950
|
+
config_path: Path to save .env file
|
|
951
|
+
"""
|
|
952
|
+
# Determine LLM provider type for env file generation
|
|
953
|
+
llm_provider = setup.llm_provider
|
|
954
|
+
llm_api_key = setup.llm_api_key
|
|
955
|
+
llm_base_url = setup.llm_base_url or ""
|
|
956
|
+
llm_model = setup.llm_model or ""
|
|
957
|
+
|
|
958
|
+
# Create .env file using existing wizard logic
|
|
959
|
+
create_env_file(
|
|
960
|
+
save_path=config_path,
|
|
961
|
+
llm_provider=llm_provider,
|
|
962
|
+
llm_api_key=llm_api_key,
|
|
963
|
+
llm_base_url=llm_base_url,
|
|
964
|
+
llm_model=llm_model,
|
|
965
|
+
agent_port=setup.agent_port,
|
|
966
|
+
)
|
|
967
|
+
|
|
968
|
+
# Append template and adapter configuration
|
|
969
|
+
with open(config_path, "a") as f:
|
|
970
|
+
# Template selection
|
|
971
|
+
f.write("\n# Agent Template\n")
|
|
972
|
+
f.write(f"CIRIS_TEMPLATE={setup.template_id}\n")
|
|
973
|
+
|
|
974
|
+
# Adapter configuration
|
|
975
|
+
f.write("\n# Enabled Adapters\n")
|
|
976
|
+
adapters_str = ",".join(setup.enabled_adapters)
|
|
977
|
+
f.write(f"CIRIS_ADAPTER={adapters_str}\n")
|
|
978
|
+
|
|
979
|
+
# Adapter-specific environment variables
|
|
980
|
+
if setup.adapter_config:
|
|
981
|
+
f.write("\n# Adapter-Specific Configuration\n")
|
|
982
|
+
for key, value in setup.adapter_config.items():
|
|
983
|
+
f.write(f"{key}={value}\n")
|
|
984
|
+
|
|
985
|
+
# Backup/Secondary LLM Configuration (Optional)
|
|
986
|
+
if setup.backup_llm_api_key:
|
|
987
|
+
f.write("\n# Backup/Secondary LLM Configuration\n")
|
|
988
|
+
f.write(f'CIRIS_OPENAI_API_KEY_2="{setup.backup_llm_api_key}"\n')
|
|
989
|
+
if setup.backup_llm_base_url:
|
|
990
|
+
f.write(f'CIRIS_OPENAI_API_BASE_2="{setup.backup_llm_base_url}"\n')
|
|
991
|
+
if setup.backup_llm_model:
|
|
992
|
+
f.write(f'CIRIS_OPENAI_MODEL_NAME_2="{setup.backup_llm_model}"\n')
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
# ============================================================================
|
|
996
|
+
# Endpoints
|
|
997
|
+
# ============================================================================
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
@router.get("/status", response_model=SuccessResponse[SetupStatusResponse])
|
|
1001
|
+
async def get_setup_status() -> SuccessResponse[SetupStatusResponse]:
|
|
1002
|
+
"""Check setup status.
|
|
1003
|
+
|
|
1004
|
+
Returns information about whether setup is required.
|
|
1005
|
+
This endpoint is always accessible without authentication.
|
|
1006
|
+
"""
|
|
1007
|
+
first_run = is_first_run()
|
|
1008
|
+
config_path = get_default_config_path()
|
|
1009
|
+
config_exists = config_path.exists()
|
|
1010
|
+
|
|
1011
|
+
status = SetupStatusResponse(
|
|
1012
|
+
is_first_run=first_run,
|
|
1013
|
+
config_exists=config_exists,
|
|
1014
|
+
config_path=str(config_path) if config_exists else None,
|
|
1015
|
+
setup_required=first_run,
|
|
1016
|
+
)
|
|
1017
|
+
|
|
1018
|
+
return SuccessResponse(data=status)
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
@router.get("/providers", response_model=SuccessResponse[List[LLMProvider]])
|
|
1022
|
+
async def list_providers() -> SuccessResponse[List[LLMProvider]]:
|
|
1023
|
+
"""List available LLM providers.
|
|
1024
|
+
|
|
1025
|
+
Returns configuration templates for supported LLM providers.
|
|
1026
|
+
This endpoint is always accessible without authentication.
|
|
1027
|
+
"""
|
|
1028
|
+
providers = _get_llm_providers()
|
|
1029
|
+
return SuccessResponse(data=providers)
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
@router.get("/templates", response_model=SuccessResponse[List[AgentTemplate]])
|
|
1033
|
+
async def list_templates() -> SuccessResponse[List[AgentTemplate]]:
|
|
1034
|
+
"""List available agent templates.
|
|
1035
|
+
|
|
1036
|
+
Returns pre-configured agent identity templates.
|
|
1037
|
+
This endpoint is always accessible without authentication.
|
|
1038
|
+
"""
|
|
1039
|
+
templates = _get_agent_templates()
|
|
1040
|
+
return SuccessResponse(data=templates)
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
@router.get("/adapters", response_model=SuccessResponse[List[AdapterConfig]])
|
|
1044
|
+
async def list_adapters() -> SuccessResponse[List[AdapterConfig]]:
|
|
1045
|
+
"""List available adapters.
|
|
1046
|
+
|
|
1047
|
+
Returns configuration for available communication adapters.
|
|
1048
|
+
This endpoint is always accessible without authentication.
|
|
1049
|
+
"""
|
|
1050
|
+
adapters = _get_available_adapters()
|
|
1051
|
+
return SuccessResponse(data=adapters)
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
@router.get("/models")
|
|
1055
|
+
async def get_model_capabilities_endpoint() -> SuccessResponse[Dict[str, Any]]:
|
|
1056
|
+
"""Get CIRIS-compatible LLM model capabilities.
|
|
1057
|
+
|
|
1058
|
+
Returns the on-device model capabilities database for BYOK model selection.
|
|
1059
|
+
Used by the wizard's Advanced settings to show compatible models per provider.
|
|
1060
|
+
This endpoint is always accessible without authentication.
|
|
1061
|
+
|
|
1062
|
+
Returns model info including:
|
|
1063
|
+
- CIRIS compatibility requirements (128K+ context, tool use, vision)
|
|
1064
|
+
- Per-provider model listings with capability flags
|
|
1065
|
+
- Tiers (default, fast, fallback, premium)
|
|
1066
|
+
- Recommendations and rejection reasons
|
|
1067
|
+
"""
|
|
1068
|
+
from ciris_engine.config import get_model_capabilities
|
|
1069
|
+
|
|
1070
|
+
try:
|
|
1071
|
+
config = get_model_capabilities()
|
|
1072
|
+
|
|
1073
|
+
# Convert to dict for JSON response
|
|
1074
|
+
return SuccessResponse(
|
|
1075
|
+
data={
|
|
1076
|
+
"version": config.version,
|
|
1077
|
+
"last_updated": config.last_updated.isoformat(),
|
|
1078
|
+
"ciris_requirements": config.ciris_requirements.model_dump(),
|
|
1079
|
+
"providers": {
|
|
1080
|
+
provider_id: {
|
|
1081
|
+
"display_name": provider.display_name,
|
|
1082
|
+
"api_base": provider.api_base,
|
|
1083
|
+
"models": {model_id: model.model_dump() for model_id, model in provider.models.items()},
|
|
1084
|
+
}
|
|
1085
|
+
for provider_id, provider in config.providers.items()
|
|
1086
|
+
},
|
|
1087
|
+
"tiers": {tier_id: tier.model_dump() for tier_id, tier in config.tiers.items()},
|
|
1088
|
+
"rejected_models": {model_id: model.model_dump() for model_id, model in config.rejected_models.items()},
|
|
1089
|
+
}
|
|
1090
|
+
)
|
|
1091
|
+
except Exception as e:
|
|
1092
|
+
logger.error(f"Failed to load model capabilities: {e}")
|
|
1093
|
+
raise HTTPException(
|
|
1094
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
1095
|
+
detail=f"Failed to load model capabilities: {str(e)}",
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
@router.get("/models/{provider_id}")
|
|
1100
|
+
async def get_provider_models(provider_id: str) -> SuccessResponse[Dict[str, Any]]:
|
|
1101
|
+
"""Get CIRIS-compatible models for a specific provider.
|
|
1102
|
+
|
|
1103
|
+
Returns models for the given provider with compatibility information.
|
|
1104
|
+
Used by the wizard to populate model dropdown after provider selection.
|
|
1105
|
+
"""
|
|
1106
|
+
from ciris_engine.config import get_model_capabilities
|
|
1107
|
+
|
|
1108
|
+
try:
|
|
1109
|
+
config = get_model_capabilities()
|
|
1110
|
+
|
|
1111
|
+
if provider_id not in config.providers:
|
|
1112
|
+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Provider '{provider_id}' not found")
|
|
1113
|
+
|
|
1114
|
+
provider = config.providers[provider_id]
|
|
1115
|
+
compatible_models = []
|
|
1116
|
+
incompatible_models = []
|
|
1117
|
+
|
|
1118
|
+
for model_id, model in provider.models.items():
|
|
1119
|
+
model_data = {
|
|
1120
|
+
"id": model_id,
|
|
1121
|
+
**model.model_dump(),
|
|
1122
|
+
}
|
|
1123
|
+
if model.ciris_compatible:
|
|
1124
|
+
compatible_models.append(model_data)
|
|
1125
|
+
else:
|
|
1126
|
+
incompatible_models.append(model_data)
|
|
1127
|
+
|
|
1128
|
+
# Sort: recommended first, then by display name
|
|
1129
|
+
compatible_models.sort(key=lambda m: (not m.get("ciris_recommended", False), m["display_name"]))
|
|
1130
|
+
|
|
1131
|
+
return SuccessResponse(
|
|
1132
|
+
data={
|
|
1133
|
+
"provider_id": provider_id,
|
|
1134
|
+
"display_name": provider.display_name,
|
|
1135
|
+
"api_base": provider.api_base,
|
|
1136
|
+
"compatible_models": compatible_models,
|
|
1137
|
+
"incompatible_models": incompatible_models,
|
|
1138
|
+
"ciris_requirements": config.ciris_requirements.model_dump(),
|
|
1139
|
+
}
|
|
1140
|
+
)
|
|
1141
|
+
except HTTPException:
|
|
1142
|
+
raise
|
|
1143
|
+
except Exception as e:
|
|
1144
|
+
logger.error(f"Failed to get provider models: {e}")
|
|
1145
|
+
raise HTTPException(
|
|
1146
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
1147
|
+
detail=f"Failed to get provider models: {str(e)}",
|
|
1148
|
+
)
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
@router.post("/validate-llm", response_model=SuccessResponse[LLMValidationResponse])
|
|
1152
|
+
async def validate_llm(config: LLMValidationRequest) -> SuccessResponse[LLMValidationResponse]:
|
|
1153
|
+
"""Validate LLM configuration.
|
|
1154
|
+
|
|
1155
|
+
Tests the provided LLM configuration by attempting a connection.
|
|
1156
|
+
This endpoint is always accessible without authentication during first-run.
|
|
1157
|
+
"""
|
|
1158
|
+
validation_result = await _validate_llm_connection(config)
|
|
1159
|
+
return SuccessResponse(data=validation_result)
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
@router.post("/complete", response_model=SuccessResponse[Dict[str, str]])
|
|
1163
|
+
async def complete_setup(setup: SetupCompleteRequest, request: Request) -> SuccessResponse[Dict[str, str]]:
|
|
1164
|
+
"""Complete initial setup.
|
|
1165
|
+
|
|
1166
|
+
Saves configuration and creates initial admin user.
|
|
1167
|
+
Only accessible during first-run (no authentication required).
|
|
1168
|
+
After setup, authentication is required for reconfiguration.
|
|
1169
|
+
"""
|
|
1170
|
+
# CIRIS_SETUP_DEBUG: Comprehensive logging for OAuth identity linking
|
|
1171
|
+
logger.info("CIRIS_SETUP_DEBUG " + "=" * 60)
|
|
1172
|
+
logger.info("CIRIS_SETUP_DEBUG complete_setup() endpoint called")
|
|
1173
|
+
logger.info("CIRIS_SETUP_DEBUG " + "=" * 60)
|
|
1174
|
+
|
|
1175
|
+
# Log ALL OAuth-related fields received from frontend
|
|
1176
|
+
logger.info("CIRIS_SETUP_DEBUG OAuth fields received from frontend:")
|
|
1177
|
+
logger.info(f"CIRIS_SETUP_DEBUG oauth_provider = {repr(setup.oauth_provider)}")
|
|
1178
|
+
logger.info(f"CIRIS_SETUP_DEBUG oauth_external_id = {repr(setup.oauth_external_id)}")
|
|
1179
|
+
logger.info(f"CIRIS_SETUP_DEBUG oauth_email = {repr(setup.oauth_email)}")
|
|
1180
|
+
|
|
1181
|
+
# Check truthiness explicitly
|
|
1182
|
+
logger.debug("CIRIS_SETUP_DEBUG Truthiness checks:")
|
|
1183
|
+
logger.debug(f"CIRIS_SETUP_DEBUG bool(oauth_provider) = {bool(setup.oauth_provider)}")
|
|
1184
|
+
logger.debug(f"CIRIS_SETUP_DEBUG bool(oauth_external_id) = {bool(setup.oauth_external_id)}")
|
|
1185
|
+
logger.debug(f"CIRIS_SETUP_DEBUG oauth_external_id is None = {setup.oauth_external_id is None}")
|
|
1186
|
+
logger.debug(f"CIRIS_SETUP_DEBUG oauth_external_id == '' = {setup.oauth_external_id == ''}")
|
|
1187
|
+
|
|
1188
|
+
# The critical check that determines OAuth linking
|
|
1189
|
+
will_link_oauth = bool(setup.oauth_provider) and bool(setup.oauth_external_id)
|
|
1190
|
+
logger.debug(
|
|
1191
|
+
f"CIRIS_SETUP_DEBUG CRITICAL: Will OAuth linking happen? = {will_link_oauth}"
|
|
1192
|
+
) # NOSONAR - boolean status only
|
|
1193
|
+
if not will_link_oauth:
|
|
1194
|
+
if not setup.oauth_provider:
|
|
1195
|
+
logger.debug("CIRIS_SETUP_DEBUG Reason: oauth_provider is falsy")
|
|
1196
|
+
if not setup.oauth_external_id:
|
|
1197
|
+
logger.debug("CIRIS_SETUP_DEBUG Reason: oauth_external_id is falsy")
|
|
1198
|
+
|
|
1199
|
+
# Log other setup fields
|
|
1200
|
+
logger.debug("CIRIS_SETUP_DEBUG Other setup fields:")
|
|
1201
|
+
logger.debug(f"CIRIS_SETUP_DEBUG admin_username = {setup.admin_username}")
|
|
1202
|
+
logger.debug(
|
|
1203
|
+
f"CIRIS_SETUP_DEBUG admin_password set = {bool(setup.admin_password)}"
|
|
1204
|
+
) # NOSONAR - boolean only, not password
|
|
1205
|
+
logger.debug(
|
|
1206
|
+
f"CIRIS_SETUP_DEBUG system_admin_password set = {bool(setup.system_admin_password)}"
|
|
1207
|
+
) # NOSONAR - boolean only
|
|
1208
|
+
logger.debug(f"CIRIS_SETUP_DEBUG llm_provider = {setup.llm_provider}")
|
|
1209
|
+
logger.debug(f"CIRIS_SETUP_DEBUG template_id = {setup.template_id}")
|
|
1210
|
+
|
|
1211
|
+
# Only allow during first-run
|
|
1212
|
+
if not is_first_run():
|
|
1213
|
+
raise HTTPException(
|
|
1214
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
1215
|
+
detail="Setup already completed. Use PUT /v1/setup/config to update configuration.",
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
# Determine if this is an OAuth user (password is optional for OAuth users)
|
|
1219
|
+
is_oauth_user = bool(setup.oauth_provider)
|
|
1220
|
+
logger.debug(
|
|
1221
|
+
f"CIRIS_SETUP_DEBUG is_oauth_user (for password validation) = {is_oauth_user}"
|
|
1222
|
+
) # NOSONAR - boolean only
|
|
1223
|
+
|
|
1224
|
+
# Validate passwords and potentially generate for OAuth users
|
|
1225
|
+
setup.admin_password = _validate_setup_passwords(setup, is_oauth_user)
|
|
1226
|
+
|
|
1227
|
+
try:
|
|
1228
|
+
# Save configuration and reload environment variables
|
|
1229
|
+
config_path = _save_and_reload_config(setup)
|
|
1230
|
+
|
|
1231
|
+
# Get runtime and database path from the running application
|
|
1232
|
+
runtime = getattr(request.app.state, "runtime", None)
|
|
1233
|
+
if not runtime:
|
|
1234
|
+
raise HTTPException(
|
|
1235
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
1236
|
+
detail="Runtime not available - cannot complete setup",
|
|
1237
|
+
)
|
|
1238
|
+
|
|
1239
|
+
# Get audit database path using same resolution as AuthenticationService
|
|
1240
|
+
# This handles both SQLite and PostgreSQL (adds _auth suffix to database name)
|
|
1241
|
+
auth_db_path = get_audit_db_full_path(runtime.essential_config)
|
|
1242
|
+
logger.info(f"Using runtime audit database: {auth_db_path}")
|
|
1243
|
+
|
|
1244
|
+
# Create users immediately (don't wait for restart)
|
|
1245
|
+
await _create_setup_users(setup, auth_db_path)
|
|
1246
|
+
|
|
1247
|
+
# Reload user cache in APIAuthService to pick up newly created users
|
|
1248
|
+
auth_service = getattr(request.app.state, "auth_service", None)
|
|
1249
|
+
if auth_service:
|
|
1250
|
+
logger.info("Reloading user cache after setup user creation...")
|
|
1251
|
+
await auth_service.reload_users_from_db()
|
|
1252
|
+
logger.info("✅ User cache reloaded - new users now visible to authentication")
|
|
1253
|
+
|
|
1254
|
+
# Build next steps message
|
|
1255
|
+
next_steps = "Configuration completed. The agent is now starting. You can log in immediately."
|
|
1256
|
+
if setup.system_admin_password:
|
|
1257
|
+
next_steps += " Both user passwords have been configured."
|
|
1258
|
+
|
|
1259
|
+
# Resume initialization from first-run mode to start agent processor
|
|
1260
|
+
logger.info("Setup complete - resuming initialization to start agent processor")
|
|
1261
|
+
# Schedule resume in background to allow response to be sent first
|
|
1262
|
+
import asyncio
|
|
1263
|
+
|
|
1264
|
+
# Set resume flag AND timestamp BEFORE scheduling task to prevent SmartStartup from killing us
|
|
1265
|
+
# This flag blocks local-shutdown requests during the resume sequence
|
|
1266
|
+
# The timestamp enables timeout detection for stuck resume scenarios
|
|
1267
|
+
runtime._resume_in_progress = True
|
|
1268
|
+
runtime._resume_started_at = time.time()
|
|
1269
|
+
logger.info(f"[Setup] Set _resume_in_progress=True, _resume_started_at={runtime._resume_started_at:.3f}")
|
|
1270
|
+
|
|
1271
|
+
async def _resume_runtime() -> None:
|
|
1272
|
+
await asyncio.sleep(0.5) # Brief delay to ensure response is sent
|
|
1273
|
+
try:
|
|
1274
|
+
await runtime.resume_from_first_run()
|
|
1275
|
+
logger.info("✅ Successfully resumed from first-run mode - agent processor running")
|
|
1276
|
+
except Exception as e:
|
|
1277
|
+
logger.error(f"Failed to resume from first-run: {e}", exc_info=True)
|
|
1278
|
+
# Clear the flag and timestamp so shutdown can proceed
|
|
1279
|
+
runtime._resume_in_progress = False
|
|
1280
|
+
runtime._resume_started_at = None
|
|
1281
|
+
logger.info("[Setup] Cleared _resume_in_progress due to error")
|
|
1282
|
+
# If resume fails, fall back to restart
|
|
1283
|
+
runtime.request_shutdown("Resume failed - restarting to apply configuration")
|
|
1284
|
+
|
|
1285
|
+
# Store task to prevent garbage collection and log task creation
|
|
1286
|
+
resume_task = asyncio.create_task(_resume_runtime())
|
|
1287
|
+
logger.info(f"Scheduled background resume task: {resume_task.get_name()}")
|
|
1288
|
+
|
|
1289
|
+
return SuccessResponse(
|
|
1290
|
+
data={
|
|
1291
|
+
"status": "completed",
|
|
1292
|
+
"message": "Setup completed successfully. Starting agent processor...",
|
|
1293
|
+
"config_path": str(config_path),
|
|
1294
|
+
"username": setup.admin_username,
|
|
1295
|
+
"next_steps": next_steps,
|
|
1296
|
+
}
|
|
1297
|
+
)
|
|
1298
|
+
|
|
1299
|
+
except Exception as e:
|
|
1300
|
+
logger.error(f"Setup completion failed: {e}", exc_info=True)
|
|
1301
|
+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
@router.get("/config", response_model=SuccessResponse[SetupConfigResponse])
|
|
1305
|
+
async def get_current_config(request: Request) -> SuccessResponse[SetupConfigResponse]:
|
|
1306
|
+
"""Get current configuration.
|
|
1307
|
+
|
|
1308
|
+
Returns current setup configuration for editing.
|
|
1309
|
+
Requires authentication if setup is already completed.
|
|
1310
|
+
"""
|
|
1311
|
+
# If not first-run, require authentication
|
|
1312
|
+
if not _is_setup_allowed_without_auth():
|
|
1313
|
+
# Manually get auth context from request
|
|
1314
|
+
try:
|
|
1315
|
+
from ..dependencies.auth import get_auth_context
|
|
1316
|
+
|
|
1317
|
+
auth = await get_auth_context(request)
|
|
1318
|
+
if auth is None:
|
|
1319
|
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")
|
|
1320
|
+
except HTTPException:
|
|
1321
|
+
raise
|
|
1322
|
+
except Exception:
|
|
1323
|
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")
|
|
1324
|
+
|
|
1325
|
+
# Read current config from environment
|
|
1326
|
+
config = SetupConfigResponse(
|
|
1327
|
+
llm_provider="openai" if os.getenv("OPENAI_API_BASE") is None else "other",
|
|
1328
|
+
llm_base_url=os.getenv("OPENAI_API_BASE"),
|
|
1329
|
+
llm_model=os.getenv("OPENAI_MODEL"),
|
|
1330
|
+
llm_api_key_set=bool(os.getenv("OPENAI_API_KEY")),
|
|
1331
|
+
backup_llm_base_url=os.getenv("CIRIS_OPENAI_API_BASE_2"),
|
|
1332
|
+
backup_llm_model=os.getenv("CIRIS_OPENAI_MODEL_NAME_2"),
|
|
1333
|
+
backup_llm_api_key_set=bool(os.getenv("CIRIS_OPENAI_API_KEY_2")),
|
|
1334
|
+
template_id=os.getenv("CIRIS_TEMPLATE", "general"),
|
|
1335
|
+
enabled_adapters=os.getenv("CIRIS_ADAPTER", "api").split(","),
|
|
1336
|
+
agent_port=int(os.getenv("CIRIS_API_PORT", "8080")),
|
|
1337
|
+
)
|
|
1338
|
+
|
|
1339
|
+
return SuccessResponse(data=config)
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
@router.put("/config", response_model=SuccessResponse[Dict[str, str]])
|
|
1343
|
+
async def update_config(
|
|
1344
|
+
setup: SetupCompleteRequest, auth: AuthContext = Depends(get_auth_context)
|
|
1345
|
+
) -> SuccessResponse[Dict[str, str]]:
|
|
1346
|
+
"""Update configuration.
|
|
1347
|
+
|
|
1348
|
+
Updates setup configuration after initial setup.
|
|
1349
|
+
Requires admin authentication.
|
|
1350
|
+
"""
|
|
1351
|
+
# Check for admin role
|
|
1352
|
+
from ciris_engine.schemas.api.auth import UserRole
|
|
1353
|
+
|
|
1354
|
+
if auth.role.level < UserRole.ADMIN.level:
|
|
1355
|
+
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin role required")
|
|
1356
|
+
|
|
1357
|
+
try:
|
|
1358
|
+
# Get config path
|
|
1359
|
+
config_path = get_default_config_path()
|
|
1360
|
+
|
|
1361
|
+
# Save updated configuration
|
|
1362
|
+
_save_setup_config(setup, config_path)
|
|
1363
|
+
|
|
1364
|
+
return SuccessResponse(
|
|
1365
|
+
data={
|
|
1366
|
+
"status": "updated",
|
|
1367
|
+
"message": "Configuration updated successfully",
|
|
1368
|
+
"config_path": str(config_path),
|
|
1369
|
+
"next_steps": "Restart the agent to apply changes",
|
|
1370
|
+
}
|
|
1371
|
+
)
|
|
1372
|
+
|
|
1373
|
+
except Exception as e:
|
|
1374
|
+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
|