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,930 @@
|
|
|
1
|
+
"""OpenAI Compatible LLM Service with Circuit Breaker Integration."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import time
|
|
8
|
+
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Type, cast
|
|
9
|
+
|
|
10
|
+
import instructor
|
|
11
|
+
from openai import (
|
|
12
|
+
APIConnectionError,
|
|
13
|
+
APIStatusError,
|
|
14
|
+
AsyncOpenAI,
|
|
15
|
+
AuthenticationError,
|
|
16
|
+
InternalServerError,
|
|
17
|
+
RateLimitError,
|
|
18
|
+
)
|
|
19
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
20
|
+
|
|
21
|
+
from ciris_engine.logic.registries.circuit_breaker import CircuitBreaker, CircuitBreakerConfig, CircuitBreakerError
|
|
22
|
+
from ciris_engine.logic.services.base_service import BaseService
|
|
23
|
+
from ciris_engine.protocols.services import LLMService as LLMServiceProtocol
|
|
24
|
+
from ciris_engine.protocols.services.graph.telemetry import TelemetryServiceProtocol
|
|
25
|
+
from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
|
|
26
|
+
from ciris_engine.protocols.services.runtime.llm import MessageDict
|
|
27
|
+
from ciris_engine.schemas.runtime.enums import ServiceType
|
|
28
|
+
from ciris_engine.schemas.runtime.protocols_core import LLMStatus, LLMUsageStatistics
|
|
29
|
+
from ciris_engine.schemas.runtime.resources import ResourceUsage
|
|
30
|
+
from ciris_engine.schemas.services.capabilities import LLMCapabilities
|
|
31
|
+
from ciris_engine.schemas.services.core import ServiceCapabilities
|
|
32
|
+
from ciris_engine.schemas.services.llm import ExtractedJSONData, JSONExtractionResult
|
|
33
|
+
|
|
34
|
+
from .pricing_calculator import LLMPricingCalculator
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Configuration class for OpenAI-compatible LLM services
|
|
38
|
+
class OpenAIConfig(BaseModel):
|
|
39
|
+
api_key: str = Field(default="")
|
|
40
|
+
model_name: str = Field(default="gpt-4o-mini")
|
|
41
|
+
base_url: Optional[str] = Field(default=None)
|
|
42
|
+
instructor_mode: str = Field(default="JSON")
|
|
43
|
+
max_retries: int = Field(default=3)
|
|
44
|
+
timeout_seconds: int = Field(default=5)
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(protected_namespaces=())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
logger = logging.getLogger(__name__)
|
|
50
|
+
|
|
51
|
+
# Type for structured call functions that can be retried
|
|
52
|
+
StructuredCallFunc = Callable[
|
|
53
|
+
[List[MessageDict], Type[BaseModel], int, float], Awaitable[Tuple[BaseModel, ResourceUsage]]
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class OpenAICompatibleClient(BaseService, LLMServiceProtocol):
|
|
58
|
+
"""Client for interacting with OpenAI-compatible APIs with circuit breaker protection."""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
*, # Force keyword-only arguments
|
|
63
|
+
config: Optional[OpenAIConfig] = None,
|
|
64
|
+
telemetry_service: Optional[TelemetryServiceProtocol] = None,
|
|
65
|
+
time_service: Optional[TimeServiceProtocol] = None,
|
|
66
|
+
service_name: Optional[str] = None,
|
|
67
|
+
version: str = "1.0.0",
|
|
68
|
+
) -> None:
|
|
69
|
+
# Set telemetry_service before calling super().__init__
|
|
70
|
+
# because _register_dependencies is called in the base constructor
|
|
71
|
+
self.telemetry_service = telemetry_service
|
|
72
|
+
|
|
73
|
+
# Initialize config BEFORE calling super().__init__
|
|
74
|
+
# This ensures openai_config exists when _check_dependencies is called
|
|
75
|
+
if config is None:
|
|
76
|
+
# Use default config - should be injected
|
|
77
|
+
self.openai_config = OpenAIConfig()
|
|
78
|
+
else:
|
|
79
|
+
self.openai_config = config
|
|
80
|
+
|
|
81
|
+
# Initialize circuit breaker BEFORE calling super().__init__
|
|
82
|
+
circuit_config = CircuitBreakerConfig(
|
|
83
|
+
failure_threshold=5, # Open after 5 consecutive failures
|
|
84
|
+
recovery_timeout=60.0, # Wait 60 seconds before testing recovery
|
|
85
|
+
success_threshold=2, # Close after 2 successful calls
|
|
86
|
+
timeout_duration=5.0, # 5 second API timeout
|
|
87
|
+
)
|
|
88
|
+
self.circuit_breaker = CircuitBreaker("llm_service", circuit_config)
|
|
89
|
+
|
|
90
|
+
# Initialize base service
|
|
91
|
+
super().__init__(time_service=time_service, service_name=service_name or "llm_service", version=version)
|
|
92
|
+
|
|
93
|
+
# CRITICAL: Check if we're in mock LLM mode
|
|
94
|
+
import os
|
|
95
|
+
import sys
|
|
96
|
+
|
|
97
|
+
if os.environ.get("MOCK_LLM") or "--mock-llm" in " ".join(sys.argv):
|
|
98
|
+
raise RuntimeError(
|
|
99
|
+
"CRITICAL BUG: OpenAICompatibleClient is being initialized while mock LLM is enabled!\n"
|
|
100
|
+
"This should never happen - the mock LLM module should prevent this initialization.\n"
|
|
101
|
+
"Stack trace will show where this is being called from."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Initialize retry configuration
|
|
105
|
+
self.max_retries = min(getattr(self.openai_config, "max_retries", 3), 3)
|
|
106
|
+
self.base_delay = 1.0
|
|
107
|
+
self.max_delay = 30.0
|
|
108
|
+
self.retryable_exceptions = (APIConnectionError, RateLimitError)
|
|
109
|
+
# Note: We can't check for instructor.exceptions.InstructorRetryException at import time
|
|
110
|
+
# because it might not exist. We'll check it at runtime instead.
|
|
111
|
+
self.non_retryable_exceptions = (APIStatusError,)
|
|
112
|
+
|
|
113
|
+
api_key = self.openai_config.api_key
|
|
114
|
+
base_url = self.openai_config.base_url
|
|
115
|
+
model_name = self.openai_config.model_name or "gpt-4o-mini"
|
|
116
|
+
|
|
117
|
+
# Require API key - no automatic fallback to mock
|
|
118
|
+
if not api_key:
|
|
119
|
+
raise RuntimeError("No OpenAI API key found. Please set OPENAI_API_KEY environment variable.")
|
|
120
|
+
|
|
121
|
+
# Initialize OpenAI client
|
|
122
|
+
self.model_name = model_name
|
|
123
|
+
timeout = self.openai_config.timeout_seconds # Use the configured timeout value
|
|
124
|
+
max_retries = 0 # Disable OpenAI client retries - we handle our own
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
self.client = AsyncOpenAI(api_key=api_key, base_url=base_url, timeout=timeout, max_retries=max_retries)
|
|
128
|
+
|
|
129
|
+
instructor_mode = getattr(self.openai_config, "instructor_mode", "json").lower()
|
|
130
|
+
mode_map = {
|
|
131
|
+
"json": instructor.Mode.JSON,
|
|
132
|
+
"tools": instructor.Mode.TOOLS,
|
|
133
|
+
"md_json": instructor.Mode.MD_JSON,
|
|
134
|
+
}
|
|
135
|
+
selected_mode = mode_map.get(instructor_mode, instructor.Mode.JSON)
|
|
136
|
+
self.instruct_client = instructor.from_openai(self.client, mode=selected_mode)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
raise RuntimeError(f"Failed to initialize OpenAI client: {e}")
|
|
139
|
+
|
|
140
|
+
# Metrics tracking (no caching - we never cache LLM responses)
|
|
141
|
+
self._response_times: List[float] = [] # List of response times in ms
|
|
142
|
+
self._max_response_history = 100 # Keep last 100 response times
|
|
143
|
+
self._total_api_calls = 0
|
|
144
|
+
self._successful_api_calls = 0
|
|
145
|
+
|
|
146
|
+
# LLM-specific metrics tracking for v1.4.3 telemetry
|
|
147
|
+
self._total_requests = 0
|
|
148
|
+
self._total_input_tokens = 0
|
|
149
|
+
self._total_output_tokens = 0
|
|
150
|
+
self._total_cost_cents = 0.0
|
|
151
|
+
self._total_errors = 0
|
|
152
|
+
|
|
153
|
+
# Initialize pricing calculator for accurate cost and impact calculation
|
|
154
|
+
self.pricing_calculator = LLMPricingCalculator()
|
|
155
|
+
|
|
156
|
+
# Required BaseService abstract methods
|
|
157
|
+
|
|
158
|
+
def get_service_type(self) -> ServiceType:
|
|
159
|
+
"""Get the service type enum value."""
|
|
160
|
+
return ServiceType.LLM
|
|
161
|
+
|
|
162
|
+
def _get_actions(self) -> List[str]:
|
|
163
|
+
"""Get list of actions this service provides."""
|
|
164
|
+
return [LLMCapabilities.CALL_LLM_STRUCTURED.value]
|
|
165
|
+
|
|
166
|
+
def _check_dependencies(self) -> bool:
|
|
167
|
+
"""Check if all required dependencies are available."""
|
|
168
|
+
# LLM service requires API key and circuit breaker to be functional
|
|
169
|
+
has_api_key = bool(self.openai_config.api_key)
|
|
170
|
+
circuit_breaker_ready = self.circuit_breaker is not None
|
|
171
|
+
return has_api_key and circuit_breaker_ready
|
|
172
|
+
|
|
173
|
+
# Override optional BaseService methods
|
|
174
|
+
|
|
175
|
+
def _register_dependencies(self) -> None:
|
|
176
|
+
"""Register service dependencies."""
|
|
177
|
+
super()._register_dependencies()
|
|
178
|
+
if self.telemetry_service:
|
|
179
|
+
self._dependencies.add("TelemetryService")
|
|
180
|
+
|
|
181
|
+
async def _on_start(self) -> None:
|
|
182
|
+
"""Custom startup logic for LLM service."""
|
|
183
|
+
logger.info(f"OpenAI Compatible LLM Service started with model: {self.model_name}")
|
|
184
|
+
logger.info(f"Circuit breaker initialized: {self.circuit_breaker.get_stats()}")
|
|
185
|
+
|
|
186
|
+
async def _on_stop(self) -> None:
|
|
187
|
+
"""Custom cleanup logic for LLM service."""
|
|
188
|
+
await self.client.close()
|
|
189
|
+
logger.info("OpenAI Compatible LLM Service stopped")
|
|
190
|
+
|
|
191
|
+
def update_api_key(self, new_api_key: str) -> None:
|
|
192
|
+
"""Update the API key and reset circuit breaker.
|
|
193
|
+
|
|
194
|
+
Called when Android TokenRefreshManager provides a fresh Google ID token.
|
|
195
|
+
This is critical for ciris.ai proxy authentication which uses JWT tokens
|
|
196
|
+
that expire after ~1 hour.
|
|
197
|
+
"""
|
|
198
|
+
if not new_api_key:
|
|
199
|
+
logger.warning("[LLM_TOKEN] Attempted to update with empty API key - ignoring")
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
old_key_preview = self.openai_config.api_key[:20] + "..." if self.openai_config.api_key else "None"
|
|
203
|
+
new_key_preview = new_api_key[:20] + "..."
|
|
204
|
+
|
|
205
|
+
# Update config
|
|
206
|
+
self.openai_config.api_key = new_api_key
|
|
207
|
+
|
|
208
|
+
# Update the OpenAI client's API key
|
|
209
|
+
# The AsyncOpenAI client stores the key and uses it for all requests
|
|
210
|
+
self.client.api_key = new_api_key
|
|
211
|
+
|
|
212
|
+
# Also update instructor client if it has a reference to the key
|
|
213
|
+
if hasattr(self.instruct_client, "client") and hasattr(self.instruct_client.client, "api_key"):
|
|
214
|
+
self.instruct_client.client.api_key = new_api_key
|
|
215
|
+
|
|
216
|
+
# Reset circuit breaker to allow immediate retry
|
|
217
|
+
self.circuit_breaker.reset()
|
|
218
|
+
|
|
219
|
+
logger.info(
|
|
220
|
+
"[LLM_TOKEN] API key updated and circuit breaker reset:\n"
|
|
221
|
+
" Old key: %s\n"
|
|
222
|
+
" New key: %s\n"
|
|
223
|
+
" Circuit breaker state: %s",
|
|
224
|
+
old_key_preview,
|
|
225
|
+
new_key_preview,
|
|
226
|
+
self.circuit_breaker.get_stats().get("state", "unknown"),
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
async def handle_token_refreshed(self, signal: str, resource: str) -> None:
|
|
230
|
+
"""Handle token_refreshed signal from ResourceMonitor.
|
|
231
|
+
|
|
232
|
+
Called when Android's TokenRefreshManager has updated .env with a fresh
|
|
233
|
+
Google ID token and the ResourceMonitor has reloaded environment variables.
|
|
234
|
+
|
|
235
|
+
This is the signal handler registered with ResourceMonitor.signal_bus.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
signal: The signal name ("token_refreshed")
|
|
239
|
+
resource: The resource that was refreshed ("openai_api_key")
|
|
240
|
+
"""
|
|
241
|
+
logger.info("[LLM_TOKEN] Received token_refreshed signal: %s for %s", signal, resource)
|
|
242
|
+
|
|
243
|
+
# Read fresh API key from environment
|
|
244
|
+
new_api_key = os.environ.get("OPENAI_API_KEY", "")
|
|
245
|
+
|
|
246
|
+
if not new_api_key:
|
|
247
|
+
logger.warning("[LLM_TOKEN] No OPENAI_API_KEY found in environment after refresh")
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
# Check if key actually changed
|
|
251
|
+
if new_api_key == self.openai_config.api_key:
|
|
252
|
+
logger.info("[LLM_TOKEN] API key unchanged after refresh - just resetting circuit breaker")
|
|
253
|
+
self.circuit_breaker.reset()
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
# Update the key
|
|
257
|
+
self.update_api_key(new_api_key)
|
|
258
|
+
logger.info("[LLM_TOKEN] Token refresh complete - LLM service ready for requests")
|
|
259
|
+
|
|
260
|
+
def _get_client(self) -> AsyncOpenAI:
|
|
261
|
+
"""Return the OpenAI client instance (private method)."""
|
|
262
|
+
return self.client
|
|
263
|
+
|
|
264
|
+
async def is_healthy(self) -> bool:
|
|
265
|
+
"""Check if service is healthy - used by buses and registries."""
|
|
266
|
+
# Call parent class health check first
|
|
267
|
+
base_healthy = await super().is_healthy()
|
|
268
|
+
# Also check circuit breaker status
|
|
269
|
+
return base_healthy and self.circuit_breaker.is_available()
|
|
270
|
+
|
|
271
|
+
def get_capabilities(self) -> ServiceCapabilities:
|
|
272
|
+
"""Get service capabilities with custom metadata."""
|
|
273
|
+
# Get base capabilities
|
|
274
|
+
capabilities = super().get_capabilities()
|
|
275
|
+
|
|
276
|
+
# Add custom metadata using model_copy
|
|
277
|
+
if capabilities.metadata:
|
|
278
|
+
capabilities.metadata = capabilities.metadata.model_copy(
|
|
279
|
+
update={
|
|
280
|
+
"model": self.model_name,
|
|
281
|
+
"instructor_mode": getattr(self.openai_config, "instructor_mode", "JSON"),
|
|
282
|
+
"timeout_seconds": getattr(self.openai_config, "timeout_seconds", 30),
|
|
283
|
+
"max_retries": self.max_retries,
|
|
284
|
+
"circuit_breaker_state": self.circuit_breaker.get_stats().get("state", "unknown"),
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return capabilities
|
|
289
|
+
|
|
290
|
+
def _collect_custom_metrics(self) -> Dict[str, float]:
|
|
291
|
+
"""Collect service-specific metrics."""
|
|
292
|
+
cb_stats = self.circuit_breaker.get_stats()
|
|
293
|
+
|
|
294
|
+
# Calculate average response time
|
|
295
|
+
avg_response_time_ms = 0.0
|
|
296
|
+
if self._response_times:
|
|
297
|
+
avg_response_time_ms = sum(self._response_times) / len(self._response_times)
|
|
298
|
+
|
|
299
|
+
# Calculate API success rate
|
|
300
|
+
api_success_rate = 0.0
|
|
301
|
+
if self._total_api_calls > 0:
|
|
302
|
+
api_success_rate = self._successful_api_calls / self._total_api_calls
|
|
303
|
+
|
|
304
|
+
# Build custom metrics
|
|
305
|
+
metrics = {
|
|
306
|
+
# Circuit breaker metrics
|
|
307
|
+
"circuit_breaker_state": (
|
|
308
|
+
1.0 if cb_stats.get("state") == "open" else (0.5 if cb_stats.get("state") == "half_open" else 0.0)
|
|
309
|
+
),
|
|
310
|
+
"consecutive_failures": float(cb_stats.get("consecutive_failures", 0)),
|
|
311
|
+
"recovery_attempts": float(cb_stats.get("recovery_attempts", 0)),
|
|
312
|
+
"last_failure_age_seconds": float(cb_stats.get("last_failure_age", 0)),
|
|
313
|
+
"success_rate": cb_stats.get("success_rate", 1.0),
|
|
314
|
+
"call_count": float(cb_stats.get("call_count", 0)),
|
|
315
|
+
"failure_count": float(cb_stats.get("failure_count", 0)),
|
|
316
|
+
# Performance metrics (no caching)
|
|
317
|
+
"avg_response_time_ms": avg_response_time_ms,
|
|
318
|
+
"max_response_time_ms": max(self._response_times) if self._response_times else 0.0,
|
|
319
|
+
"min_response_time_ms": min(self._response_times) if self._response_times else 0.0,
|
|
320
|
+
"total_api_calls": float(self._total_api_calls),
|
|
321
|
+
"successful_api_calls": float(self._successful_api_calls),
|
|
322
|
+
"api_success_rate": api_success_rate,
|
|
323
|
+
# Model pricing info
|
|
324
|
+
"model_cost_per_1k_tokens": 0.15 if "gpt-4o-mini" in self.model_name else 2.5, # Cents
|
|
325
|
+
"retry_delay_base": self.base_delay,
|
|
326
|
+
"retry_delay_max": self.max_delay,
|
|
327
|
+
# Model configuration
|
|
328
|
+
"model_timeout_seconds": float(getattr(self.openai_config, "timeout_seconds", 30)),
|
|
329
|
+
"model_max_retries": float(self.max_retries),
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return metrics
|
|
333
|
+
|
|
334
|
+
async def get_metrics(self) -> Dict[str, float]:
|
|
335
|
+
"""
|
|
336
|
+
Get all LLM metrics including base, custom, and v1.4.3 specific metrics.
|
|
337
|
+
"""
|
|
338
|
+
# Get all base + custom metrics
|
|
339
|
+
metrics = self._collect_metrics()
|
|
340
|
+
|
|
341
|
+
# Add v1.4.3 specific LLM metrics
|
|
342
|
+
metrics.update(
|
|
343
|
+
{
|
|
344
|
+
"llm_requests_total": float(self._total_requests),
|
|
345
|
+
"llm_tokens_input": float(self._total_input_tokens),
|
|
346
|
+
"llm_tokens_output": float(self._total_output_tokens),
|
|
347
|
+
"llm_tokens_total": float(self._total_input_tokens + self._total_output_tokens),
|
|
348
|
+
"llm_cost_cents": self._total_cost_cents,
|
|
349
|
+
"llm_errors_total": float(self._total_errors),
|
|
350
|
+
"llm_uptime_seconds": self._calculate_uptime(),
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
return metrics
|
|
355
|
+
|
|
356
|
+
def _extract_json_from_response(self, raw: str) -> JSONExtractionResult:
|
|
357
|
+
"""Extract and parse JSON from LLM response (private method)."""
|
|
358
|
+
return self._extract_json(raw)
|
|
359
|
+
|
|
360
|
+
@classmethod
|
|
361
|
+
def _extract_json(cls, raw: str) -> JSONExtractionResult:
|
|
362
|
+
"""Extract and parse JSON from LLM response (private method)."""
|
|
363
|
+
json_pattern = r"```json\s*(\{.*?\})\s*```"
|
|
364
|
+
match = re.search(json_pattern, raw, re.DOTALL)
|
|
365
|
+
|
|
366
|
+
if match:
|
|
367
|
+
json_str = match.group(1)
|
|
368
|
+
else:
|
|
369
|
+
json_str = raw.strip()
|
|
370
|
+
try:
|
|
371
|
+
parsed = json.loads(json_str)
|
|
372
|
+
return JSONExtractionResult(success=True, data=ExtractedJSONData(**parsed))
|
|
373
|
+
except json.JSONDecodeError:
|
|
374
|
+
try:
|
|
375
|
+
parsed_retry = json.loads(json_str.replace("'", '"'))
|
|
376
|
+
return JSONExtractionResult(success=True, data=ExtractedJSONData(**parsed_retry))
|
|
377
|
+
except json.JSONDecodeError:
|
|
378
|
+
return JSONExtractionResult(
|
|
379
|
+
success=False, error="Failed to parse JSON", raw_content=raw[:200] # First 200 chars
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
async def call_llm_structured(
|
|
383
|
+
self,
|
|
384
|
+
messages: List[MessageDict],
|
|
385
|
+
response_model: Type[BaseModel],
|
|
386
|
+
max_tokens: int = 1024,
|
|
387
|
+
temperature: float = 0.0,
|
|
388
|
+
thought_id: Optional[str] = None,
|
|
389
|
+
task_id: Optional[str] = None,
|
|
390
|
+
) -> Tuple[BaseModel, ResourceUsage]:
|
|
391
|
+
"""Make a structured LLM call with circuit breaker protection.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
messages: List of message dicts for the LLM
|
|
395
|
+
response_model: Pydantic model for structured response
|
|
396
|
+
max_tokens: Maximum tokens in response
|
|
397
|
+
temperature: Sampling temperature
|
|
398
|
+
thought_id: Optional thought ID for tracing (last 8 chars used)
|
|
399
|
+
task_id: Optional task ID for tracing (last 8 chars used)
|
|
400
|
+
"""
|
|
401
|
+
# Track the request
|
|
402
|
+
self._track_request()
|
|
403
|
+
# Track LLM-specific request
|
|
404
|
+
self._total_requests += 1
|
|
405
|
+
|
|
406
|
+
# No mock service integration - LLMService and MockLLMService are separate
|
|
407
|
+
logger.debug(f"Structured LLM call for {response_model.__name__}")
|
|
408
|
+
|
|
409
|
+
# Check circuit breaker before making call
|
|
410
|
+
self.circuit_breaker.check_and_raise()
|
|
411
|
+
|
|
412
|
+
# Track retry state for metadata
|
|
413
|
+
retry_state: Dict[str, Any] = {"count": 0, "previous_error": None, "original_request_id": None}
|
|
414
|
+
|
|
415
|
+
async def _make_structured_call(
|
|
416
|
+
msg_list: List[MessageDict],
|
|
417
|
+
resp_model: Type[BaseModel],
|
|
418
|
+
max_toks: int,
|
|
419
|
+
temp: float,
|
|
420
|
+
) -> Tuple[BaseModel, ResourceUsage]:
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
# Use instructor but capture the completion for usage data
|
|
424
|
+
# Note: We cast to Any because instructor expects OpenAI-specific message types
|
|
425
|
+
# but we use our own MessageDict protocol for type safety at the service boundary
|
|
426
|
+
|
|
427
|
+
# Build extra kwargs for CIRIS proxy (requires interaction_id)
|
|
428
|
+
# NOTE: CIRIS proxy charges per unique interaction_id, so we use task_id only
|
|
429
|
+
# All thoughts within the same task share one credit
|
|
430
|
+
extra_kwargs: Dict[str, Any] = {}
|
|
431
|
+
base_url = self.openai_config.base_url or ""
|
|
432
|
+
if "ciris.ai" in base_url or "ciris-services" in base_url:
|
|
433
|
+
# Hash task_id for billing (irreversible, same task = same hash = 1 credit)
|
|
434
|
+
import hashlib
|
|
435
|
+
import uuid
|
|
436
|
+
|
|
437
|
+
if not task_id:
|
|
438
|
+
raise RuntimeError(
|
|
439
|
+
f"BILLING BUG: task_id is required for CIRIS proxy but was None "
|
|
440
|
+
f"(thought_id={thought_id}, model={resp_model.__name__})"
|
|
441
|
+
)
|
|
442
|
+
interaction_id = hashlib.sha256(task_id.encode()).hexdigest()[:32]
|
|
443
|
+
|
|
444
|
+
# Build metadata for CIRIS proxy
|
|
445
|
+
metadata: Dict[str, Any] = {"interaction_id": interaction_id}
|
|
446
|
+
|
|
447
|
+
# Add retry info if this is a retry attempt
|
|
448
|
+
if retry_state["count"] > 0:
|
|
449
|
+
metadata["retry_count"] = retry_state["count"]
|
|
450
|
+
if retry_state["previous_error"]:
|
|
451
|
+
metadata["previous_error"] = retry_state["previous_error"]
|
|
452
|
+
if retry_state["original_request_id"]:
|
|
453
|
+
metadata["original_request_id"] = retry_state["original_request_id"]
|
|
454
|
+
logger.info(
|
|
455
|
+
f"[LLM_RETRY] attempt={retry_state['count']} "
|
|
456
|
+
f"prev_error={retry_state['previous_error']} "
|
|
457
|
+
f"interaction_id={interaction_id}"
|
|
458
|
+
)
|
|
459
|
+
else:
|
|
460
|
+
# First attempt - generate request ID for correlation
|
|
461
|
+
retry_state["original_request_id"] = uuid.uuid4().hex[:12]
|
|
462
|
+
metadata["request_id"] = retry_state["original_request_id"]
|
|
463
|
+
logger.info(
|
|
464
|
+
f"[LLM_REQUEST] interaction_id={interaction_id} "
|
|
465
|
+
f"request_id={retry_state['original_request_id']} "
|
|
466
|
+
f"thought_id={thought_id} model={resp_model.__name__}"
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
extra_kwargs["extra_body"] = {"metadata": metadata}
|
|
470
|
+
|
|
471
|
+
# DEBUG: Log multimodal content details for proxy team diagnostics
|
|
472
|
+
image_count = 0
|
|
473
|
+
total_image_bytes = 0
|
|
474
|
+
for msg in msg_list:
|
|
475
|
+
content = msg.get("content", "")
|
|
476
|
+
if isinstance(content, list):
|
|
477
|
+
for block in content:
|
|
478
|
+
if isinstance(block, dict) and block.get("type") == "image_url":
|
|
479
|
+
image_count += 1
|
|
480
|
+
url = block.get("image_url", {}).get("url", "")
|
|
481
|
+
if url.startswith("data:image"):
|
|
482
|
+
total_image_bytes += len(url)
|
|
483
|
+
if image_count > 0:
|
|
484
|
+
logger.info(
|
|
485
|
+
f"[VISION_DEBUG] Sending to proxy: model={self.model_name}, "
|
|
486
|
+
f"images={image_count}, image_data_bytes={total_image_bytes}, "
|
|
487
|
+
f"thought_id={thought_id}, response_model={resp_model.__name__}"
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
response, completion = await self.instruct_client.chat.completions.create_with_completion(
|
|
491
|
+
model=self.model_name,
|
|
492
|
+
messages=cast(Any, msg_list),
|
|
493
|
+
response_model=resp_model,
|
|
494
|
+
max_retries=0, # Disable instructor retries completely
|
|
495
|
+
max_tokens=max_toks,
|
|
496
|
+
temperature=temp,
|
|
497
|
+
**extra_kwargs,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
# DEBUG: Log proxy response details
|
|
501
|
+
if image_count > 0:
|
|
502
|
+
actual_model = getattr(completion, "model", "unknown")
|
|
503
|
+
logger.info(
|
|
504
|
+
f"[VISION_DEBUG] Proxy response: requested={self.model_name}, "
|
|
505
|
+
f"actual_model={actual_model}, thought_id={thought_id}"
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
# Extract usage data from completion
|
|
509
|
+
usage = completion.usage
|
|
510
|
+
|
|
511
|
+
# Record success with circuit breaker
|
|
512
|
+
self.circuit_breaker.record_success()
|
|
513
|
+
|
|
514
|
+
# Extract token counts
|
|
515
|
+
prompt_tokens = getattr(usage, "prompt_tokens", 0)
|
|
516
|
+
completion_tokens = getattr(usage, "completion_tokens", 0)
|
|
517
|
+
|
|
518
|
+
# Calculate costs and environmental impact using pricing calculator
|
|
519
|
+
usage_obj = self.pricing_calculator.calculate_cost_and_impact(
|
|
520
|
+
model_name=self.model_name,
|
|
521
|
+
prompt_tokens=prompt_tokens,
|
|
522
|
+
completion_tokens=completion_tokens,
|
|
523
|
+
provider_name="openai", # Since this is OpenAI-compatible client
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
# Track metrics for get_metrics() method
|
|
527
|
+
self._total_input_tokens += prompt_tokens
|
|
528
|
+
self._total_output_tokens += completion_tokens
|
|
529
|
+
self._total_cost_cents += usage_obj.cost_cents
|
|
530
|
+
|
|
531
|
+
# Record token usage in telemetry
|
|
532
|
+
if self.telemetry_service and usage_obj.tokens_used > 0:
|
|
533
|
+
await self.telemetry_service.record_metric("llm_tokens_used", usage_obj.tokens_used)
|
|
534
|
+
await self.telemetry_service.record_metric("llm_api_call_structured")
|
|
535
|
+
|
|
536
|
+
return response, usage_obj
|
|
537
|
+
|
|
538
|
+
except AuthenticationError as e:
|
|
539
|
+
# Handle 401 Unauthorized - likely expired token or billing issue for ciris.ai
|
|
540
|
+
self._track_error(e)
|
|
541
|
+
self._total_errors += 1
|
|
542
|
+
|
|
543
|
+
base_url = self.openai_config.base_url or ""
|
|
544
|
+
if "ciris.ai" in base_url:
|
|
545
|
+
# Force circuit breaker open immediately (don't wait for failure threshold)
|
|
546
|
+
# This prevents burning credits on repeated failures
|
|
547
|
+
self.circuit_breaker.force_open(reason="ciris.ai 401 - billing or token error")
|
|
548
|
+
# Write signal file for Android to trigger token refresh
|
|
549
|
+
logger.error(
|
|
550
|
+
f"LLM AUTHENTICATION ERROR (401) - ciris.ai billing or token error.\n"
|
|
551
|
+
f" Model: {self.model_name}\n"
|
|
552
|
+
f" Provider: {base_url}\n"
|
|
553
|
+
f" Circuit breaker forced open immediately.\n"
|
|
554
|
+
f" Writing token refresh signal..."
|
|
555
|
+
)
|
|
556
|
+
self._signal_token_refresh_needed()
|
|
557
|
+
else:
|
|
558
|
+
self.circuit_breaker.record_failure()
|
|
559
|
+
logger.error(
|
|
560
|
+
f"LLM AUTHENTICATION ERROR (401) - Invalid API key.\n"
|
|
561
|
+
f" Model: {self.model_name}\n"
|
|
562
|
+
f" Provider: {base_url}\n"
|
|
563
|
+
f" Error: {e}"
|
|
564
|
+
)
|
|
565
|
+
raise
|
|
566
|
+
|
|
567
|
+
except (APIConnectionError, RateLimitError, InternalServerError) as e:
|
|
568
|
+
# Record failure with circuit breaker
|
|
569
|
+
self.circuit_breaker.record_failure()
|
|
570
|
+
# Track error in base service
|
|
571
|
+
self._track_error(e)
|
|
572
|
+
# Track LLM-specific error
|
|
573
|
+
self._total_errors += 1
|
|
574
|
+
|
|
575
|
+
# Enhanced error logging for provider errors
|
|
576
|
+
error_type = type(e).__name__
|
|
577
|
+
error_details = {
|
|
578
|
+
"error_type": error_type,
|
|
579
|
+
"model": self.model_name,
|
|
580
|
+
"base_url": self.openai_config.base_url or "default",
|
|
581
|
+
"circuit_breaker_state": self.circuit_breaker.state.value,
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if isinstance(e, RateLimitError):
|
|
585
|
+
logger.error(
|
|
586
|
+
f"LLM RATE LIMIT ERROR - Provider: {error_details['base_url']}, "
|
|
587
|
+
f"Model: {error_details['model']}, CB State: {error_details['circuit_breaker_state']}. "
|
|
588
|
+
f"Error: {e}"
|
|
589
|
+
)
|
|
590
|
+
elif isinstance(e, InternalServerError):
|
|
591
|
+
error_str = str(e).lower()
|
|
592
|
+
# Check for billing service errors - should be surfaced to user
|
|
593
|
+
if "billing service error" in error_str or "billing error" in error_str:
|
|
594
|
+
from ciris_engine.logic.adapters.base_observer import BillingServiceError
|
|
595
|
+
|
|
596
|
+
# Force circuit breaker open - don't retry billing errors
|
|
597
|
+
self.circuit_breaker.force_open(reason="Billing service error")
|
|
598
|
+
logger.error(
|
|
599
|
+
f"LLM BILLING ERROR - Provider: {error_details['base_url']}, "
|
|
600
|
+
f"Model: {error_details['model']}. Billing service returned error: {e}"
|
|
601
|
+
)
|
|
602
|
+
raise BillingServiceError(
|
|
603
|
+
message=f"LLM billing service error. Please check your account status or try again later. Details: {e}",
|
|
604
|
+
status_code=402,
|
|
605
|
+
) from e
|
|
606
|
+
else:
|
|
607
|
+
logger.error(
|
|
608
|
+
f"LLM PROVIDER ERROR (500) - Provider: {error_details['base_url']}, "
|
|
609
|
+
f"Model: {error_details['model']}, CB State: {error_details['circuit_breaker_state']}. "
|
|
610
|
+
f"Provider returned internal server error: {e}"
|
|
611
|
+
)
|
|
612
|
+
elif isinstance(e, APIConnectionError):
|
|
613
|
+
logger.error(
|
|
614
|
+
f"LLM CONNECTION ERROR - Provider: {error_details['base_url']}, "
|
|
615
|
+
f"Model: {error_details['model']}, CB State: {error_details['circuit_breaker_state']}. "
|
|
616
|
+
f"Failed to connect to provider: {e}"
|
|
617
|
+
)
|
|
618
|
+
else:
|
|
619
|
+
logger.error(
|
|
620
|
+
f"LLM API ERROR ({error_type}) - Provider: {error_details['base_url']}, "
|
|
621
|
+
f"Model: {error_details['model']}, CB State: {error_details['circuit_breaker_state']}. "
|
|
622
|
+
f"Error: {e}"
|
|
623
|
+
)
|
|
624
|
+
raise
|
|
625
|
+
except Exception as e:
|
|
626
|
+
# Check if this is an instructor retry exception (includes timeouts, 503 errors, rate limits, etc.)
|
|
627
|
+
if hasattr(instructor, "exceptions") and hasattr(instructor.exceptions, "InstructorRetryException"):
|
|
628
|
+
if isinstance(e, instructor.exceptions.InstructorRetryException):
|
|
629
|
+
# Record failure for circuit breaker regardless of specific error type
|
|
630
|
+
self.circuit_breaker.record_failure()
|
|
631
|
+
self._track_error(e)
|
|
632
|
+
# Track LLM-specific error
|
|
633
|
+
self._total_errors += 1
|
|
634
|
+
|
|
635
|
+
# Build error context for better debugging
|
|
636
|
+
error_context = {
|
|
637
|
+
"model": self.model_name,
|
|
638
|
+
"provider": self.openai_config.base_url or "default",
|
|
639
|
+
"response_model": resp_model.__name__,
|
|
640
|
+
"circuit_breaker_state": self.circuit_breaker.state.value,
|
|
641
|
+
"consecutive_failures": self.circuit_breaker.consecutive_failures,
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
# Provide specific error messages for different failure types
|
|
645
|
+
error_str = str(e).lower()
|
|
646
|
+
full_error = str(e)
|
|
647
|
+
|
|
648
|
+
# Check for schema validation errors
|
|
649
|
+
if "validation" in error_str or "validationerror" in error_str:
|
|
650
|
+
# Extract validation details
|
|
651
|
+
logger.error(
|
|
652
|
+
f"LLM SCHEMA VALIDATION ERROR - Response did not match expected schema.\n"
|
|
653
|
+
f" Model: {error_context['model']}\n"
|
|
654
|
+
f" Provider: {error_context['provider']}\n"
|
|
655
|
+
f" Expected Schema: {error_context['response_model']}\n"
|
|
656
|
+
f" CB State: {error_context['circuit_breaker_state']} "
|
|
657
|
+
f"({error_context['consecutive_failures']} consecutive failures)\n"
|
|
658
|
+
f" Validation Details: {full_error[:500]}"
|
|
659
|
+
)
|
|
660
|
+
raise RuntimeError(
|
|
661
|
+
f"LLM response validation failed for {resp_model.__name__} - "
|
|
662
|
+
"circuit breaker activated for failover"
|
|
663
|
+
) from e
|
|
664
|
+
|
|
665
|
+
# Check for timeout errors
|
|
666
|
+
elif "timed out" in error_str or "timeout" in error_str:
|
|
667
|
+
logger.error(
|
|
668
|
+
f"LLM TIMEOUT ERROR - Request exceeded {self.openai_config.timeout_seconds}s timeout.\n"
|
|
669
|
+
f" Model: {error_context['model']}\n"
|
|
670
|
+
f" Provider: {error_context['provider']}\n"
|
|
671
|
+
f" CB State: {error_context['circuit_breaker_state']} "
|
|
672
|
+
f"({error_context['consecutive_failures']} consecutive failures)\n"
|
|
673
|
+
f" Error: {full_error[:300]}"
|
|
674
|
+
)
|
|
675
|
+
raise TimeoutError(
|
|
676
|
+
f"LLM API timeout ({self.openai_config.timeout_seconds}s) "
|
|
677
|
+
"- circuit breaker activated"
|
|
678
|
+
) from e
|
|
679
|
+
|
|
680
|
+
# Check for service unavailable / 503 errors
|
|
681
|
+
elif "service unavailable" in error_str or "503" in error_str:
|
|
682
|
+
logger.error(
|
|
683
|
+
f"LLM SERVICE UNAVAILABLE (503) - Provider temporarily down.\n"
|
|
684
|
+
f" Model: {error_context['model']}\n"
|
|
685
|
+
f" Provider: {error_context['provider']}\n"
|
|
686
|
+
f" CB State: {error_context['circuit_breaker_state']} "
|
|
687
|
+
f"({error_context['consecutive_failures']} consecutive failures)\n"
|
|
688
|
+
f" Error: {full_error[:300]}"
|
|
689
|
+
)
|
|
690
|
+
raise RuntimeError(
|
|
691
|
+
"LLM service unavailable (503) - circuit breaker activated for failover"
|
|
692
|
+
) from e
|
|
693
|
+
|
|
694
|
+
# Check for rate limit / 429 errors
|
|
695
|
+
elif "rate limit" in error_str or "429" in error_str:
|
|
696
|
+
logger.error(
|
|
697
|
+
f"LLM RATE LIMIT (429) - Provider quota exceeded.\n"
|
|
698
|
+
f" Model: {error_context['model']}\n"
|
|
699
|
+
f" Provider: {error_context['provider']}\n"
|
|
700
|
+
f" CB State: {error_context['circuit_breaker_state']} "
|
|
701
|
+
f"({error_context['consecutive_failures']} consecutive failures)\n"
|
|
702
|
+
f" Error: {full_error[:300]}"
|
|
703
|
+
)
|
|
704
|
+
raise RuntimeError(
|
|
705
|
+
"LLM rate limit exceeded (429) - circuit breaker activated for failover"
|
|
706
|
+
) from e
|
|
707
|
+
|
|
708
|
+
# Check for context length / token limit exceeded errors (400)
|
|
709
|
+
elif (
|
|
710
|
+
"context_length" in error_str
|
|
711
|
+
or "maximum context" in error_str
|
|
712
|
+
or "context length" in error_str
|
|
713
|
+
or "token limit" in error_str
|
|
714
|
+
or "too many tokens" in error_str
|
|
715
|
+
or "max_tokens" in error_str
|
|
716
|
+
and "exceed" in error_str
|
|
717
|
+
):
|
|
718
|
+
logger.error(
|
|
719
|
+
f"LLM CONTEXT_LENGTH_EXCEEDED - Input too long for model.\n"
|
|
720
|
+
f" Model: {error_context['model']}\n"
|
|
721
|
+
f" Provider: {error_context['provider']}\n"
|
|
722
|
+
f" CB State: {error_context['circuit_breaker_state']} "
|
|
723
|
+
f"({error_context['consecutive_failures']} consecutive failures)\n"
|
|
724
|
+
f" Error: {full_error[:500]}"
|
|
725
|
+
)
|
|
726
|
+
raise RuntimeError(
|
|
727
|
+
"CONTEXT_LENGTH_EXCEEDED: Input too long - reduce message history or context"
|
|
728
|
+
) from e
|
|
729
|
+
|
|
730
|
+
# Check for content filtering / guardrail errors
|
|
731
|
+
elif "content_filter" in error_str or "content policy" in error_str or "safety" in error_str:
|
|
732
|
+
logger.error(
|
|
733
|
+
f"LLM CONTENT FILTER / GUARDRAIL - Request blocked by provider safety systems.\n"
|
|
734
|
+
f" Model: {error_context['model']}\n"
|
|
735
|
+
f" Provider: {error_context['provider']}\n"
|
|
736
|
+
f" CB State: {error_context['circuit_breaker_state']} "
|
|
737
|
+
f"({error_context['consecutive_failures']} consecutive failures)\n"
|
|
738
|
+
f" Error: {full_error[:300]}"
|
|
739
|
+
)
|
|
740
|
+
raise RuntimeError(
|
|
741
|
+
"LLM content filter triggered - circuit breaker activated for failover"
|
|
742
|
+
) from e
|
|
743
|
+
|
|
744
|
+
# Generic instructor error with enhanced logging
|
|
745
|
+
else:
|
|
746
|
+
logger.error(
|
|
747
|
+
f"LLM INSTRUCTOR ERROR - Unspecified failure.\n"
|
|
748
|
+
f" Model: {error_context['model']}\n"
|
|
749
|
+
f" Provider: {error_context['provider']}\n"
|
|
750
|
+
f" Expected Schema: {error_context['response_model']}\n"
|
|
751
|
+
f" CB State: {error_context['circuit_breaker_state']} "
|
|
752
|
+
f"({error_context['consecutive_failures']} consecutive failures)\n"
|
|
753
|
+
f" Error Type: {type(e).__name__}\n"
|
|
754
|
+
f" Error: {full_error[:500]}"
|
|
755
|
+
)
|
|
756
|
+
raise RuntimeError("LLM API call failed - circuit breaker activated for failover") from e
|
|
757
|
+
# Re-raise other exceptions
|
|
758
|
+
raise
|
|
759
|
+
|
|
760
|
+
# Implement retry logic with OpenAI-specific error handling
|
|
761
|
+
try:
|
|
762
|
+
return await self._retry_with_backoff(
|
|
763
|
+
_make_structured_call,
|
|
764
|
+
messages,
|
|
765
|
+
response_model,
|
|
766
|
+
max_tokens,
|
|
767
|
+
temperature,
|
|
768
|
+
retry_state=retry_state, # Pass retry state for CIRIS proxy metadata
|
|
769
|
+
)
|
|
770
|
+
except CircuitBreakerError:
|
|
771
|
+
# Don't retry if circuit breaker is open
|
|
772
|
+
logger.warning("LLM service circuit breaker is open, failing fast")
|
|
773
|
+
raise
|
|
774
|
+
except TimeoutError:
|
|
775
|
+
# Don't retry timeout errors to prevent cascades
|
|
776
|
+
logger.warning("LLM structured service timeout, failing fast to prevent retry cascade")
|
|
777
|
+
raise
|
|
778
|
+
|
|
779
|
+
def _get_status(self) -> LLMStatus:
|
|
780
|
+
"""Get detailed status including circuit breaker metrics (private method)."""
|
|
781
|
+
# Get circuit breaker stats
|
|
782
|
+
cb_stats = self.circuit_breaker.get_stats()
|
|
783
|
+
|
|
784
|
+
# Calculate average response time if we have metrics
|
|
785
|
+
avg_response_time = None
|
|
786
|
+
if hasattr(self, "_response_times") and self._response_times:
|
|
787
|
+
avg_response_time = sum(self._response_times) / len(self._response_times)
|
|
788
|
+
|
|
789
|
+
return LLMStatus(
|
|
790
|
+
available=self.circuit_breaker.is_available(),
|
|
791
|
+
model=self.model_name,
|
|
792
|
+
usage=LLMUsageStatistics(
|
|
793
|
+
total_calls=cb_stats.get("call_count", 0),
|
|
794
|
+
failed_calls=cb_stats.get("failure_count", 0),
|
|
795
|
+
success_rate=cb_stats.get("success_rate", 1.0),
|
|
796
|
+
),
|
|
797
|
+
rate_limit_remaining=None, # Would need to track from API responses
|
|
798
|
+
response_time_avg=avg_response_time,
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
async def _retry_with_backoff(
|
|
802
|
+
self,
|
|
803
|
+
func: StructuredCallFunc,
|
|
804
|
+
messages: List[MessageDict],
|
|
805
|
+
response_model: Type[BaseModel],
|
|
806
|
+
max_tokens: int,
|
|
807
|
+
temperature: float,
|
|
808
|
+
retry_state: Optional[Dict[str, Any]] = None,
|
|
809
|
+
) -> Tuple[BaseModel, ResourceUsage]:
|
|
810
|
+
"""Retry with exponential backoff (private method).
|
|
811
|
+
|
|
812
|
+
Args:
|
|
813
|
+
func: The callable to retry
|
|
814
|
+
messages: LLM messages
|
|
815
|
+
response_model: Pydantic response model
|
|
816
|
+
max_tokens: Max tokens
|
|
817
|
+
temperature: Temperature
|
|
818
|
+
retry_state: Optional dict to track retry info for CIRIS proxy metadata
|
|
819
|
+
{"count": int, "previous_error": str, "original_request_id": str}
|
|
820
|
+
"""
|
|
821
|
+
last_exception = None
|
|
822
|
+
for attempt in range(self.max_retries):
|
|
823
|
+
try:
|
|
824
|
+
return await func(messages, response_model, max_tokens, temperature)
|
|
825
|
+
except self.retryable_exceptions as e:
|
|
826
|
+
last_exception = e
|
|
827
|
+
|
|
828
|
+
# Categorize error for retry metadata
|
|
829
|
+
error_category = self._categorize_llm_error(e)
|
|
830
|
+
|
|
831
|
+
# Update retry state for next attempt's metadata
|
|
832
|
+
if retry_state is not None:
|
|
833
|
+
retry_state["count"] = attempt + 1
|
|
834
|
+
retry_state["previous_error"] = error_category
|
|
835
|
+
|
|
836
|
+
if attempt < self.max_retries - 1:
|
|
837
|
+
delay = min(self.base_delay * (2**attempt), self.max_delay)
|
|
838
|
+
logger.warning(
|
|
839
|
+
f"[LLM_RETRY_SCHEDULED] attempt={attempt + 1}/{self.max_retries} "
|
|
840
|
+
f"error={error_category} delay={delay:.1f}s"
|
|
841
|
+
)
|
|
842
|
+
import asyncio
|
|
843
|
+
|
|
844
|
+
await asyncio.sleep(delay)
|
|
845
|
+
continue
|
|
846
|
+
raise
|
|
847
|
+
except self.non_retryable_exceptions:
|
|
848
|
+
raise
|
|
849
|
+
|
|
850
|
+
if last_exception:
|
|
851
|
+
raise last_exception
|
|
852
|
+
raise RuntimeError("Retry logic failed without exception")
|
|
853
|
+
|
|
854
|
+
@staticmethod
|
|
855
|
+
def _categorize_llm_error(error: Exception) -> str:
|
|
856
|
+
"""Categorize an LLM error for retry metadata.
|
|
857
|
+
|
|
858
|
+
Returns error category string for proxy correlation:
|
|
859
|
+
- TIMEOUT: Request timed out
|
|
860
|
+
- CONNECTION_ERROR: Could not connect
|
|
861
|
+
- RATE_LIMIT: Rate limit exceeded (429)
|
|
862
|
+
- CONTEXT_LENGTH_EXCEEDED: Input too long (400)
|
|
863
|
+
- VALIDATION_ERROR: Response didn't match schema
|
|
864
|
+
- AUTH_ERROR: Authentication failed (401)
|
|
865
|
+
- INTERNAL_ERROR: Provider error (500/503)
|
|
866
|
+
- UNKNOWN: Unrecognized error
|
|
867
|
+
"""
|
|
868
|
+
error_str = str(error).lower()
|
|
869
|
+
|
|
870
|
+
# Check for timeout errors
|
|
871
|
+
if "timeout" in error_str or "timed out" in error_str:
|
|
872
|
+
return "TIMEOUT"
|
|
873
|
+
|
|
874
|
+
# Check for connection errors
|
|
875
|
+
if isinstance(error, APIConnectionError):
|
|
876
|
+
if "timeout" in error_str:
|
|
877
|
+
return "TIMEOUT"
|
|
878
|
+
return "CONNECTION_ERROR"
|
|
879
|
+
|
|
880
|
+
# Check for rate limit
|
|
881
|
+
if isinstance(error, RateLimitError) or "rate limit" in error_str or "429" in error_str:
|
|
882
|
+
return "RATE_LIMIT"
|
|
883
|
+
|
|
884
|
+
# Check for context length exceeded
|
|
885
|
+
if (
|
|
886
|
+
"context_length" in error_str
|
|
887
|
+
or "maximum context" in error_str
|
|
888
|
+
or "context length" in error_str
|
|
889
|
+
or "token limit" in error_str
|
|
890
|
+
or "too many tokens" in error_str
|
|
891
|
+
):
|
|
892
|
+
return "CONTEXT_LENGTH_EXCEEDED"
|
|
893
|
+
|
|
894
|
+
# Check for validation errors
|
|
895
|
+
if "validation" in error_str or "validationerror" in error_str:
|
|
896
|
+
return "VALIDATION_ERROR"
|
|
897
|
+
|
|
898
|
+
# Check for auth errors
|
|
899
|
+
if isinstance(error, AuthenticationError) or "401" in error_str or "unauthorized" in error_str:
|
|
900
|
+
return "AUTH_ERROR"
|
|
901
|
+
|
|
902
|
+
# Check for server errors
|
|
903
|
+
if isinstance(error, InternalServerError) or "500" in error_str or "503" in error_str:
|
|
904
|
+
return "INTERNAL_ERROR"
|
|
905
|
+
|
|
906
|
+
return "UNKNOWN"
|
|
907
|
+
|
|
908
|
+
def _signal_token_refresh_needed(self) -> None:
|
|
909
|
+
"""Write a signal file to indicate token refresh is needed (for ciris.ai).
|
|
910
|
+
|
|
911
|
+
This file is monitored by the Android app to trigger Google silentSignIn().
|
|
912
|
+
The signal file is written to CIRIS_HOME/.token_refresh_needed
|
|
913
|
+
"""
|
|
914
|
+
import os
|
|
915
|
+
from pathlib import Path
|
|
916
|
+
|
|
917
|
+
try:
|
|
918
|
+
# Get CIRIS_HOME from environment (set by mobile_main.py on Android)
|
|
919
|
+
ciris_home = os.getenv("CIRIS_HOME")
|
|
920
|
+
if not ciris_home:
|
|
921
|
+
# Fallback for non-Android environments
|
|
922
|
+
from ciris_engine.logic.utils.path_resolution import get_ciris_home
|
|
923
|
+
|
|
924
|
+
ciris_home = str(get_ciris_home())
|
|
925
|
+
|
|
926
|
+
signal_file = Path(ciris_home) / ".token_refresh_needed"
|
|
927
|
+
signal_file.write_text(str(time.time()))
|
|
928
|
+
logger.info(f"Token refresh signal written to: {signal_file}")
|
|
929
|
+
except Exception as e:
|
|
930
|
+
logger.error(f"Failed to write token refresh signal: {e}")
|