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,895 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Billing endpoints for CIRIS API.
|
|
3
|
+
|
|
4
|
+
Frontend proxy endpoints to CIRIS Billing backend.
|
|
5
|
+
Frontend should NEVER call the billing backend directly.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import re
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
from urllib.parse import quote
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
|
|
17
|
+
from ciris_engine.config.ciris_services import get_billing_url
|
|
18
|
+
from ciris_engine.schemas.api.auth import AuthContext
|
|
19
|
+
from ciris_engine.schemas.types import JSONDict
|
|
20
|
+
|
|
21
|
+
from ..dependencies.auth import require_observer
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
router = APIRouter(prefix="/api/billing", tags=["billing"])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Error message constants (avoid duplication)
|
|
29
|
+
ERROR_RESOURCE_MONITOR_UNAVAILABLE = "Resource monitor not available"
|
|
30
|
+
ERROR_CREDIT_PROVIDER_NOT_CONFIGURED = "Credit provider not configured"
|
|
31
|
+
ERROR_BILLING_SERVICE_UNAVAILABLE = "Billing service unavailable"
|
|
32
|
+
ERROR_INVALID_PAYMENT_ID = "Invalid payment ID format"
|
|
33
|
+
|
|
34
|
+
# Regex pattern for valid payment IDs (Stripe format: pi_xxx or similar alphanumeric with underscores)
|
|
35
|
+
PAYMENT_ID_PATTERN = re.compile(r"^[a-zA-Z0-9_-]{1,128}$")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Request/Response schemas
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CreditStatusResponse(BaseModel):
|
|
42
|
+
"""Credit status for frontend display."""
|
|
43
|
+
|
|
44
|
+
has_credit: bool = Field(..., description="Whether user has available credit")
|
|
45
|
+
credits_remaining: int = Field(..., description="Remaining paid credits")
|
|
46
|
+
free_uses_remaining: int = Field(..., description="Remaining free uses")
|
|
47
|
+
daily_free_uses_remaining: int = Field(0, description="Remaining daily free uses")
|
|
48
|
+
total_uses: int = Field(..., description="Total uses so far")
|
|
49
|
+
plan_name: Optional[str] = Field(None, description="Current plan name")
|
|
50
|
+
purchase_required: bool = Field(..., description="Whether purchase is required to continue")
|
|
51
|
+
purchase_options: Optional[JSONDict] = Field(None, description="Purchase options if required")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PurchaseInitiateRequest(BaseModel):
|
|
55
|
+
"""Request to initiate purchase flow."""
|
|
56
|
+
|
|
57
|
+
return_url: Optional[str] = Field(None, description="URL to return to after payment")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class PurchaseInitiateResponse(BaseModel):
|
|
61
|
+
"""Response with Stripe payment intent."""
|
|
62
|
+
|
|
63
|
+
payment_id: str = Field(..., description="Payment intent ID")
|
|
64
|
+
client_secret: str = Field(..., description="Stripe client secret for frontend")
|
|
65
|
+
amount_minor: int = Field(..., description="Amount in minor units (cents)")
|
|
66
|
+
currency: str = Field(..., description="Currency code (USD)")
|
|
67
|
+
uses_purchased: int = Field(..., description="Number of uses being purchased")
|
|
68
|
+
publishable_key: str = Field(..., description="Stripe publishable key")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class PurchaseStatusResponse(BaseModel):
|
|
72
|
+
"""Purchase status response."""
|
|
73
|
+
|
|
74
|
+
status: str = Field(..., description="Payment status (succeeded, pending, failed)")
|
|
75
|
+
credits_added: int = Field(..., description="Credits added (0 if not completed)")
|
|
76
|
+
balance_after: Optional[int] = Field(None, description="Balance after credits added")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class TransactionItem(BaseModel):
|
|
80
|
+
"""Individual transaction (charge or credit)."""
|
|
81
|
+
|
|
82
|
+
transaction_id: str = Field(..., description="Unique transaction ID")
|
|
83
|
+
type: str = Field(..., description="Transaction type: charge or credit")
|
|
84
|
+
amount_minor: int = Field(..., description="Amount in minor units (negative for charges, positive for credits)")
|
|
85
|
+
currency: str = Field(..., description="Currency code (USD)")
|
|
86
|
+
description: str = Field(..., description="Transaction description")
|
|
87
|
+
created_at: str = Field(..., description="Transaction timestamp (ISO format)")
|
|
88
|
+
balance_after: int = Field(..., description="Account balance after this transaction")
|
|
89
|
+
metadata: Optional[JSONDict] = Field(None, description="Additional metadata for charges")
|
|
90
|
+
transaction_type: Optional[str] = Field(None, description="Type of credit transaction (purchase, refund, etc)")
|
|
91
|
+
external_transaction_id: Optional[str] = Field(
|
|
92
|
+
None, description="External payment ID (e.g., Stripe payment intent)"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class TransactionListResponse(BaseModel):
|
|
97
|
+
"""Transaction history response."""
|
|
98
|
+
|
|
99
|
+
transactions: list[TransactionItem] = Field(..., description="List of transactions")
|
|
100
|
+
total_count: int = Field(..., description="Total number of transactions")
|
|
101
|
+
has_more: bool = Field(..., description="Whether more transactions are available")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Helper functions
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _get_billing_client(request: Request, google_id_token: Optional[str] = None) -> httpx.AsyncClient:
|
|
108
|
+
"""Get billing API client from app state.
|
|
109
|
+
|
|
110
|
+
Supports two authentication modes:
|
|
111
|
+
1. Server mode: Uses CIRIS_BILLING_API_KEY env var (for agents.ciris.ai)
|
|
112
|
+
2. JWT pass-through mode: Uses Google ID token from request (for Android/native)
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
request: FastAPI request object
|
|
116
|
+
google_id_token: Optional Google ID token for JWT pass-through mode
|
|
117
|
+
"""
|
|
118
|
+
import os
|
|
119
|
+
|
|
120
|
+
# Check if billing client already exists in app state (for testing or pre-configured)
|
|
121
|
+
if hasattr(request.app.state, "billing_client") and request.app.state.billing_client is not None:
|
|
122
|
+
existing_client: httpx.AsyncClient = request.app.state.billing_client
|
|
123
|
+
return existing_client
|
|
124
|
+
|
|
125
|
+
billing_url = get_billing_url()
|
|
126
|
+
api_key = os.getenv("CIRIS_BILLING_API_KEY")
|
|
127
|
+
|
|
128
|
+
# Determine authentication mode
|
|
129
|
+
if api_key:
|
|
130
|
+
# Server mode: use API key (cached client)
|
|
131
|
+
if not hasattr(request.app.state, "billing_client"):
|
|
132
|
+
headers = {
|
|
133
|
+
"X-API-Key": api_key,
|
|
134
|
+
"User-Agent": "CIRIS-Agent-Frontend/1.0",
|
|
135
|
+
}
|
|
136
|
+
new_client = httpx.AsyncClient(base_url=billing_url, timeout=10.0, headers=headers)
|
|
137
|
+
request.app.state.billing_client = new_client
|
|
138
|
+
client: httpx.AsyncClient = request.app.state.billing_client
|
|
139
|
+
return client
|
|
140
|
+
elif google_id_token:
|
|
141
|
+
# JWT pass-through mode: create new client with Google ID token as Bearer
|
|
142
|
+
# Don't cache this client since token changes per request
|
|
143
|
+
headers = {
|
|
144
|
+
"Authorization": f"Bearer {google_id_token}",
|
|
145
|
+
"User-Agent": "CIRIS-Mobile/1.0",
|
|
146
|
+
}
|
|
147
|
+
logger.info(
|
|
148
|
+
f"[BILLING_JWT] Creating JWT pass-through client with Google ID token ({len(google_id_token)} chars)"
|
|
149
|
+
)
|
|
150
|
+
return httpx.AsyncClient(base_url=billing_url, timeout=10.0, headers=headers)
|
|
151
|
+
else:
|
|
152
|
+
raise HTTPException(
|
|
153
|
+
status_code=500,
|
|
154
|
+
detail="Billing not configured: set CIRIS_BILLING_API_KEY or provide X-Google-ID-Token header",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _extract_user_identity(auth: AuthContext, request: Request) -> JSONDict:
|
|
159
|
+
"""Extract user identity from auth context including marketing opt-in preference and email."""
|
|
160
|
+
# Extract user information from auth service
|
|
161
|
+
marketing_opt_in = False
|
|
162
|
+
user_email = None
|
|
163
|
+
oauth_provider = None
|
|
164
|
+
external_id = None
|
|
165
|
+
|
|
166
|
+
if hasattr(request.app.state, "auth_service") and request.app.state.auth_service is not None:
|
|
167
|
+
auth_service = request.app.state.auth_service
|
|
168
|
+
user = auth_service.get_user(auth.user_id)
|
|
169
|
+
if user:
|
|
170
|
+
marketing_opt_in = user.marketing_opt_in
|
|
171
|
+
user_email = user.oauth_email
|
|
172
|
+
# Get OAuth provider and external_id from user object (stored in database)
|
|
173
|
+
if user.oauth_provider and user.oauth_external_id:
|
|
174
|
+
oauth_provider = user.oauth_provider
|
|
175
|
+
external_id = user.oauth_external_id
|
|
176
|
+
|
|
177
|
+
# Fallback: Try to parse from user_id format (e.g., "google:115300315355793131383")
|
|
178
|
+
if not oauth_provider or not external_id:
|
|
179
|
+
if ":" in auth.user_id and not auth.user_id.startswith("wa-"):
|
|
180
|
+
parts = auth.user_id.split(":", 1)
|
|
181
|
+
oauth_provider = parts[0] # e.g., "google", "discord"
|
|
182
|
+
external_id = parts[1] # e.g., "115300315355793131383"
|
|
183
|
+
else:
|
|
184
|
+
# Internal/API user without OAuth
|
|
185
|
+
oauth_provider = "api:internal"
|
|
186
|
+
external_id = auth.user_id
|
|
187
|
+
|
|
188
|
+
# Format oauth_provider with "oauth:" prefix for billing backend
|
|
189
|
+
if not oauth_provider.startswith("oauth:"):
|
|
190
|
+
oauth_provider = f"oauth:{oauth_provider}"
|
|
191
|
+
|
|
192
|
+
identity = {
|
|
193
|
+
"oauth_provider": oauth_provider,
|
|
194
|
+
"external_id": external_id,
|
|
195
|
+
"wa_id": auth.user_id,
|
|
196
|
+
"tenant_id": None,
|
|
197
|
+
"marketing_opt_in": marketing_opt_in,
|
|
198
|
+
"customer_email": user_email, # CRITICAL: Never use fallback - let validation catch missing email
|
|
199
|
+
"user_role": auth.role.value.lower(), # Use actual user role from auth context
|
|
200
|
+
}
|
|
201
|
+
logger.debug(
|
|
202
|
+
f"[BILLING_IDENTITY] Extracted identity: has_provider={oauth_provider is not None}, "
|
|
203
|
+
f"has_external_id={external_id is not None}, has_email={user_email is not None}, "
|
|
204
|
+
f"marketing_opt_in={marketing_opt_in}"
|
|
205
|
+
)
|
|
206
|
+
return identity
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# Endpoints
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _get_unlimited_credit_response() -> CreditStatusResponse:
|
|
213
|
+
"""Return unlimited credit response when no credit provider configured."""
|
|
214
|
+
return CreditStatusResponse(
|
|
215
|
+
has_credit=True,
|
|
216
|
+
credits_remaining=999,
|
|
217
|
+
free_uses_remaining=999,
|
|
218
|
+
total_uses=0,
|
|
219
|
+
plan_name="unlimited",
|
|
220
|
+
purchase_required=False,
|
|
221
|
+
purchase_options=None,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _get_simple_provider_response(has_credit: bool) -> CreditStatusResponse:
|
|
226
|
+
"""Return credit response for SimpleCreditProvider (1 free use)."""
|
|
227
|
+
if has_credit:
|
|
228
|
+
# Still have free credit
|
|
229
|
+
return CreditStatusResponse(
|
|
230
|
+
has_credit=True,
|
|
231
|
+
credits_remaining=0,
|
|
232
|
+
free_uses_remaining=1,
|
|
233
|
+
total_uses=0,
|
|
234
|
+
plan_name="free",
|
|
235
|
+
purchase_required=False,
|
|
236
|
+
purchase_options=None,
|
|
237
|
+
)
|
|
238
|
+
else:
|
|
239
|
+
# Free credit exhausted, billing not enabled
|
|
240
|
+
return CreditStatusResponse(
|
|
241
|
+
has_credit=False,
|
|
242
|
+
credits_remaining=0,
|
|
243
|
+
free_uses_remaining=0,
|
|
244
|
+
total_uses=1,
|
|
245
|
+
plan_name="free",
|
|
246
|
+
purchase_required=False, # Can't purchase when billing disabled
|
|
247
|
+
purchase_options={
|
|
248
|
+
"price_minor": 0,
|
|
249
|
+
"uses": 0,
|
|
250
|
+
"currency": "USD",
|
|
251
|
+
"message": "Contact administrator to enable billing",
|
|
252
|
+
},
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _build_credit_check_payload(user_identity: JSONDict, context: Any) -> JSONDict:
|
|
257
|
+
"""Build payload for billing backend credit check."""
|
|
258
|
+
check_payload = {
|
|
259
|
+
"oauth_provider": user_identity["oauth_provider"],
|
|
260
|
+
"external_id": user_identity["external_id"],
|
|
261
|
+
}
|
|
262
|
+
if user_identity.get("wa_id"):
|
|
263
|
+
check_payload["wa_id"] = user_identity["wa_id"]
|
|
264
|
+
if user_identity.get("tenant_id"):
|
|
265
|
+
check_payload["tenant_id"] = user_identity["tenant_id"]
|
|
266
|
+
|
|
267
|
+
# Add agent_id at top level (not in context)
|
|
268
|
+
check_payload["agent_id"] = context.agent_id
|
|
269
|
+
|
|
270
|
+
# Add minimal context
|
|
271
|
+
check_payload["context"] = {
|
|
272
|
+
"channel_id": context.channel_id,
|
|
273
|
+
"request_id": context.request_id,
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return check_payload
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
async def _query_billing_backend(billing_client: httpx.AsyncClient, check_payload: JSONDict) -> JSONDict:
|
|
280
|
+
"""Query billing backend for credit status."""
|
|
281
|
+
try:
|
|
282
|
+
response = await billing_client.post(
|
|
283
|
+
"/v1/billing/credits/check",
|
|
284
|
+
json=check_payload,
|
|
285
|
+
)
|
|
286
|
+
response.raise_for_status()
|
|
287
|
+
result: JSONDict = response.json()
|
|
288
|
+
return result
|
|
289
|
+
|
|
290
|
+
except (httpx.HTTPStatusError, httpx.RequestError) as e:
|
|
291
|
+
logger.error(f"Billing API error: {e}", exc_info=True)
|
|
292
|
+
raise HTTPException(status_code=503, detail=ERROR_BILLING_SERVICE_UNAVAILABLE)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _format_billing_response(credit_data: JSONDict) -> CreditStatusResponse:
|
|
296
|
+
"""Format billing backend response for frontend."""
|
|
297
|
+
purchase_options = None
|
|
298
|
+
if credit_data.get("purchase_required"):
|
|
299
|
+
purchase_options = {
|
|
300
|
+
"price_minor": credit_data.get("purchase_price_minor"),
|
|
301
|
+
"uses": credit_data.get("purchase_uses"),
|
|
302
|
+
"currency": "USD",
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return CreditStatusResponse(
|
|
306
|
+
has_credit=credit_data["has_credit"],
|
|
307
|
+
credits_remaining=credit_data.get("credits_remaining", 0),
|
|
308
|
+
free_uses_remaining=credit_data.get("free_uses_remaining", 0),
|
|
309
|
+
daily_free_uses_remaining=credit_data.get("daily_free_uses_remaining", 0),
|
|
310
|
+
total_uses=credit_data.get("total_uses", 0),
|
|
311
|
+
plan_name=credit_data.get("plan_name"),
|
|
312
|
+
purchase_required=credit_data.get("purchase_required", False),
|
|
313
|
+
purchase_options=purchase_options,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _get_agent_id(request: Request) -> str:
|
|
318
|
+
"""Extract agent_id from request runtime."""
|
|
319
|
+
if hasattr(request.app.state, "runtime") and request.app.state.runtime.agent_identity:
|
|
320
|
+
agent_id: str = request.app.state.runtime.agent_identity.agent_id
|
|
321
|
+
return agent_id
|
|
322
|
+
return "pending"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _get_credit_provider(request: Request) -> Optional[Any]:
|
|
326
|
+
"""Get credit provider from resource monitor, lazily initializing if token is available.
|
|
327
|
+
|
|
328
|
+
This enables the billing provider to be created when:
|
|
329
|
+
1. Server starts without token
|
|
330
|
+
2. User logs in (Kotlin writes token to .env)
|
|
331
|
+
3. Next API call triggers lazy initialization
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Credit provider instance or None if unavailable and no token to initialize.
|
|
335
|
+
"""
|
|
336
|
+
if not hasattr(request.app.state, "resource_monitor"):
|
|
337
|
+
return None
|
|
338
|
+
resource_monitor = request.app.state.resource_monitor
|
|
339
|
+
if not hasattr(resource_monitor, "credit_provider"):
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
# Return existing provider if available
|
|
343
|
+
if resource_monitor.credit_provider is not None:
|
|
344
|
+
return resource_monitor.credit_provider
|
|
345
|
+
|
|
346
|
+
# Lazy initialization: Try to create billing provider if token is now available
|
|
347
|
+
return _try_lazy_init_billing_provider(request, resource_monitor)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _try_lazy_init_billing_provider(request: Request, resource_monitor: Any) -> Optional[Any]:
|
|
351
|
+
"""Attempt to lazily initialize the billing provider if a token is now available.
|
|
352
|
+
|
|
353
|
+
This handles the case where the Python server starts before the user logs in,
|
|
354
|
+
and the token is written to .env after server startup.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
request: FastAPI request with app state
|
|
358
|
+
resource_monitor: Resource monitor instance to attach provider to
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Newly created billing provider or None if initialization fails
|
|
362
|
+
"""
|
|
363
|
+
import os
|
|
364
|
+
|
|
365
|
+
from dotenv import load_dotenv
|
|
366
|
+
|
|
367
|
+
from ciris_engine.logic.services.infrastructure.resource_monitor.ciris_billing_provider import CIRISBillingProvider
|
|
368
|
+
|
|
369
|
+
# Reload .env to pick up any new values written by Kotlin
|
|
370
|
+
ciris_home = os.environ.get("CIRIS_HOME", "")
|
|
371
|
+
if ciris_home:
|
|
372
|
+
env_path = os.path.join(ciris_home, ".env")
|
|
373
|
+
if os.path.exists(env_path):
|
|
374
|
+
load_dotenv(env_path, override=True)
|
|
375
|
+
logger.debug("[BILLING_LAZY_INIT] Reloaded .env from %s", env_path)
|
|
376
|
+
|
|
377
|
+
# Check for Google ID token (written by Kotlin when user logs in)
|
|
378
|
+
google_token = os.environ.get("CIRIS_BILLING_GOOGLE_ID_TOKEN", "")
|
|
379
|
+
if not google_token:
|
|
380
|
+
logger.debug("[BILLING_LAZY_INIT] No CIRIS_BILLING_GOOGLE_ID_TOKEN in environment")
|
|
381
|
+
return None
|
|
382
|
+
|
|
383
|
+
# Get billing URL from central config (checks env var first)
|
|
384
|
+
billing_url = get_billing_url()
|
|
385
|
+
|
|
386
|
+
logger.info(
|
|
387
|
+
"[BILLING_LAZY_INIT] Token found (%d chars), creating CIRISBillingProvider...",
|
|
388
|
+
len(google_token),
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
# Create the billing provider with JWT auth
|
|
393
|
+
provider = CIRISBillingProvider(
|
|
394
|
+
google_id_token=google_token,
|
|
395
|
+
base_url=billing_url,
|
|
396
|
+
fail_open=False, # Don't fail open - we want accurate billing status
|
|
397
|
+
cache_ttl_seconds=15,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
# Attach to resource monitor
|
|
401
|
+
resource_monitor.credit_provider = provider
|
|
402
|
+
logger.info(
|
|
403
|
+
"[BILLING_LAZY_INIT] Successfully created CIRISBillingProvider: base_url=%s, token_length=%d",
|
|
404
|
+
billing_url,
|
|
405
|
+
len(google_token),
|
|
406
|
+
)
|
|
407
|
+
return provider
|
|
408
|
+
|
|
409
|
+
except Exception as exc:
|
|
410
|
+
logger.error("[BILLING_LAZY_INIT] Failed to create CIRISBillingProvider: %s", exc, exc_info=True)
|
|
411
|
+
return None
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _build_mobile_credit_response(result: Any) -> CreditStatusResponse:
|
|
415
|
+
"""Build credit response for mobile/JWT mode (no API key)."""
|
|
416
|
+
return CreditStatusResponse(
|
|
417
|
+
has_credit=result.has_credit,
|
|
418
|
+
credits_remaining=result.credits_remaining or 0,
|
|
419
|
+
free_uses_remaining=result.free_uses_remaining or 0,
|
|
420
|
+
daily_free_uses_remaining=result.daily_free_uses_remaining or 0,
|
|
421
|
+
total_uses=0,
|
|
422
|
+
plan_name="CIRIS Mobile",
|
|
423
|
+
purchase_required=not result.has_credit,
|
|
424
|
+
purchase_options={"price_minor": 499, "uses": 100, "currency": "USD"} if not result.has_credit else None,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
@router.get("/credits", response_model=CreditStatusResponse)
|
|
429
|
+
async def get_credits(
|
|
430
|
+
request: Request,
|
|
431
|
+
auth: AuthContext = Depends(require_observer),
|
|
432
|
+
) -> CreditStatusResponse:
|
|
433
|
+
"""
|
|
434
|
+
Get user's credit balance and status.
|
|
435
|
+
|
|
436
|
+
Works with both:
|
|
437
|
+
- SimpleCreditProvider (1 free credit per OAuth user, no billing backend needed)
|
|
438
|
+
- CIRISBillingProvider (full billing backend with paid credits)
|
|
439
|
+
|
|
440
|
+
The frontend calls this to display credit status.
|
|
441
|
+
"""
|
|
442
|
+
import os
|
|
443
|
+
|
|
444
|
+
from ciris_engine.logic.adapters.api.routes.agent import _derive_credit_account
|
|
445
|
+
from ciris_engine.schemas.services.credit_gate import CreditContext
|
|
446
|
+
|
|
447
|
+
logger.info("[BILLING_API] get_credits called for user_id=%s", auth.user_id)
|
|
448
|
+
user_identity = _extract_user_identity(auth, request)
|
|
449
|
+
agent_id = _get_agent_id(request)
|
|
450
|
+
logger.info("[BILLING_API] agent_id=%s, user_identity=%s", agent_id, user_identity)
|
|
451
|
+
|
|
452
|
+
# Check credit provider availability
|
|
453
|
+
credit_provider = _get_credit_provider(request)
|
|
454
|
+
if credit_provider is None:
|
|
455
|
+
if not hasattr(request.app.state, "resource_monitor"):
|
|
456
|
+
logger.error("[BILLING_API] No resource_monitor on app.state")
|
|
457
|
+
raise HTTPException(status_code=503, detail=ERROR_RESOURCE_MONITOR_UNAVAILABLE)
|
|
458
|
+
logger.info("[BILLING_API] No credit provider, returning unlimited response")
|
|
459
|
+
return _get_unlimited_credit_response()
|
|
460
|
+
|
|
461
|
+
# Query credit provider
|
|
462
|
+
resource_monitor = request.app.state.resource_monitor
|
|
463
|
+
account, _ = _derive_credit_account(auth, request)
|
|
464
|
+
context = CreditContext(agent_id=agent_id, channel_id="api:frontend", request_id=None)
|
|
465
|
+
|
|
466
|
+
try:
|
|
467
|
+
result = await resource_monitor.check_credit(account, context)
|
|
468
|
+
except Exception as e:
|
|
469
|
+
logger.error("Credit check error: %s", e, exc_info=True)
|
|
470
|
+
raise HTTPException(status_code=503, detail=f"Credit check failed: {type(e).__name__}: {e!s}")
|
|
471
|
+
|
|
472
|
+
# Handle SimpleCreditProvider
|
|
473
|
+
if credit_provider.__class__.__name__ == "SimpleCreditProvider":
|
|
474
|
+
return _get_simple_provider_response(result.has_credit)
|
|
475
|
+
|
|
476
|
+
# CIRISBillingProvider: mobile mode (no API key) or server mode
|
|
477
|
+
if not os.getenv("CIRIS_BILLING_API_KEY"):
|
|
478
|
+
logger.info(
|
|
479
|
+
"[BILLING_CREDITS] Using CreditCheckResult (no API key): " "free=%s, paid=%s, has_credit=%s",
|
|
480
|
+
result.free_uses_remaining,
|
|
481
|
+
result.credits_remaining,
|
|
482
|
+
result.has_credit,
|
|
483
|
+
)
|
|
484
|
+
return _build_mobile_credit_response(result)
|
|
485
|
+
|
|
486
|
+
# Server mode with API key - query billing backend
|
|
487
|
+
billing_client = _get_billing_client(request)
|
|
488
|
+
credit_data = await _query_billing_backend(billing_client, _build_credit_check_payload(user_identity, context))
|
|
489
|
+
response = _format_billing_response(credit_data)
|
|
490
|
+
logger.info(
|
|
491
|
+
"[BILLING_CREDITS] Credit check complete: free=%s, paid=%s, has_credit=%s",
|
|
492
|
+
response.free_uses_remaining,
|
|
493
|
+
response.credits_remaining,
|
|
494
|
+
response.has_credit,
|
|
495
|
+
)
|
|
496
|
+
return response
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@router.post("/purchase/initiate", response_model=PurchaseInitiateResponse)
|
|
500
|
+
async def initiate_purchase(
|
|
501
|
+
request: Request,
|
|
502
|
+
body: PurchaseInitiateRequest,
|
|
503
|
+
auth: AuthContext = Depends(require_observer),
|
|
504
|
+
) -> PurchaseInitiateResponse:
|
|
505
|
+
"""
|
|
506
|
+
Initiate credit purchase (creates Stripe payment intent).
|
|
507
|
+
|
|
508
|
+
Only works when CIRIS_BILLING_ENABLED=true (CIRISBillingProvider).
|
|
509
|
+
Returns error when SimpleCreditProvider is active (billing disabled).
|
|
510
|
+
"""
|
|
511
|
+
# Check if billing is enabled (CIRISBillingProvider vs SimpleCreditProvider)
|
|
512
|
+
if not hasattr(request.app.state, "resource_monitor"):
|
|
513
|
+
raise HTTPException(status_code=503, detail=ERROR_RESOURCE_MONITOR_UNAVAILABLE)
|
|
514
|
+
|
|
515
|
+
resource_monitor = request.app.state.resource_monitor
|
|
516
|
+
|
|
517
|
+
if not hasattr(resource_monitor, "credit_provider") or resource_monitor.credit_provider is None:
|
|
518
|
+
raise HTTPException(status_code=503, detail=ERROR_CREDIT_PROVIDER_NOT_CONFIGURED)
|
|
519
|
+
|
|
520
|
+
is_simple_provider = resource_monitor.credit_provider.__class__.__name__ == "SimpleCreditProvider"
|
|
521
|
+
|
|
522
|
+
if is_simple_provider:
|
|
523
|
+
# Billing not enabled - can't purchase
|
|
524
|
+
raise HTTPException(
|
|
525
|
+
status_code=403,
|
|
526
|
+
detail="Billing not enabled. Contact administrator to enable paid credits.",
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
# Billing enabled - proceed with purchase
|
|
530
|
+
billing_client = _get_billing_client(request)
|
|
531
|
+
user_identity = _extract_user_identity(auth, request)
|
|
532
|
+
agent_id = (
|
|
533
|
+
request.app.state.runtime.agent_identity.agent_id
|
|
534
|
+
if hasattr(request.app.state, "runtime") and request.app.state.runtime.agent_identity
|
|
535
|
+
else "pending"
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
# Get user email (needed for Stripe) - extract from OAuth profile
|
|
539
|
+
customer_email = user_identity.get("customer_email")
|
|
540
|
+
logger.debug(f"Purchase initiate for user_id={auth.user_id} on agent {agent_id}")
|
|
541
|
+
if not customer_email:
|
|
542
|
+
raise HTTPException(
|
|
543
|
+
status_code=400,
|
|
544
|
+
detail="Email address required for purchase. Please authenticate with OAuth provider.",
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
try:
|
|
548
|
+
# Create payment intent via billing backend
|
|
549
|
+
response = await billing_client.post(
|
|
550
|
+
"/v1/billing/purchases",
|
|
551
|
+
json={
|
|
552
|
+
**user_identity,
|
|
553
|
+
"customer_email": customer_email,
|
|
554
|
+
"return_url": body.return_url,
|
|
555
|
+
},
|
|
556
|
+
)
|
|
557
|
+
response.raise_for_status()
|
|
558
|
+
purchase_data = response.json()
|
|
559
|
+
|
|
560
|
+
except httpx.HTTPStatusError as e:
|
|
561
|
+
logger.error(f"Billing API error: {e.response.status_code} - {e.response.text}")
|
|
562
|
+
if e.response.status_code == 400:
|
|
563
|
+
raise HTTPException(status_code=400, detail="Invalid purchase request")
|
|
564
|
+
raise HTTPException(status_code=503, detail="Billing service unavailable")
|
|
565
|
+
except httpx.RequestError as e:
|
|
566
|
+
logger.error(f"Billing API request error: {e}")
|
|
567
|
+
raise HTTPException(status_code=503, detail="Cannot reach billing service")
|
|
568
|
+
|
|
569
|
+
# Get Stripe publishable key from billing backend response (single source of truth)
|
|
570
|
+
publishable_key = purchase_data.get("publishable_key", "pk_test_not_configured")
|
|
571
|
+
|
|
572
|
+
return PurchaseInitiateResponse(
|
|
573
|
+
payment_id=purchase_data["payment_id"],
|
|
574
|
+
client_secret=purchase_data["client_secret"],
|
|
575
|
+
amount_minor=purchase_data["amount_minor"],
|
|
576
|
+
currency=purchase_data["currency"],
|
|
577
|
+
uses_purchased=purchase_data["uses_purchased"],
|
|
578
|
+
publishable_key=publishable_key,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
@router.get("/purchase/status/{payment_id}", response_model=PurchaseStatusResponse)
|
|
583
|
+
async def get_purchase_status(
|
|
584
|
+
payment_id: str,
|
|
585
|
+
request: Request,
|
|
586
|
+
auth: AuthContext = Depends(require_observer),
|
|
587
|
+
) -> PurchaseStatusResponse:
|
|
588
|
+
"""
|
|
589
|
+
Check payment status (optional - for polling after payment).
|
|
590
|
+
|
|
591
|
+
Frontend can poll this after initiating payment to confirm credits were added.
|
|
592
|
+
Only works when CIRIS_BILLING_ENABLED=true (CIRISBillingProvider).
|
|
593
|
+
"""
|
|
594
|
+
# Validate payment_id to prevent path traversal attacks
|
|
595
|
+
if not PAYMENT_ID_PATTERN.match(payment_id):
|
|
596
|
+
raise HTTPException(status_code=400, detail=ERROR_INVALID_PAYMENT_ID)
|
|
597
|
+
|
|
598
|
+
# Check if billing is enabled
|
|
599
|
+
if not hasattr(request.app.state, "resource_monitor"):
|
|
600
|
+
raise HTTPException(status_code=503, detail=ERROR_RESOURCE_MONITOR_UNAVAILABLE)
|
|
601
|
+
|
|
602
|
+
resource_monitor = request.app.state.resource_monitor
|
|
603
|
+
|
|
604
|
+
if not hasattr(resource_monitor, "credit_provider") or resource_monitor.credit_provider is None:
|
|
605
|
+
raise HTTPException(status_code=503, detail=ERROR_CREDIT_PROVIDER_NOT_CONFIGURED)
|
|
606
|
+
|
|
607
|
+
is_simple_provider = resource_monitor.credit_provider.__class__.__name__ == "SimpleCreditProvider"
|
|
608
|
+
|
|
609
|
+
if is_simple_provider:
|
|
610
|
+
# No purchases possible with SimpleCreditProvider
|
|
611
|
+
raise HTTPException(
|
|
612
|
+
status_code=404,
|
|
613
|
+
detail="Payment not found. Billing not enabled.",
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
# Billing enabled - check payment status
|
|
617
|
+
billing_client = _get_billing_client(request)
|
|
618
|
+
user_identity = _extract_user_identity(auth, request)
|
|
619
|
+
|
|
620
|
+
payment_data = None
|
|
621
|
+
credit_data = None
|
|
622
|
+
|
|
623
|
+
try:
|
|
624
|
+
from typing import Mapping, cast
|
|
625
|
+
|
|
626
|
+
# Query billing backend for specific payment status
|
|
627
|
+
# URL-encode payment_id to prevent path traversal (already validated by PAYMENT_ID_PATTERN)
|
|
628
|
+
safe_payment_id = quote(payment_id, safe="")
|
|
629
|
+
payment_response = await billing_client.get(
|
|
630
|
+
f"/v1/billing/purchases/{safe_payment_id}/status",
|
|
631
|
+
params=cast(Mapping[str, str | int | float | bool | None], user_identity),
|
|
632
|
+
)
|
|
633
|
+
payment_response.raise_for_status()
|
|
634
|
+
payment_data = payment_response.json()
|
|
635
|
+
|
|
636
|
+
# Get updated credit balance
|
|
637
|
+
credits_response = await billing_client.post(
|
|
638
|
+
"/v1/billing/credits/check",
|
|
639
|
+
json={
|
|
640
|
+
**user_identity,
|
|
641
|
+
"context": {"source": "purchase_status_check"},
|
|
642
|
+
},
|
|
643
|
+
)
|
|
644
|
+
credits_response.raise_for_status()
|
|
645
|
+
credit_data = credits_response.json()
|
|
646
|
+
|
|
647
|
+
except httpx.HTTPStatusError as e:
|
|
648
|
+
# If payment not found, return pending status
|
|
649
|
+
if e.response.status_code == 404:
|
|
650
|
+
return PurchaseStatusResponse(
|
|
651
|
+
status="pending",
|
|
652
|
+
credits_added=0,
|
|
653
|
+
balance_after=None,
|
|
654
|
+
)
|
|
655
|
+
logger.error(f"Billing API error: {e}")
|
|
656
|
+
raise HTTPException(status_code=503, detail=ERROR_BILLING_SERVICE_UNAVAILABLE)
|
|
657
|
+
except httpx.RequestError as e:
|
|
658
|
+
logger.error(f"Billing API request error: {e}")
|
|
659
|
+
raise HTTPException(status_code=503, detail=ERROR_BILLING_SERVICE_UNAVAILABLE)
|
|
660
|
+
|
|
661
|
+
# Extract payment status and amount from billing backend response
|
|
662
|
+
payment_status = payment_data.get("status", "unknown")
|
|
663
|
+
credits_added = payment_data.get("credits_added", 0)
|
|
664
|
+
|
|
665
|
+
return PurchaseStatusResponse(
|
|
666
|
+
status=payment_status,
|
|
667
|
+
credits_added=credits_added,
|
|
668
|
+
balance_after=credit_data.get("credits_remaining"),
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
@router.get("/transactions", response_model=TransactionListResponse)
|
|
673
|
+
async def get_transactions(
|
|
674
|
+
request: Request,
|
|
675
|
+
auth: AuthContext = Depends(require_observer),
|
|
676
|
+
limit: int = 50,
|
|
677
|
+
offset: int = 0,
|
|
678
|
+
) -> TransactionListResponse:
|
|
679
|
+
"""
|
|
680
|
+
Get transaction history for the current user.
|
|
681
|
+
|
|
682
|
+
Returns a paginated list of all transactions (charges and credits) in reverse chronological order.
|
|
683
|
+
|
|
684
|
+
Only works when CIRIS_BILLING_ENABLED=true (CIRISBillingProvider).
|
|
685
|
+
Returns empty list when SimpleCreditProvider is active (billing disabled).
|
|
686
|
+
"""
|
|
687
|
+
# Check if billing is enabled (CIRISBillingProvider vs SimpleCreditProvider)
|
|
688
|
+
if not hasattr(request.app.state, "resource_monitor"):
|
|
689
|
+
raise HTTPException(status_code=503, detail=ERROR_RESOURCE_MONITOR_UNAVAILABLE)
|
|
690
|
+
|
|
691
|
+
resource_monitor = request.app.state.resource_monitor
|
|
692
|
+
|
|
693
|
+
if not hasattr(resource_monitor, "credit_provider") or resource_monitor.credit_provider is None:
|
|
694
|
+
# No credit provider - return empty list
|
|
695
|
+
return TransactionListResponse(transactions=[], total_count=0, has_more=False)
|
|
696
|
+
|
|
697
|
+
is_simple_provider = resource_monitor.credit_provider.__class__.__name__ == "SimpleCreditProvider"
|
|
698
|
+
|
|
699
|
+
if is_simple_provider:
|
|
700
|
+
# SimpleCreditProvider doesn't track transactions - return empty list
|
|
701
|
+
return TransactionListResponse(transactions=[], total_count=0, has_more=False)
|
|
702
|
+
|
|
703
|
+
# CIRISBillingProvider - query billing backend for transaction history
|
|
704
|
+
logger.info(f"[BILLING_TRANSACTIONS] Fetching transactions (limit={limit}, offset={offset})")
|
|
705
|
+
billing_client = _get_billing_client(request)
|
|
706
|
+
user_identity = _extract_user_identity(auth, request)
|
|
707
|
+
|
|
708
|
+
try:
|
|
709
|
+
from typing import Mapping, cast
|
|
710
|
+
|
|
711
|
+
# Build query parameters for billing backend - cast to expected types
|
|
712
|
+
oauth_provider = str(user_identity["oauth_provider"])
|
|
713
|
+
external_id = str(user_identity["external_id"])
|
|
714
|
+
|
|
715
|
+
params: dict[str, str | int] = {
|
|
716
|
+
"oauth_provider": oauth_provider,
|
|
717
|
+
"external_id": external_id,
|
|
718
|
+
"limit": limit,
|
|
719
|
+
"offset": offset,
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
# Add optional parameters if present
|
|
723
|
+
wa_id = user_identity.get("wa_id")
|
|
724
|
+
if wa_id:
|
|
725
|
+
params["wa_id"] = str(wa_id)
|
|
726
|
+
tenant_id = user_identity.get("tenant_id")
|
|
727
|
+
if tenant_id:
|
|
728
|
+
params["tenant_id"] = str(tenant_id)
|
|
729
|
+
|
|
730
|
+
# Log request details for debugging (without PII)
|
|
731
|
+
logger.debug(
|
|
732
|
+
f"[BILLING_TRANSACTIONS] Request to billing backend: "
|
|
733
|
+
f"oauth_provider={params.get('oauth_provider')}, "
|
|
734
|
+
f"external_id={params.get('external_id')}, "
|
|
735
|
+
f"wa_id={params.get('wa_id')}, "
|
|
736
|
+
f"has_email={user_identity.get('customer_email') is not None}"
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
# Query billing backend
|
|
740
|
+
response = await billing_client.get(
|
|
741
|
+
"/v1/billing/transactions",
|
|
742
|
+
params=cast(Mapping[str, str | int | float | bool | None], params),
|
|
743
|
+
)
|
|
744
|
+
response.raise_for_status()
|
|
745
|
+
transaction_data: JSONDict = response.json()
|
|
746
|
+
|
|
747
|
+
# Map backend response to our schema - safely extract and validate transactions list
|
|
748
|
+
transactions_raw = transaction_data.get("transactions", [])
|
|
749
|
+
if not isinstance(transactions_raw, list):
|
|
750
|
+
transactions_raw = []
|
|
751
|
+
transactions = [TransactionItem(**txn) for txn in transactions_raw if isinstance(txn, dict)]
|
|
752
|
+
|
|
753
|
+
logger.info(
|
|
754
|
+
f"[BILLING_TRANSACTIONS] Returning {len(transactions)} transactions "
|
|
755
|
+
f"(total={transaction_data.get('total_count', 0)}, has_more={transaction_data.get('has_more', False)})"
|
|
756
|
+
)
|
|
757
|
+
return TransactionListResponse(
|
|
758
|
+
transactions=transactions,
|
|
759
|
+
total_count=transaction_data.get("total_count", 0),
|
|
760
|
+
has_more=transaction_data.get("has_more", False),
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
except httpx.HTTPStatusError as e:
|
|
764
|
+
# Safely extract request details for logging
|
|
765
|
+
try:
|
|
766
|
+
headers_str = str(dict(e.request.headers))
|
|
767
|
+
except (TypeError, AttributeError):
|
|
768
|
+
headers_str = "<unavailable>"
|
|
769
|
+
|
|
770
|
+
logger.error(
|
|
771
|
+
f"Billing API error fetching transactions: {e.response.status_code} - {e.response.text}\n"
|
|
772
|
+
f"Request URL: {e.request.url}\n"
|
|
773
|
+
f"Request headers: {headers_str}"
|
|
774
|
+
)
|
|
775
|
+
if e.response.status_code == 404:
|
|
776
|
+
# Account not found - return empty list
|
|
777
|
+
return TransactionListResponse(transactions=[], total_count=0, has_more=False)
|
|
778
|
+
if e.response.status_code == 401:
|
|
779
|
+
# Authentication failed - log details and return empty
|
|
780
|
+
logger.error("401 Unauthorized - API key may be invalid or missing")
|
|
781
|
+
return TransactionListResponse(transactions=[], total_count=0, has_more=False)
|
|
782
|
+
raise HTTPException(status_code=503, detail=ERROR_BILLING_SERVICE_UNAVAILABLE)
|
|
783
|
+
except httpx.RequestError as e:
|
|
784
|
+
logger.error(f"Billing API request error: {e}")
|
|
785
|
+
raise HTTPException(status_code=503, detail=ERROR_BILLING_SERVICE_UNAVAILABLE)
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
# Google Play verification models
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
class GooglePlayVerifyRequest(BaseModel):
|
|
792
|
+
"""Request to verify a Google Play purchase."""
|
|
793
|
+
|
|
794
|
+
purchase_token: str = Field(..., description="Google Play purchase token")
|
|
795
|
+
product_id: str = Field(..., description="Product SKU (e.g., 'credits_100')")
|
|
796
|
+
package_name: str = Field(..., description="App package name")
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
class GooglePlayVerifyResponse(BaseModel):
|
|
800
|
+
"""Response from Google Play purchase verification."""
|
|
801
|
+
|
|
802
|
+
success: bool = Field(..., description="Whether verification succeeded")
|
|
803
|
+
credits_added: int = Field(0, description="Credits added from this purchase")
|
|
804
|
+
new_balance: int = Field(0, description="New credit balance after purchase")
|
|
805
|
+
already_processed: bool = Field(False, description="Whether purchase was already processed")
|
|
806
|
+
error: Optional[str] = Field(None, description="Error message if verification failed")
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
@router.post("/google-play/verify", response_model=GooglePlayVerifyResponse)
|
|
810
|
+
async def verify_google_play_purchase(
|
|
811
|
+
request: Request,
|
|
812
|
+
body: GooglePlayVerifyRequest,
|
|
813
|
+
auth: AuthContext = Depends(require_observer),
|
|
814
|
+
) -> GooglePlayVerifyResponse:
|
|
815
|
+
"""
|
|
816
|
+
Verify a Google Play purchase and add credits.
|
|
817
|
+
|
|
818
|
+
This endpoint proxies the verification request to the billing backend,
|
|
819
|
+
which validates the purchase token with Google Play and adds credits.
|
|
820
|
+
|
|
821
|
+
Supports two authentication modes:
|
|
822
|
+
1. Server mode: Uses CIRIS_BILLING_API_KEY (agents.ciris.ai)
|
|
823
|
+
2. JWT pass-through: Uses Bearer token from request (Android/native)
|
|
824
|
+
|
|
825
|
+
Only works when CIRISBillingProvider is configured.
|
|
826
|
+
"""
|
|
827
|
+
logger.info(f"[GOOGLE_PLAY_VERIFY] Verifying purchase for user_id={auth.user_id}, product={body.product_id}")
|
|
828
|
+
|
|
829
|
+
# Check if billing is enabled
|
|
830
|
+
if not hasattr(request.app.state, "resource_monitor"):
|
|
831
|
+
return GooglePlayVerifyResponse(success=False, error=ERROR_RESOURCE_MONITOR_UNAVAILABLE)
|
|
832
|
+
|
|
833
|
+
resource_monitor = request.app.state.resource_monitor
|
|
834
|
+
|
|
835
|
+
if not hasattr(resource_monitor, "credit_provider") or resource_monitor.credit_provider is None:
|
|
836
|
+
return GooglePlayVerifyResponse(success=False, error=ERROR_CREDIT_PROVIDER_NOT_CONFIGURED)
|
|
837
|
+
|
|
838
|
+
is_simple_provider = resource_monitor.credit_provider.__class__.__name__ == "SimpleCreditProvider"
|
|
839
|
+
|
|
840
|
+
if is_simple_provider:
|
|
841
|
+
return GooglePlayVerifyResponse(
|
|
842
|
+
success=False, error="Google Play purchases not supported - billing backend not configured"
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
# Extract user identity for billing backend
|
|
846
|
+
user_identity = _extract_user_identity(auth, request)
|
|
847
|
+
|
|
848
|
+
# Build verification request for billing backend
|
|
849
|
+
verify_payload = {
|
|
850
|
+
"oauth_provider": user_identity["oauth_provider"],
|
|
851
|
+
"external_id": user_identity["external_id"],
|
|
852
|
+
"email": user_identity.get("customer_email"),
|
|
853
|
+
"display_name": None, # Not needed for verification
|
|
854
|
+
"purchase_token": body.purchase_token,
|
|
855
|
+
"product_id": body.product_id,
|
|
856
|
+
"package_name": body.package_name,
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
logger.info(
|
|
860
|
+
f"[GOOGLE_PLAY_VERIFY] Sending to billing backend: oauth_provider={verify_payload['oauth_provider']}"
|
|
861
|
+
) # NOSONAR - provider type not secret
|
|
862
|
+
|
|
863
|
+
# Get Google ID token for JWT pass-through mode (Android/native)
|
|
864
|
+
# Android sends this in X-Google-ID-Token header for billing backend auth
|
|
865
|
+
google_id_token = request.headers.get("X-Google-ID-Token")
|
|
866
|
+
if google_id_token:
|
|
867
|
+
logger.info(f"[GOOGLE_PLAY_VERIFY] Using JWT pass-through with Google ID token ({len(google_id_token)} chars)")
|
|
868
|
+
billing_client = _get_billing_client(request, google_id_token=google_id_token)
|
|
869
|
+
|
|
870
|
+
try:
|
|
871
|
+
response = await billing_client.post(
|
|
872
|
+
"/v1/billing/google-play/verify",
|
|
873
|
+
json=verify_payload,
|
|
874
|
+
)
|
|
875
|
+
response.raise_for_status()
|
|
876
|
+
result = response.json()
|
|
877
|
+
|
|
878
|
+
logger.info(
|
|
879
|
+
f"[GOOGLE_PLAY_VERIFY] Success: credits_added={result.get('credits_added')}, "
|
|
880
|
+
f"new_balance={result.get('new_balance')}, already_processed={result.get('already_processed')}"
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
return GooglePlayVerifyResponse(
|
|
884
|
+
success=result.get("success", False),
|
|
885
|
+
credits_added=result.get("credits_added", 0),
|
|
886
|
+
new_balance=result.get("new_balance", 0),
|
|
887
|
+
already_processed=result.get("already_processed", False),
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
except httpx.HTTPStatusError as e:
|
|
891
|
+
logger.error(f"[GOOGLE_PLAY_VERIFY] Billing API error: {e.response.status_code} - {e.response.text}")
|
|
892
|
+
return GooglePlayVerifyResponse(success=False, error=f"Verification failed: {e.response.status_code}")
|
|
893
|
+
except httpx.RequestError as e:
|
|
894
|
+
logger.error(f"[GOOGLE_PLAY_VERIFY] Request error: {e}")
|
|
895
|
+
return GooglePlayVerifyResponse(success=False, error=f"Network error: {str(e)}")
|