agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.2__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.
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +22 -2
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
- ksadk/a2a/_space_client_events.py +381 -0
- ksadk/a2a/event_adapter.py +156 -64
- ksadk/a2a/executor.py +179 -63
- ksadk/a2a/space_client.py +6 -333
- ksadk/a2ui/core.py +222 -53
- ksadk/agui/_agent_helpers.py +243 -0
- ksadk/agui/a2ui_projection.py +12 -5
- ksadk/agui/agent.py +443 -259
- ksadk/api/client.py +525 -27
- ksadk/builders/code_builder.py +284 -8
- ksadk/builders/container_builder.py +8 -1
- ksadk/builders/managed_runtime_builder.py +39 -35
- ksadk/cli/__init__.py +17 -0
- ksadk/cli/cmd_dashboard.py +15 -1
- ksadk/cli/cmd_deploy.py +10 -2
- ksadk/cli/cmd_eval.py +66 -9
- ksadk/cli/cmd_evalset.py +284 -0
- ksadk/cli/cmd_files.py +5 -1
- ksadk/cli/cmd_hermes.py +1 -126
- ksadk/cli/cmd_invoke.py +28 -38
- ksadk/cli/cmd_managed_runtime.py +95 -0
- ksadk/cli/cmd_observe.py +85 -0
- ksadk/cli/cmd_openclaw.py +1 -435
- ksadk/cli/cmd_replay.py +28 -4
- ksadk/cli/cmd_studio.py +99 -8
- ksadk/cli/cmd_web.py +10 -2
- ksadk/cli/hermes_env.py +151 -0
- ksadk/cli/invoke_payload.py +49 -0
- ksadk/cli/openclaw_env.py +458 -0
- ksadk/codex/client.py +180 -30
- ksadk/codex/runtime.py +428 -326
- ksadk/configs/env_registry.py +66 -12
- ksadk/configs/env_registry_pcm.py +181 -0
- ksadk/configs/env_var_spec.py +18 -0
- ksadk/configs/global_config.py +5 -0
- ksadk/context_engine/__init__.py +98 -0
- ksadk/context_engine/assembler.py +176 -0
- ksadk/context_engine/baseline.py +419 -0
- ksadk/context_engine/cache_observability.py +228 -0
- ksadk/context_engine/capabilities.py +443 -0
- ksadk/context_engine/contributors.py +344 -0
- ksadk/context_engine/hosted_pipeline.py +417 -0
- ksadk/context_engine/models.py +117 -0
- ksadk/context_engine/planner.py +698 -0
- ksadk/context_engine/policies.py +319 -0
- ksadk/context_engine/projection.py +31 -0
- ksadk/context_engine/shadow_plan.py +266 -0
- ksadk/context_engine/tokenizer.py +164 -0
- ksadk/conversations/context.py +126 -6
- ksadk/conversations/message_projection.py +292 -28
- ksadk/conversations/model_context.py +49 -3
- ksadk/conversations/runtime_compaction.py +207 -5
- ksadk/conversations/runtime_input.py +184 -2
- ksadk/conversations/runtime_invocation.py +81 -3
- ksadk/conversations/runtime_observability.py +141 -0
- ksadk/conversations/runtime_payloads.py +42 -0
- ksadk/conversations/runtime_preparation.py +272 -2
- ksadk/conversations/runtime_resume.py +20 -1
- ksadk/conversations/runtime_stream_events.py +94 -9
- ksadk/conversations/semantic_summary.py +311 -1
- ksadk/conversations/session_lock.py +51 -0
- ksadk/deployment/env_forward.py +72 -0
- ksadk/deployment/managed_runtime.py +5 -0
- ksadk/deployment/providers/serverless.py +143 -69
- ksadk/evaluation/__init__.py +32 -0
- ksadk/evaluation/a2a_adapter.py +9 -1
- ksadk/evaluation/adapters.py +18 -5
- ksadk/evaluation/agent_eval_client.py +395 -0
- ksadk/evaluation/cloud_binding.py +80 -0
- ksadk/evaluation/cloud_converter.py +194 -0
- ksadk/evaluation/cloud_service.py +199 -0
- ksadk/evaluation/contracts.py +73 -4
- ksadk/evaluation/evaluators.py +183 -17
- ksadk/evaluation/evidence.py +258 -0
- ksadk/evaluation/executor.py +31 -6
- ksadk/evaluation/local_adapter.py +550 -0
- ksadk/evaluation/service_env.py +27 -0
- ksadk/evaluation/studio_build_adapter.py +253 -0
- ksadk/evaluation/target.py +15 -6
- ksadk/events/__init__.py +13 -13
- ksadk/events/_v1_compat/__init__.py +1 -0
- ksadk/events/_v1_compat/models.py +299 -0
- ksadk/events/_v1_compat/parser.py +247 -0
- ksadk/events/_v1_compat/projection.py +753 -0
- ksadk/events/adapters/__init__.py +34 -0
- ksadk/events/adapters/_a2a_snapshot.py +622 -0
- ksadk/events/adapters/_a2a_support.py +275 -0
- ksadk/events/adapters/_codex_interactions.py +375 -0
- ksadk/events/adapters/_codex_items.py +801 -0
- ksadk/events/adapters/_codex_validators.py +311 -0
- ksadk/events/adapters/_langgraph_support.py +785 -0
- ksadk/events/adapters/a2a.py +841 -0
- ksadk/events/adapters/adk.py +503 -0
- ksadk/events/adapters/codex.py +717 -0
- ksadk/events/adapters/langgraph.py +745 -0
- ksadk/events/canonical.py +440 -0
- ksadk/events/canonical_replay.py +497 -0
- ksadk/events/canonical_store.py +468 -0
- ksadk/events/cold_recovery.py +257 -0
- ksadk/events/content.py +89 -0
- ksadk/events/identity.py +86 -0
- ksadk/events/pipeline.py +455 -0
- ksadk/events/projections.py +66 -0
- ksadk/events/reducer.py +661 -0
- ksadk/events/replay.py +3 -36
- ksadk/events/runtime_event.py +20 -265
- ksadk/events/session_event.py +324 -0
- ksadk/events/store.py +3 -316
- ksadk/events/v1_compat.py +41 -0
- ksadk/harness/runtime.py +160 -57
- ksadk/identity/resolver.py +4 -3
- ksadk/interaction/__init__.py +28 -0
- ksadk/interaction/contracts.py +136 -0
- ksadk/interaction/ledger.py +189 -0
- ksadk/interaction/provider.py +122 -0
- ksadk/interaction/providers/__init__.py +52 -0
- ksadk/interaction/providers/adk.py +40 -0
- ksadk/interaction/providers/codex.py +94 -0
- ksadk/interaction/providers/langgraph.py +58 -0
- ksadk/kernel/__init__.py +117 -0
- ksadk/kernel/authorization.py +258 -0
- ksadk/kernel/bootstrap.py +1074 -0
- ksadk/kernel/contract_fingerprints.py +61 -0
- ksadk/kernel/contracts.py +362 -0
- ksadk/kernel/control.py +219 -0
- ksadk/kernel/errors.py +79 -0
- ksadk/kernel/ingress.py +1102 -0
- ksadk/kernel/mapping.py +111 -0
- ksadk/kernel/memory_store.py +1133 -0
- ksadk/kernel/postgres_store.py +1756 -0
- ksadk/kernel/recovery.py +452 -0
- ksadk/kernel/runtime_identity.py +103 -0
- ksadk/kernel/sql/001_agent_kernel.sql +120 -0
- ksadk/kernel/sqlite_store.py +1410 -0
- ksadk/kernel/state.py +145 -0
- ksadk/kernel/store.py +232 -0
- ksadk/kernel/worker.py +953 -0
- ksadk/knowledge_base/client.py +7 -0
- ksadk/knowledge_base/service.py +32 -5
- ksadk/memory/__init__.py +61 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
- ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
- ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
- ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
- ksadk/memory/coordinator.py +461 -0
- ksadk/memory/events.py +202 -0
- ksadk/memory/extraction.py +228 -0
- ksadk/memory/ltm_backend_factory.py +16 -3
- ksadk/memory/models.py +316 -0
- ksadk/memory/policy.py +215 -0
- ksadk/memory/provider.py +47 -0
- ksadk/memory/provider_adapter.py +159 -0
- ksadk/memory/provider_resolver.py +71 -0
- ksadk/memory/providers/__init__.py +5 -0
- ksadk/memory/providers/local_sqlite.py +490 -0
- ksadk/memory/resolved_policy.py +145 -0
- ksadk/memory/service.py +188 -6
- ksadk/model_proxy/bootstrap.py +16 -9
- ksadk/model_proxy/detect.py +202 -9
- ksadk/model_proxy/namespace.py +39 -15
- ksadk/model_proxy/server.py +24 -5
- ksadk/model_proxy/transform.py +60 -2
- ksadk/observability/__init__.py +25 -0
- ksadk/observability/session_log.py +420 -0
- ksadk/observability/trajectory.py +105 -0
- ksadk/prompts/__init__.py +71 -0
- ksadk/prompts/compiler.py +215 -0
- ksadk/prompts/models.py +82 -0
- ksadk/prompts/projection.py +89 -0
- ksadk/prompts/resolved.py +137 -0
- ksadk/prompts/sources.py +233 -0
- ksadk/runners/_langgraph_runner_streams.py +822 -0
- ksadk/runners/adk_runner.py +5 -5
- ksadk/runners/base_runner.py +16 -0
- ksadk/runners/factory.py +1 -2
- ksadk/runners/langgraph_runner.py +198 -437
- ksadk/runtime/_runner_adapter/__init__.py +1 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
- ksadk/runtime/adapter.py +118 -2
- ksadk/runtime/conversation_execution.py +316 -182
- ksadk/runtime/executor.py +144 -3
- ksadk/runtime/factory.py +193 -7
- ksadk/runtime/hosted_finalizer.py +268 -0
- ksadk/runtime/launch.py +14 -1
- ksadk/runtime/preprocessing.py +25 -3
- ksadk/runtime/runner_adapter.py +288 -508
- ksadk/runtime/usage.py +33 -0
- ksadk/sandbox/backends/e2b.py +35 -2
- ksadk/server/composition.py +8 -0
- ksadk/server/factory.py +69 -6
- ksadk/server/routes/kernel_ingress.py +258 -0
- ksadk/server/routes/openai_compat.py +88 -8
- ksadk/server/routes/projection.py +35 -20
- ksadk/server/routes/run.py +94 -0
- ksadk/server/routes/sessions.py +2 -0
- ksadk/server/routes/streaming.py +5 -2
- ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
- ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
- ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
- ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
- ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
- ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
- ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
- ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
- ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
- ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
- ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
- ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
- ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
- ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
- ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
- ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
- ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
- ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
- ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
- ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
- ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
- ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
- ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
- ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
- ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
- ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
- ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
- ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
- ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
- ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
- ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
- ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
- ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
- ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
- ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
- ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
- ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
- ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
- ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
- ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
- ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
- ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
- ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
- ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
- ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
- ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
- ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
- ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
- ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
- ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
- ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
- ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
- ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
- ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
- ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
- ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
- ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
- ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
- ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
- ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
- ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
- ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
- ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
- ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
- ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
- ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
- ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
- ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
- ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
- ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
- ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
- ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
- ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
- ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +26 -9
- ksadk/sessions/_local_service_sync.py +688 -0
- ksadk/sessions/_local_tables.py +21 -0
- ksadk/sessions/_postgres_schema.py +305 -0
- ksadk/sessions/_postgres_tables.py +15 -0
- ksadk/sessions/base.py +80 -1
- ksadk/sessions/in_memory.py +53 -2
- ksadk/sessions/local_service.py +63 -604
- ksadk/sessions/postgres_service.py +58 -148
- ksadk/sessions/resilient.py +35 -1
- ksadk/studio/api.py +577 -25
- ksadk/studio/api_contracts.py +114 -5
- ksadk/studio/api_memory_routes.py +82 -0
- ksadk/studio/authoring.py +252 -27
- ksadk/studio/authoring_coordinator.py +799 -67
- ksadk/studio/builder.py +88 -11
- ksadk/studio/capabilities.py +17 -0
- ksadk/studio/cloud.py +1916 -95
- ksadk/studio/codex_agent_service.py +125 -14
- ksadk/studio/codex_authoring.py +469 -0
- ksadk/studio/codex_builder.py +138 -25
- ksadk/studio/codex_manifest.py +51 -22
- ksadk/studio/codex_run.py +46 -3
- ksadk/studio/compiler.py +1 -0
- ksadk/studio/contracts.py +114 -3
- ksadk/studio/event_store.py +25 -95
- ksadk/studio/framework_run.py +231 -3
- ksadk/studio/hosted_kernel.py +317 -0
- ksadk/studio/manifest_resolver.py +131 -0
- ksadk/studio/model_client.py +95 -9
- ksadk/studio/model_profile_service.py +1 -1
- ksadk/studio/operations.py +37 -5
- ksadk/studio/otel_trace.py +348 -42
- ksadk/studio/pcm_memory.py +85 -0
- ksadk/studio/repository.py +23 -2
- ksadk/studio/resource_catalog.py +43 -0
- ksadk/studio/run_service.py +563 -137
- ksadk/studio/runtime_source.py +42 -16
- ksadk/studio/service.py +1180 -72
- ksadk/studio/shared_web.py +32 -22
- ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
- ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
- ksadk/studio/static/index.html +2 -2
- ksadk/studio/templates.py +100 -8
- ksadk/studio/validator.py +19 -0
- ksadk/studio/workspace.py +30 -32
- ksadk/tracing/setup.py +304 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
- ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
- ksadk/events/parser.py +0 -191
- ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
- ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
- ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
- ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
- ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
- ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
- ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
- ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
- ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
- ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
- ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
- ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
- ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
- ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
- ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
- ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
- ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
- ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
- ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
- ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
- ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
- ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
- ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
- ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
- ksadk/server/static/assets/index-R52pJJvg.js +0 -177
- ksadk/server/static/assets/index-jJr3_955.css +0 -2
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
- ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
- ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
- ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
- ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
- ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
- ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
- ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
- ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
- ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
- ksadk/studio/evaluation.py +0 -192
- ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
- ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
ksadk/runtime/usage.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Canonical runtime usage projection helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Mapping
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def canonical_usage_payload(
|
|
9
|
+
usage: Mapping[str, Any], *, runtime_type: str
|
|
10
|
+
) -> dict[str, Any]:
|
|
11
|
+
"""Normalize provider usage, including nested cache/reasoning details."""
|
|
12
|
+
input_details = usage.get("input_token_details")
|
|
13
|
+
output_details = usage.get("output_token_details")
|
|
14
|
+
cached = (
|
|
15
|
+
input_details.get("cached")
|
|
16
|
+
if isinstance(input_details, Mapping)
|
|
17
|
+
else usage.get("cached_tokens")
|
|
18
|
+
)
|
|
19
|
+
reasoning = (
|
|
20
|
+
output_details.get("reasoning")
|
|
21
|
+
if isinstance(output_details, Mapping)
|
|
22
|
+
else usage.get("reasoning_tokens")
|
|
23
|
+
)
|
|
24
|
+
input_tokens = int(usage.get("input_tokens") or 0)
|
|
25
|
+
output_tokens = int(usage.get("output_tokens") or 0)
|
|
26
|
+
return {
|
|
27
|
+
"input_tokens": input_tokens,
|
|
28
|
+
"output_tokens": output_tokens,
|
|
29
|
+
"total_tokens": int(usage.get("total_tokens") or input_tokens + output_tokens),
|
|
30
|
+
"cached_tokens": int(cached or 0),
|
|
31
|
+
"reasoning_tokens": int(reasoning or 0),
|
|
32
|
+
"source": str(usage.get("source") or runtime_type),
|
|
33
|
+
}
|
ksadk/sandbox/backends/e2b.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import os
|
|
4
5
|
import time
|
|
5
6
|
from typing import Any
|
|
@@ -12,10 +13,15 @@ from ksadk.sandbox.base import (
|
|
|
12
13
|
SandboxSpec,
|
|
13
14
|
)
|
|
14
15
|
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
15
18
|
_TRANSIENT_STARTUP_ERROR_NAMES = {
|
|
16
19
|
"NotFoundException",
|
|
17
20
|
"FileNotFoundException",
|
|
18
21
|
"SandboxNotFoundException",
|
|
22
|
+
# create 快速返回后 envd RPC 通道可能尚未就绪 (connection refused / unavailable),
|
|
23
|
+
# SDK 将其包装为 TimeoutException 抛出, 需按瞬时启动错误重试。
|
|
24
|
+
"TimeoutException",
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
|
|
@@ -148,7 +154,7 @@ class E2BSandboxBackend:
|
|
|
148
154
|
allow_internet_access=self.spec.allow_internet_access,
|
|
149
155
|
)
|
|
150
156
|
session = self._wrap_sandbox(sandbox)
|
|
151
|
-
self._wait_until_ready(session)
|
|
157
|
+
self._wait_until_ready(session, runtime_env)
|
|
152
158
|
session.write_files(
|
|
153
159
|
[(item.target_path, item.source.read_bytes()) for item in input_files or []]
|
|
154
160
|
)
|
|
@@ -157,6 +163,33 @@ class E2BSandboxBackend:
|
|
|
157
163
|
def _wrap_sandbox(self, sandbox: Any) -> E2BSandboxSession:
|
|
158
164
|
return E2BSandboxSession(sandbox)
|
|
159
165
|
|
|
160
|
-
def _wait_until_ready(
|
|
166
|
+
def _wait_until_ready(
|
|
167
|
+
self, session: E2BSandboxSession, env: dict[str, str] | None = None
|
|
168
|
+
) -> None:
|
|
161
169
|
_with_startup_retry(lambda: session.run_command("true"))
|
|
162
170
|
_with_startup_retry(lambda: session.write_file("/tmp/.ksadk-sandbox-ready", ""))
|
|
171
|
+
self._wait_env_applied(session, env or {})
|
|
172
|
+
|
|
173
|
+
def _wait_env_applied(self, session: E2BSandboxSession, env: dict[str, str]) -> None:
|
|
174
|
+
# create 返回后 envd 异步应用 envVars, 实测存在秒级延迟;
|
|
175
|
+
# 轮询一个非空变量直到值就位, 避免紧随其后的 agent 进程读到缺失的环境。
|
|
176
|
+
probe = next(((k, v) for k, v in env.items() if v), None)
|
|
177
|
+
if probe is None:
|
|
178
|
+
return
|
|
179
|
+
key, expected = probe
|
|
180
|
+
attempts = _startup_retry_attempts()
|
|
181
|
+
delay = _startup_retry_delay()
|
|
182
|
+
for attempt in range(attempts):
|
|
183
|
+
result = session.run_command(f"printenv {key} || true")
|
|
184
|
+
if result.stdout.strip() == expected:
|
|
185
|
+
return
|
|
186
|
+
time.sleep(min(delay * (2**attempt), 1.0))
|
|
187
|
+
# 不 raise: 模板可能预置同名空值覆盖 create 注入 (envd 合并优先级问题),
|
|
188
|
+
# 此时调用方仍可通过 run_command(env=...) 的 per-command 注入拿到正确值。
|
|
189
|
+
logger.warning(
|
|
190
|
+
"E2B sandbox env %s not applied after %d attempts (expected %r); "
|
|
191
|
+
"template may predefine an empty override",
|
|
192
|
+
key,
|
|
193
|
+
attempts,
|
|
194
|
+
expected,
|
|
195
|
+
)
|
ksadk/server/composition.py
CHANGED
|
@@ -13,6 +13,14 @@ def configure_runtime_app(app: FastAPI, state: RuntimeAppState, groups: set[str]
|
|
|
13
13
|
_configure_route_dependencies()
|
|
14
14
|
_register_integrated_routers(app, state)
|
|
15
15
|
|
|
16
|
+
# Agent Kernel canonical ingress(/agent-kernel/v1/*)必须先于 route group
|
|
17
|
+
# 注册:group 内的静态 UI catch-all(GET /{path}) 会吞掉所有未匹配 GET,
|
|
18
|
+
# 后注册的 kernel GET 路由(health / SubscribeSessionEvents)将被遮蔽 404。
|
|
19
|
+
# 灰度开关关闭时 bootstrap 返回 None、路由统一回 503,不影响旧路径。
|
|
20
|
+
from ksadk.kernel import ingress as _kernel_ingress
|
|
21
|
+
|
|
22
|
+
app.include_router(_kernel_ingress.agent_kernel_router())
|
|
23
|
+
|
|
16
24
|
ordered = sorted(group for group in groups if group != "health_meta")
|
|
17
25
|
if "health_meta" in groups:
|
|
18
26
|
ordered.append("health_meta")
|
ksadk/server/factory.py
CHANGED
|
@@ -194,6 +194,7 @@ class RuntimeAppConfig:
|
|
|
194
194
|
agui: Optional[Any] = None,
|
|
195
195
|
runtime_executor: RuntimeExecutor | None = None,
|
|
196
196
|
launch_context: RuntimeLaunchContext | None = None,
|
|
197
|
+
kernel_adapter_provider: Callable[[], RuntimeAdapter] | None = None,
|
|
197
198
|
session_service_provider: Callable[[], Any] | None = None,
|
|
198
199
|
session_backend_provider: Callable[[], dict[str, Any]] | None = None,
|
|
199
200
|
) -> None:
|
|
@@ -208,6 +209,9 @@ class RuntimeAppConfig:
|
|
|
208
209
|
self.agui = agui
|
|
209
210
|
self.runtime_executor = runtime_executor
|
|
210
211
|
self.launch_context = launch_context
|
|
212
|
+
# 特殊 runtime 可显式提供 worker/recovery 的 adapter;常规部署从
|
|
213
|
+
# runtime_executor 的同一 registry 派生,见 _kernel_adapter_provider。
|
|
214
|
+
self.kernel_adapter_provider = kernel_adapter_provider
|
|
211
215
|
self.session_service_provider = session_service_provider
|
|
212
216
|
self.session_backend_provider = session_backend_provider
|
|
213
217
|
|
|
@@ -353,6 +357,40 @@ def create_runtime_app(
|
|
|
353
357
|
|
|
354
358
|
@asynccontextmanager
|
|
355
359
|
async def _lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
360
|
+
# 生产 kernel 的 composition root 必须启动 worker/lease/recovery,
|
|
361
|
+
# 不能只注册一个可接收命令、却永远不会消费的 ingress kernel。
|
|
362
|
+
from ksadk.kernel import ingress as _kernel_ingress
|
|
363
|
+
from ksadk.kernel.bootstrap import (
|
|
364
|
+
bootstrap_agent_kernel_runtime_from_env,
|
|
365
|
+
clear_agent_kernel_runtime,
|
|
366
|
+
)
|
|
367
|
+
from ksadk.runtime.factory import kernel_start_request_defaults
|
|
368
|
+
|
|
369
|
+
adapter_provider = _kernel_adapter_provider(config)
|
|
370
|
+
request_defaults = (
|
|
371
|
+
kernel_start_request_defaults(config.launch_context)
|
|
372
|
+
if config.launch_context is not None
|
|
373
|
+
else {}
|
|
374
|
+
)
|
|
375
|
+
kernel_runtime = await bootstrap_agent_kernel_runtime_from_env(
|
|
376
|
+
adapter_provider=adapter_provider,
|
|
377
|
+
runtime_executor=config.runtime_executor,
|
|
378
|
+
launch_context=config.launch_context,
|
|
379
|
+
start_request_defaults=request_defaults,
|
|
380
|
+
session_service=state.resolve_session_service(),
|
|
381
|
+
)
|
|
382
|
+
app.state.agent_kernel_runtime = kernel_runtime
|
|
383
|
+
if kernel_runtime is not None:
|
|
384
|
+
# Kernel canonical events are the replay authority for the normal
|
|
385
|
+
# Session APIs as well. In memory/ephemeral mode bootstrap reuses
|
|
386
|
+
# the app-owned service above; in PostgreSQL mode it owns the
|
|
387
|
+
# durable service and the HTTP routes must adopt that exact one.
|
|
388
|
+
# Keeping two services made foreground SSE look correct while a
|
|
389
|
+
# later ListSessionEvents call returned an empty history forever.
|
|
390
|
+
kernel_session_service = kernel_runtime.config.session_service
|
|
391
|
+
if kernel_session_service is None: # pragma: no cover - defensive
|
|
392
|
+
raise RuntimeError("agent kernel runtime has no Session service")
|
|
393
|
+
state.session_service = kernel_session_service
|
|
356
394
|
try:
|
|
357
395
|
if state.a2a_bootstrap is not None:
|
|
358
396
|
await state.a2a_bootstrap.start()
|
|
@@ -360,6 +398,10 @@ def create_runtime_app(
|
|
|
360
398
|
finally:
|
|
361
399
|
if state.a2a_bootstrap is not None:
|
|
362
400
|
await state.a2a_bootstrap.stop()
|
|
401
|
+
if kernel_runtime is not None:
|
|
402
|
+
await kernel_runtime.close()
|
|
403
|
+
clear_agent_kernel_runtime()
|
|
404
|
+
_kernel_ingress.clear_agent_kernel()
|
|
363
405
|
await shutdown_runtime_resources(state)
|
|
364
406
|
|
|
365
407
|
app = FastAPI(
|
|
@@ -488,6 +530,25 @@ def create_runtime_app(
|
|
|
488
530
|
return app
|
|
489
531
|
|
|
490
532
|
|
|
533
|
+
def _kernel_adapter_provider(
|
|
534
|
+
config: RuntimeAppConfig,
|
|
535
|
+
) -> Callable[[], RuntimeAdapter] | None:
|
|
536
|
+
"""Return the adapter source used by the durable kernel runtime.
|
|
537
|
+
|
|
538
|
+
Hosted startup deliberately fails when neither a provider nor a Runtime
|
|
539
|
+
launch context is available. It must never construct an unrelated local
|
|
540
|
+
adapter merely to make the ingress look healthy.
|
|
541
|
+
"""
|
|
542
|
+
|
|
543
|
+
if config.kernel_adapter_provider is not None:
|
|
544
|
+
return config.kernel_adapter_provider
|
|
545
|
+
if config.a2a_runtime_adapter is not None:
|
|
546
|
+
return lambda: config.a2a_runtime_adapter
|
|
547
|
+
if config.runtime_executor is not None and config.launch_context is not None:
|
|
548
|
+
return lambda: config.runtime_executor.create_adapter(config.launch_context)
|
|
549
|
+
return None
|
|
550
|
+
|
|
551
|
+
|
|
491
552
|
def _wire_a2a_if_enabled(app: FastAPI, state: RuntimeAppState, config: RuntimeAppConfig) -> None:
|
|
492
553
|
"""Mount A2A with an explicitly injected RuntimeAdapter.
|
|
493
554
|
|
|
@@ -569,15 +630,17 @@ def _wire_agui_if_enabled(app: FastAPI, state: RuntimeAppState, config: RuntimeA
|
|
|
569
630
|
self._service = service
|
|
570
631
|
self._store = RuntimeEventStore(service)
|
|
571
632
|
|
|
572
|
-
async def append_one(self, event: Any) -> Any:
|
|
573
|
-
existing = await self._service.get_session(
|
|
633
|
+
async def append_one(self, session_id: str, event: Any) -> Any:
|
|
634
|
+
existing = await self._service.get_session(session_id)
|
|
574
635
|
if existing is None:
|
|
636
|
+
metadata = getattr(event, "source", None)
|
|
637
|
+
meta = metadata.metadata if metadata is not None else {}
|
|
575
638
|
await self._service.create_session(
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
639
|
+
str(meta.get("agent_id") or "agent"),
|
|
640
|
+
str(meta.get("user_id") or "user"),
|
|
641
|
+
session_id,
|
|
579
642
|
)
|
|
580
|
-
return await self._store.append_one(event)
|
|
643
|
+
return await self._store.append_one(session_id, event)
|
|
581
644
|
|
|
582
645
|
async def reserve_once(self, event: Any) -> Any:
|
|
583
646
|
existing = await self._service.get_session(event.session_id)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""RunAgent / Responses 入口的 kernel 路径(Phase 1 Task 8 Step 3-5)。
|
|
3
|
+
|
|
4
|
+
只在 ``kernel_route_active()`` 时启用(灰度 opt-in);旧 HTTP 行为保持兼容:
|
|
5
|
+
- accepted -> 202、duplicate -> 200、rejected -> 400、unsupported -> 409、
|
|
6
|
+
queue_full -> 429、persistence_uncertain -> 503(RECEIPT_HTTP_STATUS)。
|
|
7
|
+
- 旧响应 shape 不变;非流式在 receipt accepted 后才开始消费 stream。
|
|
8
|
+
- SSE 的 reconnect cursor 源自同一 Session seq(SessionEventSubscription)。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from fastapi.responses import JSONResponse, StreamingResponse
|
|
18
|
+
|
|
19
|
+
from ksadk.kernel import ingress
|
|
20
|
+
from ksadk.kernel.contracts import AgentControlReceipt
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _envelope_text(payload: dict[str, Any]) -> str:
|
|
26
|
+
return str(payload.get("delta") or payload.get("text") or "")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def _kernel_submit(
|
|
30
|
+
*,
|
|
31
|
+
mapper: str,
|
|
32
|
+
session_id: str,
|
|
33
|
+
idempotency_key: str,
|
|
34
|
+
content: Any,
|
|
35
|
+
correlation_ref: str | None,
|
|
36
|
+
source_kind: str,
|
|
37
|
+
runtime_options: dict[str, Any] | None = None,
|
|
38
|
+
) -> tuple[AgentControlReceipt, ingress.TrustedRuntimeContext]:
|
|
39
|
+
trusted = ingress.trusted_context(
|
|
40
|
+
source_kind=source_kind,
|
|
41
|
+
source_ref=idempotency_key,
|
|
42
|
+
session_id=session_id,
|
|
43
|
+
# A foreground compatibility request admits a mutation and then reads
|
|
44
|
+
# the same session's canonical stream. It remains session-bound, but
|
|
45
|
+
# needs explicit authority for both operations.
|
|
46
|
+
operations=("enqueue", "subscribe_events"),
|
|
47
|
+
)
|
|
48
|
+
correlation_kwarg = {
|
|
49
|
+
"map_run_request": "invocation_id",
|
|
50
|
+
"map_responses_request": "response_id",
|
|
51
|
+
"map_agui_request": "run_id",
|
|
52
|
+
"map_a2a_task": "task_id",
|
|
53
|
+
"map_studio_request": "run_id",
|
|
54
|
+
}[mapper]
|
|
55
|
+
command = getattr(ingress, mapper)(
|
|
56
|
+
trusted=trusted,
|
|
57
|
+
session_id=session_id,
|
|
58
|
+
idempotency_key=idempotency_key,
|
|
59
|
+
content=content,
|
|
60
|
+
**({correlation_kwarg: correlation_ref} if correlation_ref else {}),
|
|
61
|
+
**({"runtime_options": runtime_options} if runtime_options else {}),
|
|
62
|
+
)
|
|
63
|
+
receipt = await ingress.submit_command(command, permit=trusted.permit)
|
|
64
|
+
return receipt, trusted
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _kernel_error_response(receipt: AgentControlReceipt) -> JSONResponse:
|
|
68
|
+
return JSONResponse(
|
|
69
|
+
status_code=ingress.receipt_http_status(receipt),
|
|
70
|
+
content={
|
|
71
|
+
"error": ingress.receipt_error_payload(receipt),
|
|
72
|
+
},
|
|
73
|
+
headers=ingress.receipt_response_headers(receipt),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _sse_chunk(payload: dict[str, Any], *, event: str | None, seq: int) -> str:
|
|
78
|
+
prefix = f"event: {event}\n" if event else ""
|
|
79
|
+
return f"id: {seq}\n{prefix}data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def kernel_stream_response(
|
|
83
|
+
*,
|
|
84
|
+
receipt: AgentControlReceipt,
|
|
85
|
+
trusted: ingress.TrustedRuntimeContext,
|
|
86
|
+
session_id: str,
|
|
87
|
+
) -> StreamingResponse:
|
|
88
|
+
"""统一 cursor:从 receipt.accepted_seq 之后读 Session 事件。"""
|
|
89
|
+
|
|
90
|
+
after_seq = int(receipt.accepted_seq or 0)
|
|
91
|
+
|
|
92
|
+
async def generator():
|
|
93
|
+
async for seq, projected in ingress.subscribe_projected(
|
|
94
|
+
session_id,
|
|
95
|
+
trusted=trusted,
|
|
96
|
+
after_seq=after_seq,
|
|
97
|
+
projector=_new_responses_projector(),
|
|
98
|
+
):
|
|
99
|
+
if projected is None:
|
|
100
|
+
continue
|
|
101
|
+
kind, payload = projected
|
|
102
|
+
yield _sse_chunk(payload, event=kind, seq=seq)
|
|
103
|
+
if kind == "response.output_item.done" and (
|
|
104
|
+
(payload.get("item") or {}).get("type") == "mcp_approval_request"
|
|
105
|
+
):
|
|
106
|
+
yield _sse_chunk(
|
|
107
|
+
{"type": "response.incomplete"},
|
|
108
|
+
event="response.incomplete",
|
|
109
|
+
seq=seq,
|
|
110
|
+
)
|
|
111
|
+
return
|
|
112
|
+
# A foreground response stream is scoped to one admitted run.
|
|
113
|
+
# SessionEventStore subscriptions are deliberately long-lived for
|
|
114
|
+
# replay/SSE clients, so do not leave this HTTP response open after
|
|
115
|
+
# the terminal fact has been projected.
|
|
116
|
+
if kind in {"response.completed", "response.failed", "response.canceled"}:
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
return StreamingResponse(generator(), media_type="text/event-stream")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _responses_projector(envelope: Any) -> tuple[str, dict[str, Any]] | None:
|
|
123
|
+
"""Session envelope -> 旧 Responses SSE shape(cursor 仍用 envelope.seq)。"""
|
|
124
|
+
|
|
125
|
+
payload = envelope.payload or {}
|
|
126
|
+
event_type = envelope.event_type
|
|
127
|
+
if event_type == "interaction.requested":
|
|
128
|
+
request = payload.get("request") or {}
|
|
129
|
+
presentation = request.get("presentation") or {}
|
|
130
|
+
description = presentation.get("description") or ""
|
|
131
|
+
try:
|
|
132
|
+
visible = json.loads(description) if description else {}
|
|
133
|
+
except (TypeError, json.JSONDecodeError):
|
|
134
|
+
visible = {}
|
|
135
|
+
arguments = visible.get("arguments") if isinstance(visible, dict) else {}
|
|
136
|
+
return "response.output_item.done", {
|
|
137
|
+
"type": "response.output_item.done",
|
|
138
|
+
"item": {
|
|
139
|
+
"id": str(payload.get("interaction_id") or ""),
|
|
140
|
+
"type": "mcp_approval_request",
|
|
141
|
+
"name": str(presentation.get("title") or payload.get("kind") or "approval"),
|
|
142
|
+
"arguments": json.dumps(arguments or {}, ensure_ascii=False),
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
if event_type == "run.completed":
|
|
146
|
+
text = str(payload.get("output_text") or "")
|
|
147
|
+
return "response.completed", {
|
|
148
|
+
"type": "response.completed",
|
|
149
|
+
"output_text": text,
|
|
150
|
+
"delta": text,
|
|
151
|
+
}
|
|
152
|
+
if event_type == "run.failed":
|
|
153
|
+
return "response.failed", {
|
|
154
|
+
"type": "response.failed",
|
|
155
|
+
"error": payload.get("error") or {"code": "runtime_failed"},
|
|
156
|
+
}
|
|
157
|
+
if event_type in {"run.canceled", "run.interrupted"}:
|
|
158
|
+
return "response.canceled", {
|
|
159
|
+
"type": "response.canceled",
|
|
160
|
+
"reason": payload.get("reason") or event_type,
|
|
161
|
+
}
|
|
162
|
+
text = _envelope_text(payload)
|
|
163
|
+
if text:
|
|
164
|
+
return "response.output_text.delta", {
|
|
165
|
+
"type": "response.output_text.delta",
|
|
166
|
+
"delta": text,
|
|
167
|
+
}
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _new_responses_projector():
|
|
172
|
+
"""Create a session-scoped canonical RuntimeEvent -> Responses projector.
|
|
173
|
+
|
|
174
|
+
``run.completed.output_refs`` deliberately point at canonical items instead
|
|
175
|
+
of duplicating answer text. A projector therefore keeps only the small
|
|
176
|
+
item snapshot needed for this one HTTP/SSE response and resolves those
|
|
177
|
+
refs when the terminal fact arrives.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
item_text: dict[str, str] = {}
|
|
181
|
+
|
|
182
|
+
def project(envelope: Any) -> tuple[str, dict[str, Any]] | None:
|
|
183
|
+
payload = envelope.payload or {}
|
|
184
|
+
event_type = envelope.event_type
|
|
185
|
+
if event_type == "item.updated":
|
|
186
|
+
item_id = str(payload.get("item_id") or "")
|
|
187
|
+
update = payload.get("update") or {}
|
|
188
|
+
text = str(update.get("text") or "")
|
|
189
|
+
if item_id and text:
|
|
190
|
+
item_text[item_id] = (
|
|
191
|
+
text if payload.get("op") == "replace" else item_text.get(item_id, "") + text
|
|
192
|
+
)
|
|
193
|
+
return None
|
|
194
|
+
if event_type == "item.completed":
|
|
195
|
+
item_id = str(payload.get("item_id") or "")
|
|
196
|
+
parts = (payload.get("snapshot") or {}).get("parts") or []
|
|
197
|
+
if item_id and isinstance(parts, list):
|
|
198
|
+
item_text[item_id] = "".join(
|
|
199
|
+
str(part.get("text") or "") for part in parts if isinstance(part, dict)
|
|
200
|
+
)
|
|
201
|
+
return None
|
|
202
|
+
if event_type == "run.completed":
|
|
203
|
+
refs = payload.get("output_refs") or []
|
|
204
|
+
output = "".join(
|
|
205
|
+
item_text.get(str(ref.get("item_id") or ""), "")
|
|
206
|
+
for ref in refs
|
|
207
|
+
if isinstance(ref, dict)
|
|
208
|
+
)
|
|
209
|
+
projected_payload = dict(payload)
|
|
210
|
+
projected_payload["output_text"] = output or str(payload.get("output_text") or "")
|
|
211
|
+
return _responses_projector(
|
|
212
|
+
type("Envelope", (), {"event_type": event_type, "payload": projected_payload})()
|
|
213
|
+
)
|
|
214
|
+
return _responses_projector(envelope)
|
|
215
|
+
|
|
216
|
+
return project
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
async def kernel_conversation_turn(
|
|
220
|
+
*,
|
|
221
|
+
receipt: AgentControlReceipt,
|
|
222
|
+
trusted: ingress.TrustedRuntimeContext,
|
|
223
|
+
session_id: str,
|
|
224
|
+
build_payload,
|
|
225
|
+
):
|
|
226
|
+
"""非流式 kernel 路径:receipt accepted 后订阅聚合 output_text。"""
|
|
227
|
+
|
|
228
|
+
if receipt.status not in ("accepted", "duplicate"):
|
|
229
|
+
return _kernel_error_response(receipt)
|
|
230
|
+
output_text = ""
|
|
231
|
+
async for _seq, projected in ingress.subscribe_projected(
|
|
232
|
+
session_id,
|
|
233
|
+
trusted=trusted,
|
|
234
|
+
after_seq=int(receipt.accepted_seq or 0),
|
|
235
|
+
projector=_new_responses_projector(),
|
|
236
|
+
):
|
|
237
|
+
if projected and projected[0] == "response.completed":
|
|
238
|
+
output_text = str(projected[1].get("output_text") or output_text)
|
|
239
|
+
break
|
|
240
|
+
if projected and projected[0] in {"response.failed", "response.canceled"}:
|
|
241
|
+
return JSONResponse(
|
|
242
|
+
status_code=502,
|
|
243
|
+
content={"error": projected[1]},
|
|
244
|
+
)
|
|
245
|
+
elif projected:
|
|
246
|
+
output_text += str(projected[1].get("delta") or "")
|
|
247
|
+
payload = build_payload(output_text)
|
|
248
|
+
return JSONResponse(
|
|
249
|
+
status_code=ingress.receipt_http_status(receipt),
|
|
250
|
+
content=payload,
|
|
251
|
+
headers=ingress.receipt_response_headers(receipt),
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
__all__ = [
|
|
256
|
+
"kernel_conversation_turn",
|
|
257
|
+
"kernel_stream_response",
|
|
258
|
+
]
|
|
@@ -6,6 +6,7 @@ import uuid
|
|
|
6
6
|
from collections.abc import Mapping
|
|
7
7
|
from typing import Any, Dict, List, Optional
|
|
8
8
|
|
|
9
|
+
from fastapi import HTTPException
|
|
9
10
|
from fastapi.responses import StreamingResponse
|
|
10
11
|
from pydantic import BaseModel
|
|
11
12
|
|
|
@@ -20,11 +21,18 @@ from ksadk.conversations.runtime_streaming import (
|
|
|
20
21
|
stream_runtime_conversation_turn,
|
|
21
22
|
stream_runtime_responses_conversation_turn,
|
|
22
23
|
)
|
|
24
|
+
from ksadk.kernel.ingress import kernel_route_active
|
|
23
25
|
from ksadk.runtime.conversation_execution import invoke_runtime_conversation_once
|
|
24
26
|
from ksadk.server.factory import get_runtime_execution
|
|
25
27
|
|
|
26
28
|
from . import dependencies as deps
|
|
27
29
|
from .checkpoint_resolution import _resolve_checkpoint_resume_input_from_session
|
|
30
|
+
from .kernel_ingress import (
|
|
31
|
+
_kernel_error_response,
|
|
32
|
+
_kernel_submit,
|
|
33
|
+
kernel_conversation_turn,
|
|
34
|
+
kernel_stream_response,
|
|
35
|
+
)
|
|
28
36
|
from .models import (
|
|
29
37
|
ResponsesRequest,
|
|
30
38
|
_clean_optional_string,
|
|
@@ -60,6 +68,26 @@ async def list_openai_models():
|
|
|
60
68
|
"""Expose the current model catalog through the OpenAI-compatible path."""
|
|
61
69
|
|
|
62
70
|
payload = await _build_models_payload()
|
|
71
|
+
try:
|
|
72
|
+
_executor, launch_context = get_runtime_execution()
|
|
73
|
+
except HTTPException:
|
|
74
|
+
launch_context = None
|
|
75
|
+
if launch_context is not None:
|
|
76
|
+
config = dict(launch_context.config)
|
|
77
|
+
raw_allowed = (
|
|
78
|
+
config.get("models") or config.get("allowed_models") or config.get("allowedModels")
|
|
79
|
+
)
|
|
80
|
+
if isinstance(raw_allowed, (list, tuple, set)):
|
|
81
|
+
allowed = {str(item).strip() for item in raw_allowed if str(item).strip()}
|
|
82
|
+
default_model = str(config.get("model") or "").strip()
|
|
83
|
+
if default_model:
|
|
84
|
+
allowed.add(default_model)
|
|
85
|
+
payload = dict(payload)
|
|
86
|
+
payload["data"] = [
|
|
87
|
+
item
|
|
88
|
+
for item in payload.get("data", [])
|
|
89
|
+
if str(item.get("id") or "").strip() in allowed
|
|
90
|
+
]
|
|
63
91
|
return {
|
|
64
92
|
"object": "list",
|
|
65
93
|
"data": payload.get("data", []),
|
|
@@ -72,6 +100,8 @@ async def list_openai_models():
|
|
|
72
100
|
async def responses(request: ResponsesRequest):
|
|
73
101
|
"""OpenAI Responses 兼容接口。"""
|
|
74
102
|
executor, launch_context = get_runtime_execution()
|
|
103
|
+
if kernel_route_active():
|
|
104
|
+
return await _kernel_responses(request, launch_context)
|
|
75
105
|
resolved_session_id, resolved_user_id = _resolve_responses_session_and_user(request)
|
|
76
106
|
agent_id = _runtime_agent_id(launch_context)
|
|
77
107
|
|
|
@@ -82,11 +112,7 @@ async def responses(request: ResponsesRequest):
|
|
|
82
112
|
session_id=resolved_session_id,
|
|
83
113
|
resume_input=resume_input,
|
|
84
114
|
)
|
|
85
|
-
messages = (
|
|
86
|
-
[]
|
|
87
|
-
if resume_input is not None
|
|
88
|
-
else normalize_responses_input(request.input)
|
|
89
|
-
)
|
|
115
|
+
messages = [] if resume_input is not None else normalize_responses_input(request.input)
|
|
90
116
|
custom_metadata, request_metadata = _split_custom_metadata(request.metadata)
|
|
91
117
|
if request.previous_response_id:
|
|
92
118
|
request_metadata["previous_response_id"] = request.previous_response_id
|
|
@@ -105,9 +131,7 @@ async def responses(request: ResponsesRequest):
|
|
|
105
131
|
|
|
106
132
|
if request.stream:
|
|
107
133
|
runtime_preparation = (
|
|
108
|
-
None
|
|
109
|
-
if resume_input is not None
|
|
110
|
-
else await executor.prepare_start(launch_context)
|
|
134
|
+
None if resume_input is not None else await executor.prepare_start(launch_context)
|
|
111
135
|
)
|
|
112
136
|
resume_key = _detached_resume_key_from_input(resolved_session_id, resume_input)
|
|
113
137
|
_reject_if_detached_resume_active(resume_key)
|
|
@@ -169,6 +193,62 @@ async def responses(request: ResponsesRequest):
|
|
|
169
193
|
)
|
|
170
194
|
|
|
171
195
|
|
|
196
|
+
async def _kernel_responses(request: ResponsesRequest, launch_context):
|
|
197
|
+
"""kernel 路径(灰度 opt-in):Responses -> AgentControlCommand -> receipt。"""
|
|
198
|
+
|
|
199
|
+
from ksadk.conversations.runtime_persistence import ensure_conversation_session
|
|
200
|
+
|
|
201
|
+
resolved_session_id, resolved_user_id = _resolve_responses_session_and_user(request)
|
|
202
|
+
session = await ensure_conversation_session(
|
|
203
|
+
agent_id=_runtime_agent_id(launch_context),
|
|
204
|
+
user_id=resolved_user_id,
|
|
205
|
+
session_id=resolved_session_id,
|
|
206
|
+
session_service_provider=deps.resolve_session_service,
|
|
207
|
+
)
|
|
208
|
+
session_id = session.id
|
|
209
|
+
metadata = request.metadata if isinstance(request.metadata, dict) else {}
|
|
210
|
+
idempotency_key = (
|
|
211
|
+
_clean_optional_string(metadata.get("idempotency_key"))
|
|
212
|
+
or _metadata_invocation_id(metadata)
|
|
213
|
+
or f"resp_{uuid.uuid4().hex}"
|
|
214
|
+
)
|
|
215
|
+
messages = normalize_responses_input(request.input)
|
|
216
|
+
response_id = f"resp_{uuid.uuid4().hex}"
|
|
217
|
+
receipt, trusted = await _kernel_submit(
|
|
218
|
+
mapper="map_responses_request",
|
|
219
|
+
session_id=session_id,
|
|
220
|
+
idempotency_key=idempotency_key,
|
|
221
|
+
content=messages,
|
|
222
|
+
correlation_ref=response_id,
|
|
223
|
+
source_kind="responses",
|
|
224
|
+
)
|
|
225
|
+
if receipt.status not in ("accepted", "duplicate"):
|
|
226
|
+
return _kernel_error_response(receipt)
|
|
227
|
+
|
|
228
|
+
def build_payload(output_text: str):
|
|
229
|
+
return build_responses_payload(
|
|
230
|
+
output_text=output_text,
|
|
231
|
+
model=request.model,
|
|
232
|
+
session_id=session_id,
|
|
233
|
+
response_id=response_id,
|
|
234
|
+
metadata=None,
|
|
235
|
+
usage=None,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
if request.stream:
|
|
239
|
+
return kernel_stream_response(
|
|
240
|
+
receipt=receipt,
|
|
241
|
+
trusted=trusted,
|
|
242
|
+
session_id=session_id,
|
|
243
|
+
)
|
|
244
|
+
return await kernel_conversation_turn(
|
|
245
|
+
receipt=receipt,
|
|
246
|
+
trusted=trusted,
|
|
247
|
+
session_id=session_id,
|
|
248
|
+
build_payload=build_payload,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
172
252
|
@openai_compat_router.post("/v1/chat/completions")
|
|
173
253
|
async def chat_completions(request: ChatCompletionRequest):
|
|
174
254
|
"""OpenAI 兼容的聊天补全接口 (支持流式和非流式)"""
|