keboola-cli 0.63.4__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.
- keboola_agent_cli/__init__.py +34 -0
- keboola_agent_cli/__main__.py +5 -0
- keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js +2 -0
- keboola_agent_cli/_ui_dist/assets/arc-DhFYIddx.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/architecture-7EHR7CIX-hNCijx_H.js +1 -0
- keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js +37 -0
- keboola_agent_cli/_ui_dist/assets/architectureDiagram-3BPJPVTR-C6hUlprM.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js +2 -0
- keboola_agent_cli/_ui_dist/assets/array-BifhSqXX.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js +133 -0
- keboola_agent_cli/_ui_dist/assets/blockDiagram-GPEHLZMM-DC7qY9i4.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js +11 -0
- keboola_agent_cli/_ui_dist/assets/c4Diagram-AAUBKEIU-5Lh44evt.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js +2 -0
- keboola_agent_cli/_ui_dist/assets/channel-DBMrXlxx.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-2J33WTMH-Coy82EBh.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js +63 -0
- keboola_agent_cli/_ui_dist/assets/chunk-3OPIFGDE-BQC5CRHI.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-4BX2VUAB-DUuEt70o.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-55IACEB6-BvR-6chF.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js +3 -0
- keboola_agent_cli/_ui_dist/assets/chunk-5ZQYHXKU-BjcTN7ul.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js +207 -0
- keboola_agent_cli/_ui_dist/assets/chunk-727SXJPM-C0zxqqRN.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js +232 -0
- keboola_agent_cli/_ui_dist/assets/chunk-AQP2D5EJ-CXf7rIlZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-BSJP7CBP-Oj_FO9Q7.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js +124 -0
- keboola_agent_cli/_ui_dist/assets/chunk-CSCIHK7Q-CcTsLrFc.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js +16 -0
- keboola_agent_cli/_ui_dist/assets/chunk-FMBD7UC4-FH-zLkkW.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-L5ZTLDWV-B1Ky_e7O.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-ND2GUHAM-BHz1rpbm.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js +160 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NNHCCRGN-DlpIbxXb.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-NZK2D7GU-tnrSoegS.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js +71 -0
- keboola_agent_cli/_ui_dist/assets/chunk-O5CBEL6O-DxxqDH0l.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js +2 -0
- keboola_agent_cli/_ui_dist/assets/chunk-QZHKN3VN-CSjc2gjj.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js +2 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-4FO5ZUOK-BuZcZu85.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js +2 -0
- keboola_agent_cli/_ui_dist/assets/classDiagram-v2-Q7XG4LA2-BuZcZu85.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js +2 -0
- keboola_agent_cli/_ui_dist/assets/cose-bilkent-S5V4N54A-Y0L8LDMa.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js +322 -0
- keboola_agent_cli/_ui_dist/assets/cytoscape.esm-C8YCVR3_.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js +5 -0
- keboola_agent_cli/_ui_dist/assets/dagre-BM42HDAG-UZ-9BTqF.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js +2 -0
- keboola_agent_cli/_ui_dist/assets/dagre-Bx709z4p.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js +2 -0
- keboola_agent_cli/_ui_dist/assets/defaultLocale-C8Fc0cco.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js +44 -0
- keboola_agent_cli/_ui_dist/assets/diagram-2AECGRRQ-DoDQ60wi.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js +11 -0
- keboola_agent_cli/_ui_dist/assets/diagram-5GNKFQAL-CMGFxpUs.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js +4 -0
- keboola_agent_cli/_ui_dist/assets/diagram-KO2AKTUF-1uGDa-Iu.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js +25 -0
- keboola_agent_cli/_ui_dist/assets/diagram-LMA3HP47-XtFH7B51.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js +25 -0
- keboola_agent_cli/_ui_dist/assets/diagram-OG6HWLK6-B4_Te1T5.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js +2 -0
- keboola_agent_cli/_ui_dist/assets/dist-Di6zmlv0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js +86 -0
- keboola_agent_cli/_ui_dist/assets/erDiagram-TEJ5UH35-NjQkrdFt.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/eventmodeling-FCH6USID-BrJMIks8.js +1 -0
- keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js +163 -0
- keboola_agent_cli/_ui_dist/assets/flowDiagram-I6XJVG4X-CIr8DWl7.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js +293 -0
- keboola_agent_cli/_ui_dist/assets/ganttDiagram-6RSMTGT7-C1VY_xbQ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/gitGraph-WXDBUCRP-COacYjo-.js +1 -0
- keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js +107 -0
- keboola_agent_cli/_ui_dist/assets/gitGraphDiagram-PVQCEYII-DQT8-kg2.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js +2 -0
- keboola_agent_cli/_ui_dist/assets/graphlib-B8gBHxth.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/index-CMq50kkV.css +1 -0
- keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js +118 -0
- keboola_agent_cli/_ui_dist/assets/index-D8W97DAz.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/info-J43DQDTF-DdCTRIzU.js +1 -0
- keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js +3 -0
- keboola_agent_cli/_ui_dist/assets/infoDiagram-5YYISTIA-C77rsoTp.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js +2 -0
- keboola_agent_cli/_ui_dist/assets/init-D6jRqBbL.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js +71 -0
- keboola_agent_cli/_ui_dist/assets/ishikawaDiagram-YF4QCWOH-BcTbXaLy.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js +140 -0
- keboola_agent_cli/_ui_dist/assets/journeyDiagram-JHISSGLW-BejeAJQ_.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js +90 -0
- keboola_agent_cli/_ui_dist/assets/kanban-definition-UN3LZRKU-BRNz_UrH.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js +258 -0
- keboola_agent_cli/_ui_dist/assets/katex-C4eR7coU.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js +2 -0
- keboola_agent_cli/_ui_dist/assets/line-CzAQKFbJ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js +2 -0
- keboola_agent_cli/_ui_dist/assets/linear-DUNFFdck.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js +5 -0
- keboola_agent_cli/_ui_dist/assets/mermaid-parser.core-CpuBOkFa.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js +97 -0
- keboola_agent_cli/_ui_dist/assets/mindmap-definition-RKZ34NQL-9EJQNjH0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js +2 -0
- keboola_agent_cli/_ui_dist/assets/ordinal-hYBb2elL.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/packet-YPE3B663-DLiiw_B2.js +1 -0
- keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js +2 -0
- keboola_agent_cli/_ui_dist/assets/path-BWPyau1x.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/pie-LRSECV5Y-CRoO8G1g.js +1 -0
- keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js +31 -0
- keboola_agent_cli/_ui_dist/assets/pieDiagram-4H26LBE5-XH4cy6Cb.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js +8 -0
- keboola_agent_cli/_ui_dist/assets/quadrantDiagram-W4KKPZXB-fdhc93U8.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/radar-GUYGQ44K-DAlLVJHm.js +1 -0
- keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js +85 -0
- keboola_agent_cli/_ui_dist/assets/requirementDiagram-4Y6WPE33-a94eP3R9.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js +2 -0
- keboola_agent_cli/_ui_dist/assets/rough.esm-CSKSodPl.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js +41 -0
- keboola_agent_cli/_ui_dist/assets/sankeyDiagram-5OEKKPKP-jcBa02sp.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js +163 -0
- keboola_agent_cli/_ui_dist/assets/sequenceDiagram-3UESZ5HK-A5-GGM-e.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js +2 -0
- keboola_agent_cli/_ui_dist/assets/src-ZI-V_AF0.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js +2 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-AJRCARHV-BKAA5rqE.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js +2 -0
- keboola_agent_cli/_ui_dist/assets/stateDiagram-v2-BHNVJYJU-DnJwJBsE.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js +121 -0
- keboola_agent_cli/_ui_dist/assets/timeline-definition-PNZ67QCA-Cy39jp8b.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/treeView-BLDUP644-DbLYl23-.js +1 -0
- keboola_agent_cli/_ui_dist/assets/treemap-LRROVOQU-Bp0eGlOt.js +1 -0
- keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js +35 -0
- keboola_agent_cli/_ui_dist/assets/vennDiagram-CIIHVFJN-BGECKubd.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/wardley-L42UT6IY-D4yH4jqS.js +1 -0
- keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js +79 -0
- keboola_agent_cli/_ui_dist/assets/wardleyDiagram-YWT4CUSO-D6XRG3cZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js +8 -0
- keboola_agent_cli/_ui_dist/assets/xychartDiagram-2RQKCTM6-DRre-pfZ.js.map +1 -0
- keboola_agent_cli/_ui_dist/index.html +50 -0
- keboola_agent_cli/ai_client.py +83 -0
- keboola_agent_cli/auto_update.py +550 -0
- keboola_agent_cli/changelog.py +1198 -0
- keboola_agent_cli/cli.py +448 -0
- keboola_agent_cli/client.py +3422 -0
- keboola_agent_cli/commands/__init__.py +0 -0
- keboola_agent_cli/commands/_data_app_git.py +343 -0
- keboola_agent_cli/commands/_helpers.py +377 -0
- keboola_agent_cli/commands/_metadata_input.py +49 -0
- keboola_agent_cli/commands/_semantic_layer_crud.py +632 -0
- keboola_agent_cli/commands/_semantic_layer_helpers.py +44 -0
- keboola_agent_cli/commands/_semantic_layer_reference_data.py +247 -0
- keboola_agent_cli/commands/agent.py +968 -0
- keboola_agent_cli/commands/branch.py +423 -0
- keboola_agent_cli/commands/changelog.py +168 -0
- keboola_agent_cli/commands/component.py +216 -0
- keboola_agent_cli/commands/config.py +2442 -0
- keboola_agent_cli/commands/context.py +1481 -0
- keboola_agent_cli/commands/data_app.py +1279 -0
- keboola_agent_cli/commands/dev_portal.py +584 -0
- keboola_agent_cli/commands/doctor.py +37 -0
- keboola_agent_cli/commands/encrypt.py +145 -0
- keboola_agent_cli/commands/feature.py +311 -0
- keboola_agent_cli/commands/flow.py +948 -0
- keboola_agent_cli/commands/http_client.py +157 -0
- keboola_agent_cli/commands/init.py +279 -0
- keboola_agent_cli/commands/job.py +661 -0
- keboola_agent_cli/commands/kai.py +301 -0
- keboola_agent_cli/commands/lineage.py +1464 -0
- keboola_agent_cli/commands/org.py +292 -0
- keboola_agent_cli/commands/permissions.py +360 -0
- keboola_agent_cli/commands/project.py +1192 -0
- keboola_agent_cli/commands/repl.py +243 -0
- keboola_agent_cli/commands/schedule.py +340 -0
- keboola_agent_cli/commands/search.py +178 -0
- keboola_agent_cli/commands/semantic_layer.py +939 -0
- keboola_agent_cli/commands/serve.py +272 -0
- keboola_agent_cli/commands/sharing.py +340 -0
- keboola_agent_cli/commands/storage.py +2630 -0
- keboola_agent_cli/commands/stream.py +266 -0
- keboola_agent_cli/commands/sync.py +1277 -0
- keboola_agent_cli/commands/tool.py +206 -0
- keboola_agent_cli/commands/version.py +186 -0
- keboola_agent_cli/commands/workspace.py +635 -0
- keboola_agent_cli/config_store.py +582 -0
- keboola_agent_cli/constants.py +528 -0
- keboola_agent_cli/data_science_client.py +342 -0
- keboola_agent_cli/dev_portal_client.py +323 -0
- keboola_agent_cli/errors.py +248 -0
- keboola_agent_cli/http_base.py +315 -0
- keboola_agent_cli/json_utils.py +126 -0
- keboola_agent_cli/lib.py +536 -0
- keboola_agent_cli/manage_client.py +324 -0
- keboola_agent_cli/metastore_client.py +214 -0
- keboola_agent_cli/models.py +427 -0
- keboola_agent_cli/output.py +1084 -0
- keboola_agent_cli/permissions.py +469 -0
- keboola_agent_cli/py.typed +3 -0
- keboola_agent_cli/result_models.py +271 -0
- keboola_agent_cli/server/__init__.py +34 -0
- keboola_agent_cli/server/agent_runner.py +1289 -0
- keboola_agent_cli/server/agents_store.py +325 -0
- keboola_agent_cli/server/app.py +764 -0
- keboola_agent_cli/server/auth.py +117 -0
- keboola_agent_cli/server/dependencies.py +149 -0
- keboola_agent_cli/server/pricing.py +303 -0
- keboola_agent_cli/server/routers/__init__.py +1 -0
- keboola_agent_cli/server/routers/agents.py +616 -0
- keboola_agent_cli/server/routers/ai_chat.py +129 -0
- keboola_agent_cli/server/routers/branches.py +133 -0
- keboola_agent_cli/server/routers/components.py +48 -0
- keboola_agent_cli/server/routers/configs.py +507 -0
- keboola_agent_cli/server/routers/data_apps.py +384 -0
- keboola_agent_cli/server/routers/dev_portal.py +67 -0
- keboola_agent_cli/server/routers/encrypt.py +35 -0
- keboola_agent_cli/server/routers/feature.py +179 -0
- keboola_agent_cli/server/routers/flows.py +204 -0
- keboola_agent_cli/server/routers/health.py +53 -0
- keboola_agent_cli/server/routers/jobs.py +175 -0
- keboola_agent_cli/server/routers/kai.py +80 -0
- keboola_agent_cli/server/routers/lineage.py +226 -0
- keboola_agent_cli/server/routers/mcp.py +70 -0
- keboola_agent_cli/server/routers/members.py +170 -0
- keboola_agent_cli/server/routers/org.py +96 -0
- keboola_agent_cli/server/routers/projects.py +106 -0
- keboola_agent_cli/server/routers/schedules.py +54 -0
- keboola_agent_cli/server/routers/search.py +30 -0
- keboola_agent_cli/server/routers/semantic_layer.py +650 -0
- keboola_agent_cli/server/routers/sharing.py +86 -0
- keboola_agent_cli/server/routers/storage.py +574 -0
- keboola_agent_cli/server/routers/stream.py +100 -0
- keboola_agent_cli/server/routers/workspaces.py +302 -0
- keboola_agent_cli/server/run_broadcaster.py +329 -0
- keboola_agent_cli/server/sse.py +25 -0
- keboola_agent_cli/services/__init__.py +0 -0
- keboola_agent_cli/services/_encryption.py +217 -0
- keboola_agent_cli/services/_semantic_layer_cascade.py +147 -0
- keboola_agent_cli/services/_semantic_layer_crud.py +382 -0
- keboola_agent_cli/services/_semantic_layer_internals.py +1078 -0
- keboola_agent_cli/services/_semantic_layer_lookup.py +181 -0
- keboola_agent_cli/services/_semantic_layer_reference_data.py +217 -0
- keboola_agent_cli/services/_sync_bindings.py +456 -0
- keboola_agent_cli/services/_sync_branch.py +191 -0
- keboola_agent_cli/services/_sync_bulk.py +228 -0
- keboola_agent_cli/services/_sync_clone.py +163 -0
- keboola_agent_cli/services/_sync_models.py +97 -0
- keboola_agent_cli/services/_sync_push_ops.py +369 -0
- keboola_agent_cli/services/_sync_storage.py +376 -0
- keboola_agent_cli/services/_sync_writeback.py +167 -0
- keboola_agent_cli/services/agent_service.py +458 -0
- keboola_agent_cli/services/base.py +175 -0
- keboola_agent_cli/services/branch_service.py +588 -0
- keboola_agent_cli/services/component_service.py +694 -0
- keboola_agent_cli/services/config_service.py +2099 -0
- keboola_agent_cli/services/data_app_git_service.py +224 -0
- keboola_agent_cli/services/data_app_service.py +2082 -0
- keboola_agent_cli/services/deep_lineage_service.py +1322 -0
- keboola_agent_cli/services/dev_portal_service.py +345 -0
- keboola_agent_cli/services/doctor_service.py +445 -0
- keboola_agent_cli/services/encrypt_service.py +87 -0
- keboola_agent_cli/services/feature_service.py +268 -0
- keboola_agent_cli/services/flow_service.py +769 -0
- keboola_agent_cli/services/flow_validation.py +188 -0
- keboola_agent_cli/services/http_forwarder_service.py +236 -0
- keboola_agent_cli/services/job_idempotency_store.py +285 -0
- keboola_agent_cli/services/job_service.py +797 -0
- keboola_agent_cli/services/kai_service.py +367 -0
- keboola_agent_cli/services/lineage_service.py +274 -0
- keboola_agent_cli/services/mcp_service.py +1498 -0
- keboola_agent_cli/services/mcp_transport.py +259 -0
- keboola_agent_cli/services/member_service.py +593 -0
- keboola_agent_cli/services/org_service.py +619 -0
- keboola_agent_cli/services/project_service.py +947 -0
- keboola_agent_cli/services/repo_validate_service.py +767 -0
- keboola_agent_cli/services/schedule_service.py +731 -0
- keboola_agent_cli/services/search_service.py +331 -0
- keboola_agent_cli/services/semantic_layer_service.py +1497 -0
- keboola_agent_cli/services/sharing_service.py +307 -0
- keboola_agent_cli/services/storage_service.py +2524 -0
- keboola_agent_cli/services/stream_service.py +395 -0
- keboola_agent_cli/services/sync_service.py +2244 -0
- keboola_agent_cli/services/variables_service.py +447 -0
- keboola_agent_cli/services/version_service.py +1038 -0
- keboola_agent_cli/services/workspace_service.py +1103 -0
- keboola_agent_cli/stream_client.py +217 -0
- keboola_agent_cli/sync/__init__.py +1 -0
- keboola_agent_cli/sync/branch_mapping.py +174 -0
- keboola_agent_cli/sync/clone.py +211 -0
- keboola_agent_cli/sync/code_extraction.py +655 -0
- keboola_agent_cli/sync/config_format.py +290 -0
- keboola_agent_cli/sync/diff_engine.py +566 -0
- keboola_agent_cli/sync/git_utils.py +93 -0
- keboola_agent_cli/sync/manifest.py +162 -0
- keboola_agent_cli/sync/naming.py +90 -0
- keboola_agent_cli/sync/secrets.py +62 -0
- keboola_agent_cli/sync/sql_split.py +134 -0
- keboola_cli-0.63.4.dist-info/METADATA +308 -0
- keboola_cli-0.63.4.dist-info/RECORD +306 -0
- keboola_cli-0.63.4.dist-info/WHEEL +4 -0
- keboola_cli-0.63.4.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Bearer token auth middleware for kbagent serve.
|
|
2
|
+
|
|
3
|
+
Generates a single token at startup; clients must send it as
|
|
4
|
+
``Authorization: Bearer <token>`` on every request. Public paths
|
|
5
|
+
(``/docs``, ``/openapi.json``, ``/health/auth-info``) are allowed
|
|
6
|
+
without auth so the UI can bootstrap.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hmac
|
|
12
|
+
import logging
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
from fastapi import FastAPI, Request
|
|
16
|
+
from fastapi.responses import JSONResponse
|
|
17
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
18
|
+
from starlette.types import ASGIApp
|
|
19
|
+
|
|
20
|
+
from ..errors import ErrorCode
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
PUBLIC_PATHS: frozenset[str] = frozenset(
|
|
25
|
+
{
|
|
26
|
+
"/docs",
|
|
27
|
+
"/redoc",
|
|
28
|
+
"/openapi.json",
|
|
29
|
+
"/health/ping",
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True, slots=True)
|
|
35
|
+
class AuthSettings:
|
|
36
|
+
"""Bearer-auth configuration for the FastAPI app."""
|
|
37
|
+
|
|
38
|
+
token: str
|
|
39
|
+
header_name: str = "authorization"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BearerAuthMiddleware(BaseHTTPMiddleware):
|
|
43
|
+
"""Reject requests without a matching ``Authorization: Bearer <token>`` header.
|
|
44
|
+
|
|
45
|
+
Token comparison uses :func:`hmac.compare_digest` to mitigate timing attacks.
|
|
46
|
+
Public paths (docs, openapi, ping) are unauthenticated so the UI can
|
|
47
|
+
discover the server without first knowing the token.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, app: ASGIApp, settings: AuthSettings) -> None:
|
|
51
|
+
super().__init__(app)
|
|
52
|
+
self._token = settings.token
|
|
53
|
+
self._header = settings.header_name
|
|
54
|
+
|
|
55
|
+
async def dispatch(self, request: Request, call_next): # type: ignore[override]
|
|
56
|
+
if request.method == "OPTIONS":
|
|
57
|
+
return await call_next(request)
|
|
58
|
+
|
|
59
|
+
path = request.url.path
|
|
60
|
+
if path in PUBLIC_PATHS or path.startswith("/docs") or path.startswith("/redoc"):
|
|
61
|
+
return await call_next(request)
|
|
62
|
+
|
|
63
|
+
# Single-process UI mode (`kbagent serve --ui`): the SPA shell
|
|
64
|
+
# (index.html, /assets/*, favicons, client-side route URLs) is
|
|
65
|
+
# served unauthenticated so the browser can boot. The HttpOnly
|
|
66
|
+
# ``kbagent_session`` cookie set on ``GET /`` then carries auth
|
|
67
|
+
# on every same-origin API call (see the cookie branch below).
|
|
68
|
+
# ``is_ui_public`` is set on app.state by ``_install_ui`` only when
|
|
69
|
+
# ``--ui`` is enabled; in pure-API mode it is absent and this skip
|
|
70
|
+
# never fires.
|
|
71
|
+
is_ui_public = getattr(request.app.state, "is_ui_public", None)
|
|
72
|
+
if callable(is_ui_public) and is_ui_public(request.method, path):
|
|
73
|
+
return await call_next(request)
|
|
74
|
+
|
|
75
|
+
header = request.headers.get(self._header, "")
|
|
76
|
+
scheme, _, value = header.partition(" ")
|
|
77
|
+
# Browser fallback: in single-process UI mode the SPA reaches the
|
|
78
|
+
# API on the same origin via ``credentials: "include"``, so the
|
|
79
|
+
# browser attaches a HttpOnly ``kbagent_session`` cookie set by
|
|
80
|
+
# ``GET /`` (see ``server.__init__._install_ui``). We accept it
|
|
81
|
+
# only when no Authorization header was present, so scripted callers
|
|
82
|
+
# (``kbagent http``, curl, BFF) keep the header path -- the token
|
|
83
|
+
# therefore never lands in URLs / access logs and never in any
|
|
84
|
+
# JS-readable surface.
|
|
85
|
+
if not value:
|
|
86
|
+
cookie_token = request.cookies.get("kbagent_session", "")
|
|
87
|
+
if cookie_token:
|
|
88
|
+
value = cookie_token
|
|
89
|
+
scheme = "bearer"
|
|
90
|
+
if scheme.lower() != "bearer" or not value:
|
|
91
|
+
return JSONResponse(
|
|
92
|
+
status_code=401,
|
|
93
|
+
content={
|
|
94
|
+
"status": "error",
|
|
95
|
+
"error": {
|
|
96
|
+
"code": str(ErrorCode.UNAUTHORIZED),
|
|
97
|
+
"message": "Missing Bearer token. Set Authorization header.",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
if not hmac.compare_digest(value, self._token):
|
|
102
|
+
return JSONResponse(
|
|
103
|
+
status_code=401,
|
|
104
|
+
content={
|
|
105
|
+
"status": "error",
|
|
106
|
+
"error": {
|
|
107
|
+
"code": str(ErrorCode.UNAUTHORIZED),
|
|
108
|
+
"message": "Invalid Bearer token.",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
return await call_next(request)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def install_auth(app: FastAPI, settings: AuthSettings) -> None:
|
|
116
|
+
"""Attach the auth middleware to a FastAPI app."""
|
|
117
|
+
app.add_middleware(BearerAuthMiddleware, settings=settings)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Service registry and FastAPI dependency providers.
|
|
2
|
+
|
|
3
|
+
A single :class:`ServiceRegistry` holds long-lived service instances bound
|
|
4
|
+
to one :class:`ConfigStore`. Endpoints declare what they need via FastAPI
|
|
5
|
+
``Depends(get_<service>)`` helpers — clean DI without rebuilding services
|
|
6
|
+
per request.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
from fastapi import FastAPI, Request
|
|
15
|
+
|
|
16
|
+
from ..config_store import ConfigStore
|
|
17
|
+
from ..dev_portal_client import DeveloperPortalClient
|
|
18
|
+
from ..services.branch_service import BranchService
|
|
19
|
+
from ..services.component_service import ComponentService
|
|
20
|
+
from ..services.config_service import ConfigService
|
|
21
|
+
from ..services.data_app_git_service import DataAppGitService
|
|
22
|
+
from ..services.data_app_service import DataAppService
|
|
23
|
+
from ..services.deep_lineage_service import DeepLineageService
|
|
24
|
+
from ..services.dev_portal_service import DeveloperPortalService
|
|
25
|
+
from ..services.doctor_service import DoctorService
|
|
26
|
+
from ..services.encrypt_service import EncryptService
|
|
27
|
+
from ..services.feature_service import FeatureService
|
|
28
|
+
from ..services.flow_service import FlowService
|
|
29
|
+
from ..services.job_service import JobService
|
|
30
|
+
from ..services.kai_service import KaiService
|
|
31
|
+
from ..services.lineage_service import LineageService
|
|
32
|
+
from ..services.mcp_service import McpService
|
|
33
|
+
from ..services.member_service import MemberService
|
|
34
|
+
from ..services.org_service import OrgService
|
|
35
|
+
from ..services.project_service import ProjectService
|
|
36
|
+
from ..services.repo_validate_service import RepoValidateService
|
|
37
|
+
from ..services.schedule_service import ScheduleService
|
|
38
|
+
from ..services.search_service import SearchService
|
|
39
|
+
from ..services.semantic_layer_service import SemanticLayerService
|
|
40
|
+
from ..services.sharing_service import SharingService
|
|
41
|
+
from ..services.storage_service import StorageService
|
|
42
|
+
from ..services.stream_service import StreamService
|
|
43
|
+
from ..services.sync_service import SyncService
|
|
44
|
+
from ..services.variables_service import VariablesService
|
|
45
|
+
from ..services.version_service import VersionService
|
|
46
|
+
from ..services.workspace_service import WorkspaceService
|
|
47
|
+
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class ServiceRegistry:
|
|
54
|
+
"""Container of long-lived services for the FastAPI app."""
|
|
55
|
+
|
|
56
|
+
config_store: ConfigStore
|
|
57
|
+
# Self-contact info -- the URL + bearer token of the running serve.
|
|
58
|
+
# Injected into AI-agent / CLI subprocess env so `kbagent http` (and
|
|
59
|
+
# any HTTP-aware tool) can call this very server instead of forking
|
|
60
|
+
# a fresh process tree against potentially stale local config.
|
|
61
|
+
# Populated by ``create_app`` once uvicorn binding is known.
|
|
62
|
+
serve_url: str | None = None
|
|
63
|
+
serve_token: str | None = None
|
|
64
|
+
project: ProjectService = field(init=False)
|
|
65
|
+
config: ConfigService = field(init=False)
|
|
66
|
+
component: ComponentService = field(init=False)
|
|
67
|
+
storage: StorageService = field(init=False)
|
|
68
|
+
stream: StreamService = field(init=False)
|
|
69
|
+
job: JobService = field(init=False)
|
|
70
|
+
branch: BranchService = field(init=False)
|
|
71
|
+
workspace: WorkspaceService = field(init=False)
|
|
72
|
+
flow: FlowService = field(init=False)
|
|
73
|
+
schedule: ScheduleService = field(init=False)
|
|
74
|
+
lineage: LineageService = field(init=False)
|
|
75
|
+
deep_lineage: DeepLineageService = field(init=False)
|
|
76
|
+
sharing: SharingService = field(init=False)
|
|
77
|
+
data_app: DataAppService = field(init=False)
|
|
78
|
+
data_app_git: DataAppGitService = field(init=False)
|
|
79
|
+
dev_portal: DeveloperPortalService = field(init=False)
|
|
80
|
+
semantic_layer: SemanticLayerService = field(init=False)
|
|
81
|
+
repo_validate: RepoValidateService = field(init=False)
|
|
82
|
+
mcp: McpService = field(init=False)
|
|
83
|
+
kai: KaiService = field(init=False)
|
|
84
|
+
encrypt: EncryptService = field(init=False)
|
|
85
|
+
search: SearchService = field(init=False)
|
|
86
|
+
org: OrgService = field(init=False)
|
|
87
|
+
member: MemberService = field(init=False)
|
|
88
|
+
feature: FeatureService = field(init=False)
|
|
89
|
+
sync: SyncService = field(init=False)
|
|
90
|
+
variables: VariablesService = field(init=False)
|
|
91
|
+
doctor: DoctorService = field(init=False)
|
|
92
|
+
version: VersionService = field(init=False)
|
|
93
|
+
|
|
94
|
+
def __post_init__(self) -> None:
|
|
95
|
+
cs = self.config_store
|
|
96
|
+
self.project = ProjectService(config_store=cs)
|
|
97
|
+
self.config = ConfigService(config_store=cs)
|
|
98
|
+
self.component = ComponentService(config_store=cs)
|
|
99
|
+
self.storage = StorageService(config_store=cs)
|
|
100
|
+
self.stream = StreamService(config_store=cs)
|
|
101
|
+
self.job = JobService(config_store=cs)
|
|
102
|
+
self.branch = BranchService(config_store=cs)
|
|
103
|
+
self.workspace = WorkspaceService(config_store=cs)
|
|
104
|
+
self.flow = FlowService(config_store=cs)
|
|
105
|
+
self.schedule = ScheduleService(config_store=cs)
|
|
106
|
+
self.lineage = LineageService(config_store=cs)
|
|
107
|
+
self.deep_lineage = DeepLineageService(config_store=cs)
|
|
108
|
+
self.sharing = SharingService(config_store=cs)
|
|
109
|
+
self.data_app = DataAppService(config_store=cs)
|
|
110
|
+
self.data_app_git = DataAppGitService(config_store=cs)
|
|
111
|
+
self.dev_portal = DeveloperPortalService(
|
|
112
|
+
config_store=cs,
|
|
113
|
+
client_factory=lambda identity: DeveloperPortalClient(identity),
|
|
114
|
+
)
|
|
115
|
+
# SemanticLayerService takes both a storage client_factory (for
|
|
116
|
+
# validate --deep + add dataset --deep-fields + build) and an
|
|
117
|
+
# optional metastore_client_factory; the defaults work for both.
|
|
118
|
+
self.semantic_layer = SemanticLayerService(config_store=cs)
|
|
119
|
+
self.repo_validate = RepoValidateService(config_store=cs)
|
|
120
|
+
self.mcp = McpService(config_store=cs)
|
|
121
|
+
self.kai = KaiService(config_store=cs)
|
|
122
|
+
self.encrypt = EncryptService(config_store=cs)
|
|
123
|
+
self.search = SearchService(config_store=cs)
|
|
124
|
+
self.org = OrgService(config_store=cs)
|
|
125
|
+
self.member = MemberService(config_store=cs)
|
|
126
|
+
self.feature = FeatureService(config_store=cs)
|
|
127
|
+
self.sync = SyncService(config_store=cs)
|
|
128
|
+
self.variables = VariablesService(config_store=cs)
|
|
129
|
+
self.doctor = DoctorService(config_store=cs, mcp_service=self.mcp)
|
|
130
|
+
self.version = VersionService()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def install_registry(app: FastAPI, registry: ServiceRegistry) -> None:
|
|
134
|
+
"""Attach the registry to the FastAPI app state."""
|
|
135
|
+
app.state.registry = registry
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def get_registry(request: Request) -> ServiceRegistry:
|
|
139
|
+
"""FastAPI dependency: return the registry from app state."""
|
|
140
|
+
return request.app.state.registry # type: ignore[no-any-return]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_manage_token(request: Request) -> str | None:
|
|
144
|
+
"""Return the per-request manage token from the X-Manage-Token header.
|
|
145
|
+
|
|
146
|
+
Returns None if not provided. Endpoints that require it should validate.
|
|
147
|
+
Never logged, never persisted.
|
|
148
|
+
"""
|
|
149
|
+
return request.headers.get("x-manage-token")
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"""Per-model pricing tables and cost calculation for AI agent runs.
|
|
2
|
+
|
|
3
|
+
Used by the agent-runs persistence path to attach a ``$`` figure and a
|
|
4
|
+
token breakdown to every persisted run, so the UI can show users the
|
|
5
|
+
cost of each scheduled-agent invocation without having to dig into the
|
|
6
|
+
raw event stream.
|
|
7
|
+
|
|
8
|
+
Pricing rates are quoted in **USD per 1,000,000 tokens** (USD/MTok).
|
|
9
|
+
Numbers reflect Anthropic's published pricing as of January 2026 and
|
|
10
|
+
follow the four-bucket structure that ``stream-json`` reports per
|
|
11
|
+
assistant turn:
|
|
12
|
+
|
|
13
|
+
- ``input_tokens`` -- regular prompt tokens this turn.
|
|
14
|
+
- ``output_tokens`` -- assistant-generated tokens this turn.
|
|
15
|
+
- ``cache_creation_input_tokens`` -- prompt tokens that *populated* the
|
|
16
|
+
prompt cache (5-min TTL); billed
|
|
17
|
+
at ~1.25x the input rate.
|
|
18
|
+
- ``cache_read_input_tokens`` -- prompt tokens served from a warm
|
|
19
|
+
cache; billed at ~0.10x the input
|
|
20
|
+
rate. This is where caching pays off.
|
|
21
|
+
|
|
22
|
+
When the live event stream surfaces ``total_cost_usd`` (claude does, in
|
|
23
|
+
the ``result`` event), prefer that authoritative figure; the manual
|
|
24
|
+
calculation here is the fallback for streams that do not.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
# Per-model rates in USD per 1M tokens. Buckets:
|
|
32
|
+
# input | output | cache_create | cache_read
|
|
33
|
+
# Source: anthropic.com/pricing as of 2026-01.
|
|
34
|
+
PRICING_USD_PER_MTOK: dict[str, dict[str, float]] = {
|
|
35
|
+
# Claude Opus 4.7 (incl. 1M ctx variant) -- premium tier.
|
|
36
|
+
"claude-opus-4-7": {
|
|
37
|
+
"input": 15.00,
|
|
38
|
+
"output": 75.00,
|
|
39
|
+
"cache_create": 18.75,
|
|
40
|
+
"cache_read": 1.50,
|
|
41
|
+
},
|
|
42
|
+
"claude-opus-4-7[1m]": {
|
|
43
|
+
"input": 15.00,
|
|
44
|
+
"output": 75.00,
|
|
45
|
+
"cache_create": 18.75,
|
|
46
|
+
"cache_read": 1.50,
|
|
47
|
+
},
|
|
48
|
+
# Earlier Opus revisions kept on the same rate (Anthropic has not
|
|
49
|
+
# split historical Opus pricing to date).
|
|
50
|
+
"claude-opus-4-6": {
|
|
51
|
+
"input": 15.00,
|
|
52
|
+
"output": 75.00,
|
|
53
|
+
"cache_create": 18.75,
|
|
54
|
+
"cache_read": 1.50,
|
|
55
|
+
},
|
|
56
|
+
"claude-opus-4-5": {
|
|
57
|
+
"input": 15.00,
|
|
58
|
+
"output": 75.00,
|
|
59
|
+
"cache_create": 18.75,
|
|
60
|
+
"cache_read": 1.50,
|
|
61
|
+
},
|
|
62
|
+
# Sonnet 4.6 -- balanced tier.
|
|
63
|
+
"claude-sonnet-4-6": {
|
|
64
|
+
"input": 3.00,
|
|
65
|
+
"output": 15.00,
|
|
66
|
+
"cache_create": 3.75,
|
|
67
|
+
"cache_read": 0.30,
|
|
68
|
+
},
|
|
69
|
+
"claude-sonnet-4-5": {
|
|
70
|
+
"input": 3.00,
|
|
71
|
+
"output": 15.00,
|
|
72
|
+
"cache_create": 3.75,
|
|
73
|
+
"cache_read": 0.30,
|
|
74
|
+
},
|
|
75
|
+
# Haiku 4.5 -- speed tier.
|
|
76
|
+
"claude-haiku-4-5": {
|
|
77
|
+
"input": 1.00,
|
|
78
|
+
"output": 5.00,
|
|
79
|
+
"cache_create": 1.25,
|
|
80
|
+
"cache_read": 0.10,
|
|
81
|
+
},
|
|
82
|
+
"claude-haiku-4-5-20251001": {
|
|
83
|
+
"input": 1.00,
|
|
84
|
+
"output": 5.00,
|
|
85
|
+
"cache_create": 1.25,
|
|
86
|
+
"cache_read": 0.10,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Fallback rate when an unknown model id appears in the stream. We pick
|
|
91
|
+
# Sonnet's rate because it's the median tier; users will see *roughly*
|
|
92
|
+
# the right ballpark even if the model is mis-tagged. The structured
|
|
93
|
+
# response also returns ``model_recognized: false`` so the UI can show
|
|
94
|
+
# a "approximate" badge.
|
|
95
|
+
FALLBACK_RATE: dict[str, float] = {
|
|
96
|
+
"input": 3.00,
|
|
97
|
+
"output": 15.00,
|
|
98
|
+
"cache_create": 3.75,
|
|
99
|
+
"cache_read": 0.30,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_rate(model: str | None) -> tuple[dict[str, float], bool]:
|
|
104
|
+
"""Resolve a per-MTok rate dict for a model id.
|
|
105
|
+
|
|
106
|
+
Returns ``(rate, recognized)``: ``recognized`` is False when we fell
|
|
107
|
+
back to the median tier, so callers can mark the figure as approximate.
|
|
108
|
+
Matching is exact first, then prefix-based (``claude-opus-4-7-foo``
|
|
109
|
+
matches ``claude-opus-4-7``) so future point-release tags still hit.
|
|
110
|
+
"""
|
|
111
|
+
if not model:
|
|
112
|
+
return FALLBACK_RATE, False
|
|
113
|
+
norm = model.strip().lower()
|
|
114
|
+
if norm in PRICING_USD_PER_MTOK:
|
|
115
|
+
return PRICING_USD_PER_MTOK[norm], True
|
|
116
|
+
# Longest-prefix wins so ``claude-opus-4-7-future`` doesn't accidentally
|
|
117
|
+
# match ``claude-opus-4-5``.
|
|
118
|
+
candidates = sorted(
|
|
119
|
+
(k for k in PRICING_USD_PER_MTOK if norm.startswith(k)),
|
|
120
|
+
key=lambda k: len(k),
|
|
121
|
+
reverse=True,
|
|
122
|
+
)
|
|
123
|
+
if candidates:
|
|
124
|
+
return PRICING_USD_PER_MTOK[candidates[0]], True
|
|
125
|
+
return FALLBACK_RATE, False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def compute_cost(
|
|
129
|
+
model: str | None,
|
|
130
|
+
usage: dict[str, Any] | None,
|
|
131
|
+
) -> dict[str, Any]:
|
|
132
|
+
"""Compute cost breakdown for a single turn or aggregated usage.
|
|
133
|
+
|
|
134
|
+
``usage`` is the dict shape claude emits per assistant message:
|
|
135
|
+
|
|
136
|
+
{"input_tokens": 1234, "output_tokens": 567,
|
|
137
|
+
"cache_creation_input_tokens": 8000, "cache_read_input_tokens": 2000}
|
|
138
|
+
|
|
139
|
+
Returns a self-describing dict with both the rate that was applied
|
|
140
|
+
and the per-bucket cost contribution, so the UI can render a
|
|
141
|
+
breakdown table without re-doing the math:
|
|
142
|
+
|
|
143
|
+
{
|
|
144
|
+
"model": "claude-opus-4-7",
|
|
145
|
+
"model_recognized": true,
|
|
146
|
+
"rate_per_mtok": {"input": 15.0, "output": 75.0, ...},
|
|
147
|
+
"tokens": {input: 1234, output: 567, ...},
|
|
148
|
+
"cost_usd": {input: 0.0185, output: 0.0425, cache_create: 0.15, cache_read: 0.003,
|
|
149
|
+
total: 0.214}
|
|
150
|
+
}
|
|
151
|
+
"""
|
|
152
|
+
usage = usage or {}
|
|
153
|
+
rate, recognized = get_rate(model)
|
|
154
|
+
in_tok = int(usage.get("input_tokens") or 0)
|
|
155
|
+
out_tok = int(usage.get("output_tokens") or 0)
|
|
156
|
+
cc_tok = int(usage.get("cache_creation_input_tokens") or 0)
|
|
157
|
+
cr_tok = int(usage.get("cache_read_input_tokens") or 0)
|
|
158
|
+
in_cost = (in_tok / 1_000_000) * rate["input"]
|
|
159
|
+
out_cost = (out_tok / 1_000_000) * rate["output"]
|
|
160
|
+
cc_cost = (cc_tok / 1_000_000) * rate["cache_create"]
|
|
161
|
+
cr_cost = (cr_tok / 1_000_000) * rate["cache_read"]
|
|
162
|
+
total_cost = in_cost + out_cost + cc_cost + cr_cost
|
|
163
|
+
return {
|
|
164
|
+
"model": model,
|
|
165
|
+
"model_recognized": recognized,
|
|
166
|
+
"rate_per_mtok": rate,
|
|
167
|
+
"tokens": {
|
|
168
|
+
"input": in_tok,
|
|
169
|
+
"output": out_tok,
|
|
170
|
+
"cache_create": cc_tok,
|
|
171
|
+
"cache_read": cr_tok,
|
|
172
|
+
"total": in_tok + out_tok + cc_tok + cr_tok,
|
|
173
|
+
},
|
|
174
|
+
"cost_usd": {
|
|
175
|
+
"input": round(in_cost, 6),
|
|
176
|
+
"output": round(out_cost, 6),
|
|
177
|
+
"cache_create": round(cc_cost, 6),
|
|
178
|
+
"cache_read": round(cr_cost, 6),
|
|
179
|
+
"total": round(total_cost, 6),
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def aggregate_usage_from_events(events: list[dict[str, Any]]) -> dict[str, Any]:
|
|
185
|
+
"""Sum per-assistant-turn ``usage`` blocks into a single dict.
|
|
186
|
+
|
|
187
|
+
Walks the stream-json event list, picks every ``assistant`` event,
|
|
188
|
+
and accumulates the four token buckets. Final ``result`` event also
|
|
189
|
+
carries a usage block (often equal to the last assistant turn) which
|
|
190
|
+
we deliberately *skip* to avoid double-counting -- claude's result
|
|
191
|
+
usage is already reflected in the assistant turns it summarizes.
|
|
192
|
+
"""
|
|
193
|
+
totals = {
|
|
194
|
+
"input_tokens": 0,
|
|
195
|
+
"output_tokens": 0,
|
|
196
|
+
"cache_creation_input_tokens": 0,
|
|
197
|
+
"cache_read_input_tokens": 0,
|
|
198
|
+
}
|
|
199
|
+
for evt in events:
|
|
200
|
+
if evt.get("event") not in {"stdout"}:
|
|
201
|
+
continue
|
|
202
|
+
data = evt.get("data") or {}
|
|
203
|
+
if data.get("type") != "assistant":
|
|
204
|
+
continue
|
|
205
|
+
usage = (data.get("message") or {}).get("usage") or {}
|
|
206
|
+
for k in totals:
|
|
207
|
+
v = usage.get(k)
|
|
208
|
+
if isinstance(v, int):
|
|
209
|
+
totals[k] += v
|
|
210
|
+
return totals
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def extract_model_from_events(events: list[dict[str, Any]]) -> str | None:
|
|
214
|
+
"""Return the model id from the first ``system.init`` event, if present."""
|
|
215
|
+
for evt in events:
|
|
216
|
+
if evt.get("event") != "stdout":
|
|
217
|
+
continue
|
|
218
|
+
data = evt.get("data") or {}
|
|
219
|
+
if data.get("type") == "system" and data.get("subtype") == "init":
|
|
220
|
+
model = data.get("model")
|
|
221
|
+
if isinstance(model, str):
|
|
222
|
+
return model
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def extract_total_cost_from_events(events: list[dict[str, Any]]) -> float | None:
|
|
227
|
+
"""Return claude's authoritative ``total_cost_usd`` from the result event.
|
|
228
|
+
|
|
229
|
+
When present this is preferred over the manual ``compute_cost`` figure
|
|
230
|
+
(it accounts for any per-turn pricing nuance Anthropic applies that
|
|
231
|
+
we cannot model from the public rate card).
|
|
232
|
+
"""
|
|
233
|
+
for evt in events:
|
|
234
|
+
if evt.get("event") != "stdout":
|
|
235
|
+
continue
|
|
236
|
+
data = evt.get("data") or {}
|
|
237
|
+
if data.get("type") == "result":
|
|
238
|
+
cost = data.get("total_cost_usd")
|
|
239
|
+
if isinstance(cost, int | float):
|
|
240
|
+
return float(cost)
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def extract_tool_summary(events: list[dict[str, Any]]) -> dict[str, Any]:
|
|
245
|
+
"""Walk events and produce a per-tool call count + ordered list of tools used.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
{
|
|
249
|
+
"count": <total tool_use blocks>,
|
|
250
|
+
"by_tool": {"Bash": 4, "Read": 7, ...},
|
|
251
|
+
"errors": <count of tool_results with is_error=true>,
|
|
252
|
+
}
|
|
253
|
+
"""
|
|
254
|
+
by_tool: dict[str, int] = {}
|
|
255
|
+
total = 0
|
|
256
|
+
errors = 0
|
|
257
|
+
for evt in events:
|
|
258
|
+
if evt.get("event") != "stdout":
|
|
259
|
+
continue
|
|
260
|
+
data = evt.get("data") or {}
|
|
261
|
+
if data.get("type") == "assistant":
|
|
262
|
+
for block in (data.get("message") or {}).get("content") or []:
|
|
263
|
+
if isinstance(block, dict) and block.get("type") == "tool_use":
|
|
264
|
+
name = str(block.get("name") or "?")
|
|
265
|
+
by_tool[name] = by_tool.get(name, 0) + 1
|
|
266
|
+
total += 1
|
|
267
|
+
elif data.get("type") == "user":
|
|
268
|
+
for block in (data.get("message") or {}).get("content") or []:
|
|
269
|
+
if (
|
|
270
|
+
isinstance(block, dict)
|
|
271
|
+
and block.get("type") == "tool_result"
|
|
272
|
+
and block.get("is_error") is True
|
|
273
|
+
):
|
|
274
|
+
errors += 1
|
|
275
|
+
return {"count": total, "by_tool": by_tool, "errors": errors}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def build_run_summary(events: list[dict[str, Any]]) -> dict[str, Any]:
|
|
279
|
+
"""Single entry point: build the summary blob persisted on a finished run.
|
|
280
|
+
|
|
281
|
+
Combines model, token totals, cost breakdown (preferring claude's
|
|
282
|
+
authoritative number), and tool-call breakdown. Shape is stable across
|
|
283
|
+
runs so the UI can render unconditionally.
|
|
284
|
+
"""
|
|
285
|
+
model = extract_model_from_events(events)
|
|
286
|
+
usage = aggregate_usage_from_events(events)
|
|
287
|
+
cost_breakdown = compute_cost(model, usage)
|
|
288
|
+
authoritative = extract_total_cost_from_events(events)
|
|
289
|
+
if authoritative is not None:
|
|
290
|
+
cost_breakdown["cost_usd"]["total"] = round(authoritative, 6)
|
|
291
|
+
cost_breakdown["cost_usd"]["source"] = "claude_result"
|
|
292
|
+
else:
|
|
293
|
+
cost_breakdown["cost_usd"]["source"] = "computed"
|
|
294
|
+
tools = extract_tool_summary(events)
|
|
295
|
+
return {
|
|
296
|
+
"model": model,
|
|
297
|
+
"model_recognized": cost_breakdown["model_recognized"],
|
|
298
|
+
"tokens": cost_breakdown["tokens"],
|
|
299
|
+
"rate_per_mtok": cost_breakdown["rate_per_mtok"],
|
|
300
|
+
"cost_usd": cost_breakdown["cost_usd"],
|
|
301
|
+
"tools": tools,
|
|
302
|
+
"events_count": len(events),
|
|
303
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""FastAPI routers for kbagent serve."""
|