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
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
"""Durable schema-v2 RuntimeEvent storage on the existing session event log.
|
|
2
|
+
|
|
3
|
+
The canonical event envelope intentionally has no ``session_id``. Session
|
|
4
|
+
scope is therefore an explicit store argument and never hidden in source
|
|
5
|
+
metadata. The physical ``SessionEvent.id`` is a deterministic encoding of
|
|
6
|
+
``(session_id, event_id)`` so the existing durable primary-key constraint can
|
|
7
|
+
enforce the canonical idempotency domain before a session cursor is allocated.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import hashlib
|
|
14
|
+
import json
|
|
15
|
+
import uuid
|
|
16
|
+
from collections.abc import AsyncIterator, Iterable
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
19
|
+
|
|
20
|
+
from ksadk.events.canonical import RuntimeEvent, dump_runtime_event, parse_runtime_event
|
|
21
|
+
from ksadk.kernel.contracts import ActivationWriteGuard, SessionEventEnvelope
|
|
22
|
+
from ksadk.sessions.base import SessionEvent, SessionEventSeqBinding
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from ksadk.events.session_event import SessionEventStore
|
|
26
|
+
|
|
27
|
+
_CANONICAL_RUNTIME_MARKER = "ksadk_canonical_runtime_event"
|
|
28
|
+
_CANONICAL_CONTENT_KEY = "runtime_event"
|
|
29
|
+
_ENVELOPE_MARKER = "ksadk_session_event_envelope"
|
|
30
|
+
_TERMINAL_EVENT_TYPES = frozenset({"run.completed", "run.failed", "run.canceled"})
|
|
31
|
+
|
|
32
|
+
_REQUIRED_SEQ_BINDING: SessionEventSeqBinding = "runtime_event.seq"
|
|
33
|
+
|
|
34
|
+
# Stable namespace for mapping free-form RuntimeEvent event ids onto the
|
|
35
|
+
# UUID-typed ``SessionEventEnvelope.event_id`` contract.
|
|
36
|
+
_RUNTIME_EVENT_UUID_NAMESPACE = uuid.UUID("6e9f0c5a-2f4d-4d8a-9b31-1c2a5f7e9b41")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def runtime_event_envelope_id(session_id: str, event_id: str) -> uuid.UUID:
|
|
40
|
+
"""UUID contract id for one runtime fact (deterministic per session)."""
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
return uuid.UUID(str(event_id))
|
|
44
|
+
except (ValueError, AttributeError, TypeError):
|
|
45
|
+
return uuid.uuid5(_RUNTIME_EVENT_UUID_NAMESPACE, f"{session_id}|{event_id}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def runtime_event_envelope(session_id: str, event: RuntimeEvent) -> SessionEventEnvelope:
|
|
49
|
+
"""Lift one canonical RuntimeEvent fact into a family=runtime/v2 envelope."""
|
|
50
|
+
|
|
51
|
+
if getattr(event, "schema_version", None) != 2:
|
|
52
|
+
raise ValueError("canonical RuntimeEventStore accepts schema_version=2 only")
|
|
53
|
+
timestamp = datetime.fromtimestamp(event.timestamp, tz=timezone.utc).isoformat()
|
|
54
|
+
return SessionEventEnvelope(
|
|
55
|
+
event_id=runtime_event_envelope_id(session_id, event.event_id),
|
|
56
|
+
session_id=session_id,
|
|
57
|
+
seq=0, # overwritten with the store-allocated cursor on persistence
|
|
58
|
+
timestamp=timestamp,
|
|
59
|
+
family="runtime",
|
|
60
|
+
family_version=2,
|
|
61
|
+
event_type=event.event_type,
|
|
62
|
+
payload=dump_runtime_event(event),
|
|
63
|
+
run_id=event.run_id,
|
|
64
|
+
actor_ref=event.source.framework,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def canonical_storage_id(session_id: str, event_id: str) -> str:
|
|
69
|
+
"""Return a stable physical id distinct from the producer event id."""
|
|
70
|
+
|
|
71
|
+
if not session_id.strip() or not event_id.strip():
|
|
72
|
+
raise ValueError("session_id and event_id must be nonempty")
|
|
73
|
+
encoded = json.dumps([session_id, event_id], ensure_ascii=False, separators=(",", ":")).encode(
|
|
74
|
+
"utf-8"
|
|
75
|
+
)
|
|
76
|
+
return f"cev_{hashlib.sha256(encoded).hexdigest()[:40]}"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def runtime_event_to_session_event(session_id: str, event: RuntimeEvent) -> SessionEvent:
|
|
80
|
+
"""Pack one canonical fact into the existing free-form SessionEvent carrier."""
|
|
81
|
+
|
|
82
|
+
if getattr(event, "schema_version", None) != 2:
|
|
83
|
+
raise ValueError("canonical RuntimeEventStore accepts schema_version=2 only")
|
|
84
|
+
payload = dump_runtime_event(event)
|
|
85
|
+
return SessionEvent(
|
|
86
|
+
id=canonical_storage_id(session_id, event.event_id),
|
|
87
|
+
session_id=session_id,
|
|
88
|
+
author=event.source.framework,
|
|
89
|
+
event_type=event.event_type,
|
|
90
|
+
content={_CANONICAL_CONTENT_KEY: payload},
|
|
91
|
+
timestamp=event.timestamp,
|
|
92
|
+
invocation_id=event.run_id,
|
|
93
|
+
metadata={
|
|
94
|
+
_CANONICAL_RUNTIME_MARKER: True,
|
|
95
|
+
"schema_version": 2,
|
|
96
|
+
"canonical_event_id": event.event_id,
|
|
97
|
+
},
|
|
98
|
+
seq_binding=_REQUIRED_SEQ_BINDING,
|
|
99
|
+
seq_id=int(event.seq),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def session_event_to_runtime_event(event: SessionEvent) -> RuntimeEvent | None:
|
|
104
|
+
"""Restore a canonical fact, using the physical session cursor as ``seq``."""
|
|
105
|
+
|
|
106
|
+
metadata = event.metadata or {}
|
|
107
|
+
if metadata.get(_ENVELOPE_MARKER) and metadata.get("family") == "runtime":
|
|
108
|
+
# Task 2 typed view rows: family=runtime/v2 written through the
|
|
109
|
+
# generic SessionEventStore envelope carrier.
|
|
110
|
+
payload = (event.content or {}).get(_CANONICAL_CONTENT_KEY)
|
|
111
|
+
if not isinstance(payload, dict):
|
|
112
|
+
raise ValueError("runtime family SessionEvent is missing runtime_event content")
|
|
113
|
+
if payload.get("seq") != event.seq_id:
|
|
114
|
+
raise ValueError("canonical RuntimeEvent seq does not match physical seq")
|
|
115
|
+
return parse_runtime_event(payload)
|
|
116
|
+
if not metadata.get(_CANONICAL_RUNTIME_MARKER):
|
|
117
|
+
return None
|
|
118
|
+
if metadata.get("schema_version") != 2:
|
|
119
|
+
raise ValueError("canonical SessionEvent marker requires schema_version=2")
|
|
120
|
+
stored_payload = (event.content or {}).get(_CANONICAL_CONTENT_KEY)
|
|
121
|
+
if not isinstance(stored_payload, dict):
|
|
122
|
+
raise ValueError("canonical SessionEvent is missing runtime_event content")
|
|
123
|
+
if stored_payload.get("seq") != event.seq_id:
|
|
124
|
+
raise ValueError("canonical RuntimeEvent seq does not match physical seq")
|
|
125
|
+
payload = dict(stored_payload)
|
|
126
|
+
restored = parse_runtime_event(payload)
|
|
127
|
+
canonical_event_id = str(metadata.get("canonical_event_id") or "")
|
|
128
|
+
if canonical_event_id != restored.event_id:
|
|
129
|
+
raise ValueError("canonical SessionEvent event id metadata does not match content")
|
|
130
|
+
expected_storage_id = canonical_storage_id(event.session_id, restored.event_id)
|
|
131
|
+
if event.id != expected_storage_id:
|
|
132
|
+
raise ValueError("canonical SessionEvent storage id does not match session event identity")
|
|
133
|
+
if event.invocation_id != restored.run_id or event.event_type != restored.event_type:
|
|
134
|
+
raise ValueError("canonical SessionEvent envelope does not match runtime event content")
|
|
135
|
+
return restored
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _is_session_event_store(candidate: Any) -> bool:
|
|
139
|
+
"""Duck-type the generic SessionEventStore port without an import cycle."""
|
|
140
|
+
|
|
141
|
+
return all(
|
|
142
|
+
callable(getattr(candidate, name, None)) for name in ("append", "read", "subscribe")
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class RuntimeEventStore:
|
|
147
|
+
"""Schema-v2-only canonical store with durable session-scoped idempotency.
|
|
148
|
+
|
|
149
|
+
Task 2 起 ``RuntimeEventStore`` 是单一 SessionEvent Store 的 typed view:
|
|
150
|
+
传入 ``SessionEventStore`` 时走 envelope 写路径(只接受
|
|
151
|
+
``ActivationWriteGuard``),传 session service 时保持旧 carrier 兼容路径。
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
def __init__(self, store: Any, *, session_id: str | None = None) -> None:
|
|
155
|
+
if _is_session_event_store(store):
|
|
156
|
+
self._event_store: SessionEventStore | None = store
|
|
157
|
+
self._service = getattr(store, "session_service", None)
|
|
158
|
+
self._typed_session_id = session_id
|
|
159
|
+
else:
|
|
160
|
+
self._event_store = None
|
|
161
|
+
self._service = store
|
|
162
|
+
self._typed_session_id = session_id
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def session_service(self) -> Any:
|
|
166
|
+
return self._service
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def session_id(self) -> str | None:
|
|
170
|
+
"""Session bound at construction for the typed (envelope) write path."""
|
|
171
|
+
|
|
172
|
+
return self._typed_session_id
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def event_store(self) -> "SessionEventStore | None":
|
|
176
|
+
return self._event_store
|
|
177
|
+
|
|
178
|
+
async def append(
|
|
179
|
+
self,
|
|
180
|
+
session_id_or_event: Any,
|
|
181
|
+
events: Iterable[RuntimeEvent] | None = None,
|
|
182
|
+
*,
|
|
183
|
+
guard: ActivationWriteGuard | None = None,
|
|
184
|
+
) -> Any:
|
|
185
|
+
"""Typed view append: ``append(event, *, guard=ActivationWriteGuard)``.
|
|
186
|
+
|
|
187
|
+
旧签名 ``append(session_id, events)`` 保持兼容(carrier 路径)。
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
if not isinstance(session_id_or_event, str):
|
|
191
|
+
if events is not None:
|
|
192
|
+
raise TypeError("typed append takes a single RuntimeEvent")
|
|
193
|
+
if not isinstance(guard, ActivationWriteGuard):
|
|
194
|
+
raise TypeError(
|
|
195
|
+
"RuntimeEventStore typed append requires an ActivationWriteGuard"
|
|
196
|
+
)
|
|
197
|
+
return await self.append_typed(session_id_or_event, guard=guard)
|
|
198
|
+
if guard is not None:
|
|
199
|
+
raise TypeError("legacy append(session_id, events) does not take a guard")
|
|
200
|
+
return [await self.append_one(session_id_or_event, event) for event in events or ()]
|
|
201
|
+
|
|
202
|
+
async def append_typed(
|
|
203
|
+
self, event: RuntimeEvent, *, guard: ActivationWriteGuard
|
|
204
|
+
) -> RuntimeEvent:
|
|
205
|
+
"""Persist one fact through the generic SessionEventStore envelope."""
|
|
206
|
+
|
|
207
|
+
if self._event_store is None:
|
|
208
|
+
raise RuntimeError("typed append requires a SessionEventStore-backed runtime view")
|
|
209
|
+
if self._typed_session_id is None or not self._typed_session_id.strip():
|
|
210
|
+
raise ValueError("typed append requires a session_id bound at construction")
|
|
211
|
+
if not isinstance(guard, ActivationWriteGuard):
|
|
212
|
+
raise TypeError("RuntimeEventStore only accepts ActivationWriteGuard")
|
|
213
|
+
envelope = runtime_event_envelope(self._typed_session_id, event)
|
|
214
|
+
persisted = await self._event_store.append(envelope, guard=guard)
|
|
215
|
+
return parse_runtime_event(dict(persisted.payload) | {"seq": persisted.seq})
|
|
216
|
+
|
|
217
|
+
async def append_one(self, session_id: str, event: RuntimeEvent) -> RuntimeEvent:
|
|
218
|
+
persisted, _created = await self.persist_one(session_id, event)
|
|
219
|
+
return persisted
|
|
220
|
+
|
|
221
|
+
async def persist_one(self, session_id: str, event: RuntimeEvent) -> tuple[RuntimeEvent, bool]:
|
|
222
|
+
"""Persist before publication and return whether this call created the fact."""
|
|
223
|
+
|
|
224
|
+
if getattr(event, "schema_version", None) != 2:
|
|
225
|
+
raise ValueError("canonical RuntimeEventStore accepts schema_version=2 only")
|
|
226
|
+
if not session_id.strip():
|
|
227
|
+
raise ValueError("session_id must be nonempty")
|
|
228
|
+
self._require_storage_capabilities()
|
|
229
|
+
existing = await self.event_by_id(session_id, event.event_id)
|
|
230
|
+
if existing is not None:
|
|
231
|
+
self._assert_same_fact(existing, event)
|
|
232
|
+
return existing, False
|
|
233
|
+
packed = runtime_event_to_session_event(session_id, event)
|
|
234
|
+
try:
|
|
235
|
+
stored = await self._service.append_event(session_id, packed)
|
|
236
|
+
except Exception:
|
|
237
|
+
# The deterministic physical id turns concurrent appends into an
|
|
238
|
+
# insert-winner/insert-loser race on durable backends. Re-read the
|
|
239
|
+
# winner and only absorb the error when it is the same fact.
|
|
240
|
+
existing = await self.event_by_id(session_id, event.event_id)
|
|
241
|
+
if existing is None:
|
|
242
|
+
raise
|
|
243
|
+
self._assert_same_fact(existing, event)
|
|
244
|
+
return existing, False
|
|
245
|
+
persisted = session_event_to_runtime_event(stored)
|
|
246
|
+
if persisted is None: # pragma: no cover - packed by this module
|
|
247
|
+
raise RuntimeError("canonical RuntimeEvent lost its storage marker")
|
|
248
|
+
self._assert_same_fact(persisted, event)
|
|
249
|
+
return persisted, True
|
|
250
|
+
|
|
251
|
+
async def _read_rows(
|
|
252
|
+
self,
|
|
253
|
+
session_id: str,
|
|
254
|
+
after_seq: int,
|
|
255
|
+
before_seq: int | None,
|
|
256
|
+
*,
|
|
257
|
+
limit: int | None = None,
|
|
258
|
+
):
|
|
259
|
+
"""Typed envelope 路径的读取兜底。
|
|
260
|
+
|
|
261
|
+
hosted PG 的 ``PostgresFencedSessionEventStore`` 只包 kernel store,
|
|
262
|
+
没有 ``session_service``(``_service is None``)。冷恢复
|
|
263
|
+
(scan_open_runs -> list) 在此之前会 AttributeError,导致 takeover
|
|
264
|
+
recovery 双路径失败 -> runtime degraded。改走 event store 自己的
|
|
265
|
+
``read``(envelope 语义)再转 SessionEvent 行。
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
if self._service is not None:
|
|
269
|
+
return await self._service.get_events(
|
|
270
|
+
session_id,
|
|
271
|
+
limit=limit,
|
|
272
|
+
after_seq_id=after_seq,
|
|
273
|
+
before_seq_id=before_seq,
|
|
274
|
+
)
|
|
275
|
+
if self._event_store is None:
|
|
276
|
+
raise RuntimeError(
|
|
277
|
+
"RuntimeEventStore has neither a session service nor an event store"
|
|
278
|
+
)
|
|
279
|
+
rows = []
|
|
280
|
+
from ksadk.events.session_event import envelope_to_session_event
|
|
281
|
+
|
|
282
|
+
for envelope in await self._event_store.read(
|
|
283
|
+
session_id, int(after_seq), int(limit or 100_000)
|
|
284
|
+
):
|
|
285
|
+
if before_seq is not None and int(envelope.seq) >= int(before_seq):
|
|
286
|
+
break
|
|
287
|
+
row = envelope_to_session_event(envelope)
|
|
288
|
+
if int(row.seq_id or 0) != int(envelope.seq):
|
|
289
|
+
row.seq_id = int(envelope.seq)
|
|
290
|
+
rows.append(row)
|
|
291
|
+
return rows
|
|
292
|
+
|
|
293
|
+
async def page(
|
|
294
|
+
self,
|
|
295
|
+
session_id: str,
|
|
296
|
+
*,
|
|
297
|
+
after_seq: int = 0,
|
|
298
|
+
before_seq: int | None = None,
|
|
299
|
+
limit: int = 500,
|
|
300
|
+
) -> list[RuntimeEvent]:
|
|
301
|
+
"""Read the next canonical page in ascending physical cursor order.
|
|
302
|
+
|
|
303
|
+
``list(..., limit=...)`` is a compatibility tail projection. Durable
|
|
304
|
+
export and replay callers that need bounded forward pagination must use
|
|
305
|
+
this explicit method, otherwise a large session can be read wholesale
|
|
306
|
+
before Python applies its limit.
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
if limit < 1:
|
|
310
|
+
raise ValueError("limit must be positive")
|
|
311
|
+
raw = await self._read_rows(
|
|
312
|
+
session_id,
|
|
313
|
+
int(after_seq),
|
|
314
|
+
before_seq,
|
|
315
|
+
limit=limit,
|
|
316
|
+
)
|
|
317
|
+
events = [
|
|
318
|
+
canonical
|
|
319
|
+
for canonical in (session_event_to_runtime_event(item) for item in raw)
|
|
320
|
+
if canonical is not None
|
|
321
|
+
]
|
|
322
|
+
events.sort(key=lambda event: event.seq)
|
|
323
|
+
return events[:limit]
|
|
324
|
+
|
|
325
|
+
async def event_by_id(self, session_id: str, event_id: str) -> RuntimeEvent | None:
|
|
326
|
+
if self._service is not None:
|
|
327
|
+
self._require_storage_capabilities()
|
|
328
|
+
storage_id = canonical_storage_id(session_id, event_id)
|
|
329
|
+
stored = await self._service.get_event_by_id(session_id, storage_id)
|
|
330
|
+
return session_event_to_runtime_event(stored) if stored is not None else None
|
|
331
|
+
for event in await self.list(session_id):
|
|
332
|
+
if event.event_id == event_id:
|
|
333
|
+
return event
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
async def resolve_existing(
|
|
337
|
+
self, session_id: str, candidate: RuntimeEvent
|
|
338
|
+
) -> RuntimeEvent | None:
|
|
339
|
+
"""Return an identical durable fact or raise for an id collision."""
|
|
340
|
+
|
|
341
|
+
existing = await self.event_by_id(session_id, candidate.event_id)
|
|
342
|
+
if existing is not None:
|
|
343
|
+
self._assert_same_fact(existing, candidate)
|
|
344
|
+
return existing
|
|
345
|
+
|
|
346
|
+
async def list(
|
|
347
|
+
self,
|
|
348
|
+
session_id: str,
|
|
349
|
+
*,
|
|
350
|
+
after_seq: int = 0,
|
|
351
|
+
before_seq: int | None = None,
|
|
352
|
+
run_id: str | None = None,
|
|
353
|
+
limit: int | None = None,
|
|
354
|
+
) -> list[RuntimeEvent]:
|
|
355
|
+
# Run replay uses the backend's invocation index; session replay still
|
|
356
|
+
# reads the shared physical cursor log and filters legacy rows here.
|
|
357
|
+
if run_id is None or self._service is None:
|
|
358
|
+
# run 过滤在 typed envelope 兜底路径上退化为全量读取后按
|
|
359
|
+
# run_id 过滤(fenced store 没有按 invocation 的索引查询)。
|
|
360
|
+
raw = await self._read_rows(session_id, after_seq, before_seq)
|
|
361
|
+
else:
|
|
362
|
+
self._require_storage_capabilities()
|
|
363
|
+
raw = await self._service.get_events_by_invocation_id(
|
|
364
|
+
session_id,
|
|
365
|
+
run_id,
|
|
366
|
+
after_seq_id=after_seq,
|
|
367
|
+
before_seq_id=before_seq,
|
|
368
|
+
)
|
|
369
|
+
events = [
|
|
370
|
+
canonical
|
|
371
|
+
for canonical in (session_event_to_runtime_event(item) for item in raw)
|
|
372
|
+
if canonical is not None and (run_id is None or canonical.run_id == run_id)
|
|
373
|
+
]
|
|
374
|
+
events.sort(key=lambda event: event.seq)
|
|
375
|
+
if limit is not None:
|
|
376
|
+
if limit < 1:
|
|
377
|
+
raise ValueError("limit must be positive")
|
|
378
|
+
events = events[-limit:]
|
|
379
|
+
return events
|
|
380
|
+
|
|
381
|
+
async def list_run_ids(self, session_id: str) -> list[str]:
|
|
382
|
+
"""Distinct run ids in session order of first appearance."""
|
|
383
|
+
|
|
384
|
+
seen: dict[str, None] = {}
|
|
385
|
+
for event in await self.list(session_id):
|
|
386
|
+
seen.setdefault(event.run_id, None)
|
|
387
|
+
return list(seen)
|
|
388
|
+
|
|
389
|
+
async def subscribe_session(
|
|
390
|
+
self,
|
|
391
|
+
session_id: str,
|
|
392
|
+
*,
|
|
393
|
+
after_seq: int = 0,
|
|
394
|
+
poll_interval: float = 0.25,
|
|
395
|
+
timeout: float = 5 * 60,
|
|
396
|
+
) -> AsyncIterator[RuntimeEvent]:
|
|
397
|
+
cursor = int(after_seq or 0)
|
|
398
|
+
deadline = asyncio.get_running_loop().time() + timeout
|
|
399
|
+
while True:
|
|
400
|
+
rows = await self._service.get_events(session_id, after_seq_id=cursor)
|
|
401
|
+
rows.sort(key=lambda event: event.seq_id)
|
|
402
|
+
for row in rows:
|
|
403
|
+
event = session_event_to_runtime_event(row)
|
|
404
|
+
cursor = row.seq_id
|
|
405
|
+
if event is not None:
|
|
406
|
+
yield event
|
|
407
|
+
if asyncio.get_running_loop().time() >= deadline:
|
|
408
|
+
return
|
|
409
|
+
await asyncio.sleep(poll_interval)
|
|
410
|
+
|
|
411
|
+
async def subscribe_run(
|
|
412
|
+
self,
|
|
413
|
+
session_id: str,
|
|
414
|
+
run_id: str,
|
|
415
|
+
*,
|
|
416
|
+
after_seq: int = 0,
|
|
417
|
+
poll_interval: float = 0.25,
|
|
418
|
+
timeout: float = 5 * 60,
|
|
419
|
+
) -> AsyncIterator[RuntimeEvent]:
|
|
420
|
+
cursor = int(after_seq or 0)
|
|
421
|
+
deadline = asyncio.get_running_loop().time() + timeout
|
|
422
|
+
while True:
|
|
423
|
+
rows = await self._service.get_events(session_id, after_seq_id=cursor)
|
|
424
|
+
rows.sort(key=lambda event: event.seq_id)
|
|
425
|
+
for row in rows:
|
|
426
|
+
event = session_event_to_runtime_event(row)
|
|
427
|
+
cursor = row.seq_id
|
|
428
|
+
if event is not None and event.run_id == run_id:
|
|
429
|
+
yield event
|
|
430
|
+
if event.event_type in _TERMINAL_EVENT_TYPES:
|
|
431
|
+
return
|
|
432
|
+
if asyncio.get_running_loop().time() >= deadline:
|
|
433
|
+
return
|
|
434
|
+
await asyncio.sleep(poll_interval)
|
|
435
|
+
|
|
436
|
+
@staticmethod
|
|
437
|
+
def _assert_same_fact(existing: RuntimeEvent, candidate: RuntimeEvent) -> None:
|
|
438
|
+
existing_payload = dump_runtime_event(existing)
|
|
439
|
+
candidate_payload = dump_runtime_event(candidate)
|
|
440
|
+
# ``seq`` is the store-assigned delivery cursor, not producer fact
|
|
441
|
+
# identity. Every other canonical field participates in collision
|
|
442
|
+
# validation, including timestamp, source, run_seq and typed content.
|
|
443
|
+
existing_payload.pop("seq", None)
|
|
444
|
+
candidate_payload.pop("seq", None)
|
|
445
|
+
if existing_payload != candidate_payload:
|
|
446
|
+
raise ValueError(f"RuntimeEvent id collision for {candidate.event_id!r}")
|
|
447
|
+
|
|
448
|
+
def _require_storage_capabilities(self) -> None:
|
|
449
|
+
capabilities = self._service.storage_capabilities
|
|
450
|
+
if (
|
|
451
|
+
_REQUIRED_SEQ_BINDING not in capabilities.atomic_seq_bindings
|
|
452
|
+
or not capabilities.indexed_event_lookup
|
|
453
|
+
or not capabilities.indexed_invocation_lookup
|
|
454
|
+
):
|
|
455
|
+
raise RuntimeError(
|
|
456
|
+
"session backend must support atomic runtime_event.seq binding "
|
|
457
|
+
"and indexed physical event lookup and indexed invocation lookup"
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
__all__ = [
|
|
462
|
+
"RuntimeEventStore",
|
|
463
|
+
"canonical_storage_id",
|
|
464
|
+
"runtime_event_to_session_event",
|
|
465
|
+
"session_event_to_runtime_event",
|
|
466
|
+
"runtime_event_envelope",
|
|
467
|
+
"runtime_event_envelope_id",
|
|
468
|
+
]
|