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,698 @@
|
|
|
1
|
+
"""ContextPlanner —— 预算、required/group 原子性与确定性缩减(方案 §8.4 / §8.5)。
|
|
2
|
+
|
|
3
|
+
Planner 是 Context Engine 的决策核心:输入候选项 ``ContextItem`` 与 ``ContextBudget``,
|
|
4
|
+
输出 ``ContextPlan``(selected + decisions)。强制优先级遵循方案 §8.4 第 11 条;group 原子性
|
|
5
|
+
保证 tool call/result、approval request/response、Responses call/output 不被拆散
|
|
6
|
+
(方案 §8.1 ``group_id``)。
|
|
7
|
+
|
|
8
|
+
本模块纯计算、无副作用、可复用;不接触 Session Store、不调用模型。replan 由调用方在
|
|
9
|
+
compaction/PTL 后通过新 ``plan_id`` 重新调用(方案 §11.1 / ADR-016)。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import uuid
|
|
15
|
+
from typing import Iterable
|
|
16
|
+
|
|
17
|
+
from ksadk.context_engine.models import (
|
|
18
|
+
CONTEXT_POLICY_VERSION,
|
|
19
|
+
ContextBudget,
|
|
20
|
+
ContextDecision,
|
|
21
|
+
ContextItem,
|
|
22
|
+
ContextKind,
|
|
23
|
+
ContextPlan,
|
|
24
|
+
)
|
|
25
|
+
from ksadk.context_engine.policies import ContextBudgetPolicy, SectionBudget
|
|
26
|
+
|
|
27
|
+
# 强制优先级(方案 §8.4):数字越小越优先保留。
|
|
28
|
+
# platform_safety(1) → current_input(2) → pending approval/tool(3) → receipt/framework ref(4)
|
|
29
|
+
# → agent identity/policy(5) → checkpoint summary(6) → working state(7) → recent rounds(8)
|
|
30
|
+
# → core memory(9) → recall memory(10) → optional skill/旧 tool/附件(11)
|
|
31
|
+
_PRIORITY_RANK: dict[ContextKind, int] = {
|
|
32
|
+
"compiled_prompt": 1, # 含 platform_safety/agent_identity/agent_policy
|
|
33
|
+
"current_input": 2,
|
|
34
|
+
"tool_result": 3, # pending tool 状态随 round 保留
|
|
35
|
+
"working_state": 7,
|
|
36
|
+
"checkpoint_summary": 6,
|
|
37
|
+
"core_memory": 9,
|
|
38
|
+
"recalled_memory": 10,
|
|
39
|
+
"history_round": 8,
|
|
40
|
+
"skill_content": 11,
|
|
41
|
+
"attachment_context": 11,
|
|
42
|
+
"resource_manifest": 5,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# 分区 key 映射(方案 §8.3 分区预算表 → ContextKind)。
|
|
46
|
+
_KIND_TO_SECTION: dict[ContextKind, str] = {
|
|
47
|
+
"compiled_prompt": "prompt",
|
|
48
|
+
"resource_manifest": "resource_manifest",
|
|
49
|
+
"core_memory": "core_memory",
|
|
50
|
+
"recalled_memory": "recalled_memory",
|
|
51
|
+
"checkpoint_summary": "checkpoint_summary",
|
|
52
|
+
"working_state": "working_state",
|
|
53
|
+
"history_round": "recent_history",
|
|
54
|
+
"tool_result": "tool_and_attachment",
|
|
55
|
+
"attachment_context": "tool_and_attachment",
|
|
56
|
+
"skill_content": "tool_and_attachment",
|
|
57
|
+
"current_input": "recent_history",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _tokens(items: Iterable[ContextItem]) -> int:
|
|
62
|
+
return sum(i.estimated_tokens for i in items)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _group_groups(items: list[ContextItem]) -> dict[str, list[ContextItem]]:
|
|
66
|
+
"""按 ``group_id`` 聚合,无 group_id 的各自成组。"""
|
|
67
|
+
groups: dict[str, list[ContextItem]] = {}
|
|
68
|
+
for item in items:
|
|
69
|
+
key = item.group_id or item.item_id
|
|
70
|
+
groups.setdefault(key, []).append(item)
|
|
71
|
+
return groups
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _section_for(kind: ContextKind) -> str:
|
|
75
|
+
return _KIND_TO_SECTION.get(kind, "tool_and_attachment")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ContextPlanner:
|
|
79
|
+
"""确定性 Context 规划器(方案 §8.4 / §8.5)。
|
|
80
|
+
|
|
81
|
+
``plan()`` 对相同输入产生相同输出(确定性排序 + 确定性缩减)。无状态、可复用。
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, *, policy: ContextBudgetPolicy | None = None) -> None:
|
|
85
|
+
self._policy = policy or ContextBudgetPolicy()
|
|
86
|
+
|
|
87
|
+
def plan(
|
|
88
|
+
self,
|
|
89
|
+
candidates: list[ContextItem],
|
|
90
|
+
*,
|
|
91
|
+
budget: ContextBudget,
|
|
92
|
+
integration_mode: str = "ksadk_hosted",
|
|
93
|
+
accounting_accuracy: str = "estimated",
|
|
94
|
+
tokenizer: str = "heuristic_cjk_ascii",
|
|
95
|
+
stable_prefix_hash: str = "",
|
|
96
|
+
) -> ContextPlan:
|
|
97
|
+
plan_id = f"ctxplan_{uuid.uuid4().hex[:16]}"
|
|
98
|
+
decisions: list[ContextDecision] = []
|
|
99
|
+
|
|
100
|
+
# 1. required 先锁定(方案 §8.4)。required 超 hard_limit → 配置错误,仍返回 plan 但标 dropped。 # noqa: E501
|
|
101
|
+
required = self._select_required(candidates, budget.hard_limit_tokens, decisions)
|
|
102
|
+
selected = list(required)
|
|
103
|
+
|
|
104
|
+
# 2. 非 required 按 §8.4 优先级排序后增量加入,遵守 group 原子性 + 分区预算。
|
|
105
|
+
non_required = [
|
|
106
|
+
c
|
|
107
|
+
for c in candidates
|
|
108
|
+
if not c.required and c.item_id not in {i.item_id for i in selected}
|
|
109
|
+
]
|
|
110
|
+
non_required.sort(key=self._sort_key)
|
|
111
|
+
selected = self._add_within_budget(
|
|
112
|
+
selected, non_required, budget, decisions, strict_section_limits=True
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# 3. soft limit → 确定性缩减(零 LLM 成本,方案 §8.5)
|
|
116
|
+
if _tokens(selected) > budget.soft_limit_tokens:
|
|
117
|
+
selected = self._deterministic_reduce(selected, budget.soft_limit_tokens, decisions)
|
|
118
|
+
|
|
119
|
+
# 4. hard limit → 紧急缩减(仍零 LLM;semantic compaction 由调用方在后续触发,方案 §8.4 末)
|
|
120
|
+
if _tokens(selected) > budget.hard_limit_tokens:
|
|
121
|
+
selected = self._emergency_reduce(selected, budget.hard_limit_tokens, decisions)
|
|
122
|
+
|
|
123
|
+
tokens_by_kind = self._tokens_by_kind(selected)
|
|
124
|
+
return ContextPlan(
|
|
125
|
+
plan_id=plan_id,
|
|
126
|
+
policy_version=CONTEXT_POLICY_VERSION,
|
|
127
|
+
tokenizer=tokenizer,
|
|
128
|
+
integration_mode=integration_mode, # type: ignore[arg-type]
|
|
129
|
+
accounting_accuracy=accounting_accuracy, # type: ignore[arg-type]
|
|
130
|
+
budget=budget,
|
|
131
|
+
selected=selected,
|
|
132
|
+
decisions=decisions,
|
|
133
|
+
tokens_by_kind=tokens_by_kind,
|
|
134
|
+
planned_input_tokens=_tokens(selected),
|
|
135
|
+
projected_input_tokens=None,
|
|
136
|
+
runtime_reported_input_tokens=None,
|
|
137
|
+
stable_prefix_hash=stable_prefix_hash,
|
|
138
|
+
projection_id=None,
|
|
139
|
+
contributor_status={},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# ---- 步骤实现 ----
|
|
143
|
+
|
|
144
|
+
def _select_required(
|
|
145
|
+
self,
|
|
146
|
+
candidates: list[ContextItem],
|
|
147
|
+
hard_limit: int,
|
|
148
|
+
decisions: list[ContextDecision],
|
|
149
|
+
) -> list[ContextItem]:
|
|
150
|
+
"""required 先锁定,并按 group 原子性拉入同组非 required 成员(方案 §8.1)。
|
|
151
|
+
|
|
152
|
+
group 原子性是跨全体 candidates 的:只要 group 中有 required,整组进入;非 required
|
|
153
|
+
成员不进非 required 增量阶段,避免孤儿 group。
|
|
154
|
+
"""
|
|
155
|
+
required = [c for c in candidates if c.required]
|
|
156
|
+
required.sort(key=self._sort_key)
|
|
157
|
+
all_groups = _group_groups(candidates)
|
|
158
|
+
selected: list[ContextItem] = []
|
|
159
|
+
seen: set[str] = set()
|
|
160
|
+
# required 本身总是进入(即使超 hard_limit,标 included;调用方据 hard_limit 判配置错误)。
|
|
161
|
+
for item in required:
|
|
162
|
+
selected.append(item)
|
|
163
|
+
seen.add(item.item_id)
|
|
164
|
+
decisions.append(
|
|
165
|
+
ContextDecision(
|
|
166
|
+
item_id=item.item_id,
|
|
167
|
+
action="included",
|
|
168
|
+
reason="required",
|
|
169
|
+
tokens_before=item.estimated_tokens,
|
|
170
|
+
tokens_after=item.estimated_tokens,
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
# 拉入 required 所在 group 的非 required 成员(原子保留,方案 §8.1)。
|
|
174
|
+
for item in required:
|
|
175
|
+
if not item.group_id:
|
|
176
|
+
continue
|
|
177
|
+
for mate in all_groups.get(item.group_id, []):
|
|
178
|
+
if mate.item_id in seen:
|
|
179
|
+
continue
|
|
180
|
+
selected.append(mate)
|
|
181
|
+
seen.add(mate.item_id)
|
|
182
|
+
decisions.append(
|
|
183
|
+
ContextDecision(
|
|
184
|
+
item_id=mate.item_id,
|
|
185
|
+
action="included",
|
|
186
|
+
reason="required_group_atomic",
|
|
187
|
+
tokens_before=mate.estimated_tokens,
|
|
188
|
+
tokens_after=mate.estimated_tokens,
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
return selected
|
|
192
|
+
|
|
193
|
+
def _sort_key(self, item: ContextItem) -> tuple[int, int, str]:
|
|
194
|
+
rank = _PRIORITY_RANK.get(item.kind, 99)
|
|
195
|
+
# 同优先级:required 先、score 高先、seq 小先
|
|
196
|
+
score = item.score if item.score is not None else 0.0
|
|
197
|
+
seq = item.seq_start if item.seq_start is not None else 0
|
|
198
|
+
return (rank, -int(score * 1000), seq, item.item_id)
|
|
199
|
+
|
|
200
|
+
def _add_within_budget(
|
|
201
|
+
self,
|
|
202
|
+
selected: list[ContextItem],
|
|
203
|
+
candidates: list[ContextItem],
|
|
204
|
+
budget: ContextBudget,
|
|
205
|
+
decisions: list[ContextDecision],
|
|
206
|
+
*,
|
|
207
|
+
strict_section_limits: bool,
|
|
208
|
+
) -> list[ContextItem]:
|
|
209
|
+
chosen_ids = {i.item_id for i in selected}
|
|
210
|
+
section_used: dict[str, int] = self._section_used(selected)
|
|
211
|
+
groups = _group_groups(candidates)
|
|
212
|
+
# 按 group 的最小优先级排序,保证整组按优先级进入
|
|
213
|
+
group_order = sorted(
|
|
214
|
+
groups.items(),
|
|
215
|
+
key=lambda kv: min(self._sort_key(m) for m in kv[1]),
|
|
216
|
+
)
|
|
217
|
+
for _gkey, members in group_order:
|
|
218
|
+
group_tokens = _tokens(members)
|
|
219
|
+
section = _section_for(members[0].kind)
|
|
220
|
+
limit = budget.section_limits.get(section)
|
|
221
|
+
# group 原子性:整组进或不进(除非单组就超 hard_limit,则尝试截断 truncatable)
|
|
222
|
+
if _tokens(selected) + group_tokens > budget.hard_limit_tokens:
|
|
223
|
+
# 尝试 truncatable 单项截断
|
|
224
|
+
added = self._try_truncate_into(selected, members, budget, decisions)
|
|
225
|
+
selected.extend(added)
|
|
226
|
+
if not added:
|
|
227
|
+
# 整组因 hard_limit 被跳过 → 记录 dropped 决策(方案 §8.8)
|
|
228
|
+
for m in members:
|
|
229
|
+
if m.item_id not in chosen_ids:
|
|
230
|
+
self._drop(
|
|
231
|
+
decisions,
|
|
232
|
+
m,
|
|
233
|
+
"hard_limit_exceeded",
|
|
234
|
+
)
|
|
235
|
+
continue
|
|
236
|
+
if (
|
|
237
|
+
strict_section_limits
|
|
238
|
+
and limit is not None
|
|
239
|
+
and section_used.get(section, 0) + group_tokens > limit
|
|
240
|
+
):
|
|
241
|
+
# 分区预算超限:整组跳过 → 记录 dropped 决策(方案 §8.8)
|
|
242
|
+
for m in members:
|
|
243
|
+
if m.item_id not in chosen_ids:
|
|
244
|
+
self._drop(
|
|
245
|
+
decisions,
|
|
246
|
+
m,
|
|
247
|
+
f"section_limit:{section}",
|
|
248
|
+
)
|
|
249
|
+
continue
|
|
250
|
+
# 全组成员未选 → 整组进入
|
|
251
|
+
new_members = [m for m in members if m.item_id not in chosen_ids]
|
|
252
|
+
if not new_members:
|
|
253
|
+
continue
|
|
254
|
+
selected.extend(new_members)
|
|
255
|
+
section_used[section] = section_used.get(section, 0) + _tokens(new_members)
|
|
256
|
+
for m in new_members:
|
|
257
|
+
decisions.append(
|
|
258
|
+
ContextDecision(
|
|
259
|
+
item_id=m.item_id,
|
|
260
|
+
action="included",
|
|
261
|
+
reason=f"section:{section}",
|
|
262
|
+
tokens_before=m.estimated_tokens,
|
|
263
|
+
tokens_after=m.estimated_tokens,
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
return selected
|
|
267
|
+
|
|
268
|
+
def _try_truncate_into(
|
|
269
|
+
self,
|
|
270
|
+
selected: list[ContextItem],
|
|
271
|
+
members: list[ContextItem],
|
|
272
|
+
budget: ContextBudget,
|
|
273
|
+
decisions: list[ContextDecision],
|
|
274
|
+
) -> list[ContextItem]:
|
|
275
|
+
"""整组超 hard_limit 时原子抢救:固定成员保留,其余截断或摘要(方案 §8.1/§8.6)。
|
|
276
|
+
|
|
277
|
+
即使 Tool Result 可降载,Tool Call/Result 仍是一个协议组,不能只留下 Result。
|
|
278
|
+
因此先为不可缩减成员预留预算,再处理可缩减成员;任一成员无法进入时整组放弃。
|
|
279
|
+
"""
|
|
280
|
+
from dataclasses import replace
|
|
281
|
+
|
|
282
|
+
def _reducible(item: ContextItem) -> bool:
|
|
283
|
+
return bool(
|
|
284
|
+
item.truncatable
|
|
285
|
+
or (item.kind == "tool_result" and item.droppable and not item.required)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
remaining_total = budget.hard_limit_tokens - _tokens(selected)
|
|
289
|
+
fixed = [item for item in members if not _reducible(item)]
|
|
290
|
+
fixed_tokens = _tokens(fixed)
|
|
291
|
+
reducible = [item for item in members if _reducible(item)]
|
|
292
|
+
if fixed_tokens > remaining_total or (reducible and fixed_tokens >= remaining_total):
|
|
293
|
+
return []
|
|
294
|
+
|
|
295
|
+
added: list[ContextItem] = list(fixed)
|
|
296
|
+
pending_decisions: list[ContextDecision] = [
|
|
297
|
+
ContextDecision(
|
|
298
|
+
item_id=item.item_id,
|
|
299
|
+
action="included",
|
|
300
|
+
reason="group_atomic_fixed",
|
|
301
|
+
tokens_before=item.estimated_tokens,
|
|
302
|
+
tokens_after=item.estimated_tokens,
|
|
303
|
+
)
|
|
304
|
+
for item in fixed
|
|
305
|
+
]
|
|
306
|
+
for index, m in enumerate(reducible):
|
|
307
|
+
remaining = budget.hard_limit_tokens - _tokens(selected) - _tokens(added)
|
|
308
|
+
# 至少给后续每个可缩减成员留 1 token,保证整组原子进入。
|
|
309
|
+
available = remaining - (len(reducible) - index - 1)
|
|
310
|
+
if available <= 0:
|
|
311
|
+
return []
|
|
312
|
+
|
|
313
|
+
# 大 tool_result → artifact summary(方案 §8.6:保留 error tail + 引用)
|
|
314
|
+
if (
|
|
315
|
+
m.kind == "tool_result"
|
|
316
|
+
and m.droppable
|
|
317
|
+
and not m.required
|
|
318
|
+
and m.estimated_tokens > available
|
|
319
|
+
):
|
|
320
|
+
after = max(1, min(available, m.estimated_tokens // 8 + 200))
|
|
321
|
+
added.append(
|
|
322
|
+
replace(
|
|
323
|
+
m,
|
|
324
|
+
estimated_tokens=after,
|
|
325
|
+
metadata={**m.metadata, "replaced_with_artifact_summary": True},
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
pending_decisions.append(
|
|
329
|
+
ContextDecision(
|
|
330
|
+
item_id=m.item_id,
|
|
331
|
+
action="summarized",
|
|
332
|
+
reason="large_tool_result_to_artifact",
|
|
333
|
+
tokens_before=m.estimated_tokens,
|
|
334
|
+
tokens_after=after,
|
|
335
|
+
)
|
|
336
|
+
)
|
|
337
|
+
continue
|
|
338
|
+
if not m.truncatable or m.estimated_tokens == 0:
|
|
339
|
+
# 可缩减集合里的非 truncatable 项只能是尚未超过 available 的 Tool Result。
|
|
340
|
+
if m.estimated_tokens > available:
|
|
341
|
+
return []
|
|
342
|
+
added.append(m)
|
|
343
|
+
pending_decisions.append(
|
|
344
|
+
ContextDecision(
|
|
345
|
+
item_id=m.item_id,
|
|
346
|
+
action="included",
|
|
347
|
+
reason="group_atomic_fit",
|
|
348
|
+
tokens_before=m.estimated_tokens,
|
|
349
|
+
tokens_after=m.estimated_tokens,
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
continue
|
|
353
|
+
# 截断到剩余预算(启发式按 token 比例截字符;实际截断由 assembler 处理)
|
|
354
|
+
ratio = available / max(m.estimated_tokens, 1)
|
|
355
|
+
after = max(0, int(m.estimated_tokens * ratio))
|
|
356
|
+
if after == 0:
|
|
357
|
+
return []
|
|
358
|
+
added.append(
|
|
359
|
+
replace(
|
|
360
|
+
m, estimated_tokens=after, metadata={**m.metadata, "truncated_to_tokens": after}
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
pending_decisions.append(
|
|
364
|
+
ContextDecision(
|
|
365
|
+
item_id=m.item_id,
|
|
366
|
+
action="truncated",
|
|
367
|
+
reason="hard_limit_truncate",
|
|
368
|
+
tokens_before=m.estimated_tokens,
|
|
369
|
+
tokens_after=after,
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
if len(added) != len(members):
|
|
373
|
+
return []
|
|
374
|
+
decisions.extend(pending_decisions)
|
|
375
|
+
return added
|
|
376
|
+
|
|
377
|
+
def _deterministic_reduce(
|
|
378
|
+
self, selected: list[ContextItem], soft_limit: int, decisions: list[ContextDecision]
|
|
379
|
+
) -> list[ContextItem]:
|
|
380
|
+
"""零 LLM 成本的确定性缩减(方案 §8.5 1-6 步)。
|
|
381
|
+
|
|
382
|
+
顺序:删除重复 manifest → 大 tool result 转 artifact reference → 移除被覆盖旧
|
|
383
|
+
tool call/result → 去二进制/重复日志 → 旧冷轮次 microcompact(此处只 drop 冷轮)→
|
|
384
|
+
降低 recall top_k。required 与 current_input 不动。
|
|
385
|
+
"""
|
|
386
|
+
kept = list(selected)
|
|
387
|
+
{i.item_id for i in kept}
|
|
388
|
+
|
|
389
|
+
# 1. 重复 resource_manifest(同 content_hash 去重)
|
|
390
|
+
seen_hashes: set[str] = set()
|
|
391
|
+
new_kept: list[ContextItem] = []
|
|
392
|
+
for item in kept:
|
|
393
|
+
if item.kind == "resource_manifest" and item.content_hash:
|
|
394
|
+
if item.content_hash in seen_hashes:
|
|
395
|
+
self._drop(decisions, item, "dedupe_manifest")
|
|
396
|
+
continue
|
|
397
|
+
seen_hashes.add(item.content_hash)
|
|
398
|
+
new_kept.append(item)
|
|
399
|
+
kept = new_kept
|
|
400
|
+
if _tokens(kept) <= soft_limit:
|
|
401
|
+
return kept
|
|
402
|
+
|
|
403
|
+
# 2. 大 tool_result 转摘要(droppable 且非 required)
|
|
404
|
+
kept = self._reduce_large_tool_results(kept, decisions)
|
|
405
|
+
if _tokens(kept) <= soft_limit:
|
|
406
|
+
return kept
|
|
407
|
+
|
|
408
|
+
# 3. 移除被同参数覆盖的旧 tool call/result(metadata.overwritten_by)
|
|
409
|
+
kept = self._drop_overwritten_tools(kept, decisions)
|
|
410
|
+
if _tokens(kept) <= soft_limit:
|
|
411
|
+
return kept
|
|
412
|
+
|
|
413
|
+
# 5. 旧冷轮次 drop(非 required、非 current_input、seq 最早的 history_round)
|
|
414
|
+
kept = self._drop_cold_rounds(kept, decisions, soft_limit)
|
|
415
|
+
if _tokens(kept) <= soft_limit:
|
|
416
|
+
return kept
|
|
417
|
+
|
|
418
|
+
# 6. 降低 recall memory top_k(按 score 最低先丢)
|
|
419
|
+
kept = self._reduce_recall(kept, decisions, soft_limit)
|
|
420
|
+
return kept
|
|
421
|
+
|
|
422
|
+
def _emergency_reduce(
|
|
423
|
+
self, selected: list[ContextItem], hard_limit: int, decisions: list[ContextDecision]
|
|
424
|
+
) -> list[ContextItem]:
|
|
425
|
+
"""紧急缩减:在确定性缩减基础上按优先级逆序丢非 required 可丢项(方案 §8.4)。
|
|
426
|
+
|
|
427
|
+
group 原子性:只有当一个 group 的**全体**成员都是可丢且非 required 时才整组丢;否则该
|
|
428
|
+
group 保持完整(避免孤儿 group)。required/current_input 永不丢。
|
|
429
|
+
"""
|
|
430
|
+
kept = list(selected)
|
|
431
|
+
# 计算每个 group 是否可整组丢(全体 droppable 且非 required 且非 current_input)。
|
|
432
|
+
all_groups = _group_groups(kept)
|
|
433
|
+
droppable_groups: set[str] = set()
|
|
434
|
+
for gkey, members in all_groups.items():
|
|
435
|
+
if all(m.droppable and not m.required and m.kind != "current_input" for m in members):
|
|
436
|
+
droppable_groups.add(gkey)
|
|
437
|
+
# 候选丢弃单元:可整组丢的 group + 无 group 的可丢单项
|
|
438
|
+
drop_candidates: list[tuple[int, list[ContextItem]]] = []
|
|
439
|
+
for gkey, members in all_groups.items():
|
|
440
|
+
if gkey in droppable_groups:
|
|
441
|
+
drop_candidates.append(
|
|
442
|
+
(min(_PRIORITY_RANK.get(m.kind, 99) for m in members), members)
|
|
443
|
+
)
|
|
444
|
+
else:
|
|
445
|
+
# 无 group 的可丢单项
|
|
446
|
+
for m in members:
|
|
447
|
+
if (
|
|
448
|
+
not m.group_id
|
|
449
|
+
and m.droppable
|
|
450
|
+
and not m.required
|
|
451
|
+
and m.kind != "current_input"
|
|
452
|
+
):
|
|
453
|
+
drop_candidates.append((_PRIORITY_RANK.get(m.kind, 99), [m]))
|
|
454
|
+
# 按优先级逆序丢(rank 大先丢)
|
|
455
|
+
drop_candidates.sort(key=lambda x: (-x[0],))
|
|
456
|
+
for _rank, members in drop_candidates:
|
|
457
|
+
if _tokens(kept) <= hard_limit:
|
|
458
|
+
break
|
|
459
|
+
for m in members:
|
|
460
|
+
if m in kept:
|
|
461
|
+
kept.remove(m)
|
|
462
|
+
self._drop(decisions, m, "emergency_drop")
|
|
463
|
+
return kept
|
|
464
|
+
|
|
465
|
+
# ---- 缩减子步骤 ----
|
|
466
|
+
|
|
467
|
+
def _reduce_large_tool_results(
|
|
468
|
+
self, kept: list[ContextItem], decisions: list[ContextDecision]
|
|
469
|
+
) -> list[ContextItem]:
|
|
470
|
+
new_kept: list[ContextItem] = []
|
|
471
|
+
for item in kept:
|
|
472
|
+
if (
|
|
473
|
+
item.kind == "tool_result"
|
|
474
|
+
and item.droppable
|
|
475
|
+
and not item.required
|
|
476
|
+
and item.estimated_tokens
|
|
477
|
+
> self._policy.sections.get(
|
|
478
|
+
"tool_and_attachment", SectionBudget(10, 16000)
|
|
479
|
+
).max_tokens
|
|
480
|
+
):
|
|
481
|
+
# 转摘要:保留 error tail + artifact reference(这里以估算 1/8 表达,assembler 真正截断) # noqa: E501
|
|
482
|
+
from dataclasses import replace
|
|
483
|
+
|
|
484
|
+
after = max(item.estimated_tokens // 8, 200)
|
|
485
|
+
new_kept.append(
|
|
486
|
+
replace(
|
|
487
|
+
item,
|
|
488
|
+
estimated_tokens=after,
|
|
489
|
+
metadata={**item.metadata, "replaced_with_artifact_summary": True},
|
|
490
|
+
)
|
|
491
|
+
)
|
|
492
|
+
decisions.append(
|
|
493
|
+
ContextDecision(
|
|
494
|
+
item_id=item.item_id,
|
|
495
|
+
action="summarized",
|
|
496
|
+
reason="large_tool_result_to_artifact",
|
|
497
|
+
tokens_before=item.estimated_tokens,
|
|
498
|
+
tokens_after=after,
|
|
499
|
+
)
|
|
500
|
+
)
|
|
501
|
+
else:
|
|
502
|
+
new_kept.append(item)
|
|
503
|
+
return new_kept
|
|
504
|
+
|
|
505
|
+
def _drop_overwritten_tools(
|
|
506
|
+
self, kept: list[ContextItem], decisions: list[ContextDecision]
|
|
507
|
+
) -> list[ContextItem]:
|
|
508
|
+
overwritten = {
|
|
509
|
+
item.metadata.get("overwritten_by")
|
|
510
|
+
for item in kept
|
|
511
|
+
if item.kind == "tool_result" and item.metadata.get("overwritten_by")
|
|
512
|
+
}
|
|
513
|
+
if not overwritten:
|
|
514
|
+
return kept
|
|
515
|
+
all_groups = _group_groups(kept)
|
|
516
|
+
droppable_groups: set[str] = set()
|
|
517
|
+
for gkey, members in all_groups.items():
|
|
518
|
+
if all(m.droppable and not m.required for m in members):
|
|
519
|
+
droppable_groups.add(gkey)
|
|
520
|
+
# 标记要丢弃的 group 与单项
|
|
521
|
+
drop_groups: set[str] = set()
|
|
522
|
+
drop_items: set[str] = set()
|
|
523
|
+
for item in kept:
|
|
524
|
+
if not (
|
|
525
|
+
item.kind == "tool_result"
|
|
526
|
+
and item.content_hash in overwritten
|
|
527
|
+
and not item.required
|
|
528
|
+
):
|
|
529
|
+
continue
|
|
530
|
+
if item.group_id:
|
|
531
|
+
if item.group_id in droppable_groups:
|
|
532
|
+
drop_groups.add(item.group_id)
|
|
533
|
+
else:
|
|
534
|
+
drop_items.add(item.item_id)
|
|
535
|
+
if not drop_groups and not drop_items:
|
|
536
|
+
return kept
|
|
537
|
+
new_kept: list[ContextItem] = []
|
|
538
|
+
for item in kept:
|
|
539
|
+
if item.item_id in drop_items or (item.group_id and item.group_id in drop_groups):
|
|
540
|
+
self._drop(decisions, item, "overwritten_tool_result")
|
|
541
|
+
continue
|
|
542
|
+
new_kept.append(item)
|
|
543
|
+
return new_kept
|
|
544
|
+
|
|
545
|
+
def _drop_cold_rounds(
|
|
546
|
+
self, kept: list[ContextItem], decisions: list[ContextDecision], soft_limit: int
|
|
547
|
+
) -> list[ContextItem]:
|
|
548
|
+
all_groups = _group_groups(kept)
|
|
549
|
+
# 只丢全体可丢且非 required 的 group(避免孤儿)。
|
|
550
|
+
droppable_groups: set[str] = set()
|
|
551
|
+
for gkey, members in all_groups.items():
|
|
552
|
+
if all(m.droppable and not m.required for m in members):
|
|
553
|
+
droppable_groups.add(gkey)
|
|
554
|
+
rounds = [i for i in kept if i.kind == "history_round" and i.droppable and not i.required]
|
|
555
|
+
rounds.sort(key=lambda i: (i.seq_start if i.seq_start is not None else 0,))
|
|
556
|
+
for item in rounds:
|
|
557
|
+
if _tokens(kept) <= soft_limit:
|
|
558
|
+
break
|
|
559
|
+
if item.group_id and item.group_id not in droppable_groups:
|
|
560
|
+
continue # 组内有 required/非可丢成员,保持完整
|
|
561
|
+
if item.group_id:
|
|
562
|
+
group = [i for i in kept if i.group_id == item.group_id]
|
|
563
|
+
for g in group:
|
|
564
|
+
if g in kept:
|
|
565
|
+
kept.remove(g)
|
|
566
|
+
self._drop(decisions, g, "cold_round_drop")
|
|
567
|
+
else:
|
|
568
|
+
if item in kept:
|
|
569
|
+
kept.remove(item)
|
|
570
|
+
self._drop(decisions, item, "cold_round_drop")
|
|
571
|
+
return kept
|
|
572
|
+
|
|
573
|
+
def _reduce_recall(
|
|
574
|
+
self, kept: list[ContextItem], decisions: list[ContextDecision], limit: int
|
|
575
|
+
) -> list[ContextItem]:
|
|
576
|
+
all_groups = _group_groups(kept)
|
|
577
|
+
droppable_groups: set[str] = set()
|
|
578
|
+
for gkey, members in all_groups.items():
|
|
579
|
+
if all(m.droppable and not m.required for m in members):
|
|
580
|
+
droppable_groups.add(gkey)
|
|
581
|
+
recalls = [
|
|
582
|
+
i for i in kept if i.kind == "recalled_memory" and i.droppable and not i.required
|
|
583
|
+
]
|
|
584
|
+
recalls.sort(key=lambda i: i.score if i.score is not None else 0.0)
|
|
585
|
+
for item in recalls:
|
|
586
|
+
if _tokens(kept) <= limit:
|
|
587
|
+
break
|
|
588
|
+
if item.group_id and item.group_id not in droppable_groups:
|
|
589
|
+
continue # 组内有 required/非可丢成员,保持完整
|
|
590
|
+
if item.group_id:
|
|
591
|
+
# 原子丢弃整组(方案 §8.1)
|
|
592
|
+
for g in [i for i in kept if i.group_id == item.group_id]:
|
|
593
|
+
kept.remove(g)
|
|
594
|
+
self._drop(decisions, g, "recall_topk_reduce")
|
|
595
|
+
elif item in kept:
|
|
596
|
+
kept.remove(item)
|
|
597
|
+
self._drop(decisions, item, "recall_topk_reduce")
|
|
598
|
+
return kept
|
|
599
|
+
|
|
600
|
+
# ---- helpers ----
|
|
601
|
+
|
|
602
|
+
@staticmethod
|
|
603
|
+
def _drop(decisions: list[ContextDecision], item: ContextItem, reason: str) -> None:
|
|
604
|
+
decisions.append(
|
|
605
|
+
ContextDecision(
|
|
606
|
+
item_id=item.item_id,
|
|
607
|
+
action="dropped",
|
|
608
|
+
reason=reason,
|
|
609
|
+
tokens_before=item.estimated_tokens,
|
|
610
|
+
tokens_after=0,
|
|
611
|
+
)
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
@staticmethod
|
|
615
|
+
def _section_used(selected: list[ContextItem]) -> dict[str, int]:
|
|
616
|
+
used: dict[str, int] = {}
|
|
617
|
+
for item in selected:
|
|
618
|
+
section = _section_for(item.kind)
|
|
619
|
+
used[section] = used.get(section, 0) + item.estimated_tokens
|
|
620
|
+
return used
|
|
621
|
+
|
|
622
|
+
@staticmethod
|
|
623
|
+
def _tokens_by_kind(selected: list[ContextItem]) -> dict[str, int]:
|
|
624
|
+
by_kind: dict[str, int] = {}
|
|
625
|
+
for item in selected:
|
|
626
|
+
by_kind[item.kind] = by_kind.get(item.kind, 0) + item.estimated_tokens
|
|
627
|
+
return by_kind
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def build_budget(
|
|
631
|
+
*,
|
|
632
|
+
policy: ContextBudgetPolicy,
|
|
633
|
+
context_window_tokens: int,
|
|
634
|
+
reserved_output_tokens: int = 0,
|
|
635
|
+
reserved_reasoning_tokens: int = 0,
|
|
636
|
+
) -> ContextBudget:
|
|
637
|
+
"""从 policy 与模型窗口构造 ``ContextBudget``(方案 §8.2 / §8.3)。"""
|
|
638
|
+
tokens = compute_section_budget_tokens(
|
|
639
|
+
policy,
|
|
640
|
+
context_window_tokens=context_window_tokens,
|
|
641
|
+
reserved_output_tokens=reserved_output_tokens,
|
|
642
|
+
reserved_reasoning_tokens=reserved_reasoning_tokens,
|
|
643
|
+
)
|
|
644
|
+
max_input = tokens["max_input_tokens"]
|
|
645
|
+
# 小窗口(≤8K)动态调整:Prompt 占比提高到 30%,History 降到 25%
|
|
646
|
+
if max_input <= 8192:
|
|
647
|
+
from dataclasses import replace as _replace
|
|
648
|
+
|
|
649
|
+
small_policy = _replace(
|
|
650
|
+
policy,
|
|
651
|
+
sections={
|
|
652
|
+
"prompt": _replace(policy.sections["prompt"], percent=30, max_tokens=24000),
|
|
653
|
+
"recent_history": _replace(
|
|
654
|
+
policy.sections["recent_history"],
|
|
655
|
+
percent=25,
|
|
656
|
+
max_tokens=64000,
|
|
657
|
+
),
|
|
658
|
+
},
|
|
659
|
+
)
|
|
660
|
+
section_limits = {
|
|
661
|
+
name: min(int(max_input * sb.percent / 100.0), sb.max_tokens)
|
|
662
|
+
for name, sb in small_policy.sections.items()
|
|
663
|
+
}
|
|
664
|
+
else:
|
|
665
|
+
section_limits = {
|
|
666
|
+
name: min(int(max_input * sb.percent / 100.0), sb.max_tokens)
|
|
667
|
+
for name, sb in policy.sections.items()
|
|
668
|
+
}
|
|
669
|
+
return ContextBudget(
|
|
670
|
+
context_window_tokens=context_window_tokens,
|
|
671
|
+
reserved_output_tokens=reserved_output_tokens,
|
|
672
|
+
reserved_reasoning_tokens=reserved_reasoning_tokens,
|
|
673
|
+
safety_buffer_tokens=tokens["safety_buffer_tokens"],
|
|
674
|
+
max_input_tokens=tokens["max_input_tokens"],
|
|
675
|
+
soft_limit_tokens=tokens["soft_limit_tokens"],
|
|
676
|
+
hard_limit_tokens=tokens["hard_limit_tokens"],
|
|
677
|
+
section_limits=section_limits,
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
def compute_section_budget_tokens(
|
|
682
|
+
policy: ContextBudgetPolicy,
|
|
683
|
+
*,
|
|
684
|
+
context_window_tokens: int,
|
|
685
|
+
reserved_output_tokens: int,
|
|
686
|
+
reserved_reasoning_tokens: int,
|
|
687
|
+
) -> dict[str, int]:
|
|
688
|
+
from ksadk.context_engine.policies import compute_budget_tokens
|
|
689
|
+
|
|
690
|
+
return compute_budget_tokens(
|
|
691
|
+
policy,
|
|
692
|
+
context_window_tokens=context_window_tokens,
|
|
693
|
+
reserved_output_tokens=reserved_output_tokens,
|
|
694
|
+
reserved_reasoning_tokens=reserved_reasoning_tokens,
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
__all__ = ["ContextPlanner", "build_budget"]
|