agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +22 -2
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
- ksadk/a2a/_space_client_events.py +381 -0
- ksadk/a2a/event_adapter.py +156 -64
- ksadk/a2a/executor.py +179 -63
- ksadk/a2a/space_client.py +6 -333
- ksadk/a2ui/core.py +222 -53
- ksadk/agui/_agent_helpers.py +243 -0
- ksadk/agui/a2ui_projection.py +12 -5
- ksadk/agui/agent.py +443 -259
- ksadk/api/client.py +525 -27
- ksadk/builders/code_builder.py +284 -8
- ksadk/builders/container_builder.py +8 -1
- ksadk/builders/managed_runtime_builder.py +39 -35
- ksadk/cli/__init__.py +17 -0
- ksadk/cli/cmd_dashboard.py +15 -1
- ksadk/cli/cmd_deploy.py +10 -2
- ksadk/cli/cmd_eval.py +66 -9
- ksadk/cli/cmd_evalset.py +284 -0
- ksadk/cli/cmd_files.py +5 -1
- ksadk/cli/cmd_hermes.py +1 -126
- ksadk/cli/cmd_invoke.py +28 -38
- ksadk/cli/cmd_managed_runtime.py +95 -0
- ksadk/cli/cmd_observe.py +85 -0
- ksadk/cli/cmd_openclaw.py +1 -435
- ksadk/cli/cmd_replay.py +28 -4
- ksadk/cli/cmd_studio.py +99 -8
- ksadk/cli/cmd_web.py +10 -2
- ksadk/cli/hermes_env.py +151 -0
- ksadk/cli/invoke_payload.py +49 -0
- ksadk/cli/openclaw_env.py +458 -0
- ksadk/codex/client.py +180 -30
- ksadk/codex/runtime.py +428 -326
- ksadk/configs/env_registry.py +66 -12
- ksadk/configs/env_registry_pcm.py +181 -0
- ksadk/configs/env_var_spec.py +18 -0
- ksadk/configs/global_config.py +5 -0
- ksadk/context_engine/__init__.py +98 -0
- ksadk/context_engine/assembler.py +176 -0
- ksadk/context_engine/baseline.py +419 -0
- ksadk/context_engine/cache_observability.py +228 -0
- ksadk/context_engine/capabilities.py +443 -0
- ksadk/context_engine/contributors.py +344 -0
- ksadk/context_engine/hosted_pipeline.py +417 -0
- ksadk/context_engine/models.py +117 -0
- ksadk/context_engine/planner.py +698 -0
- ksadk/context_engine/policies.py +319 -0
- ksadk/context_engine/projection.py +31 -0
- ksadk/context_engine/shadow_plan.py +266 -0
- ksadk/context_engine/tokenizer.py +164 -0
- ksadk/conversations/context.py +126 -6
- ksadk/conversations/message_projection.py +292 -28
- ksadk/conversations/model_context.py +49 -3
- ksadk/conversations/runtime_compaction.py +207 -5
- ksadk/conversations/runtime_input.py +184 -2
- ksadk/conversations/runtime_invocation.py +81 -3
- ksadk/conversations/runtime_observability.py +141 -0
- ksadk/conversations/runtime_payloads.py +42 -0
- ksadk/conversations/runtime_preparation.py +272 -2
- ksadk/conversations/runtime_resume.py +20 -1
- ksadk/conversations/runtime_stream_events.py +94 -9
- ksadk/conversations/semantic_summary.py +311 -1
- ksadk/conversations/session_lock.py +51 -0
- ksadk/deployment/env_forward.py +72 -0
- ksadk/deployment/managed_runtime.py +5 -0
- ksadk/deployment/providers/serverless.py +143 -69
- ksadk/evaluation/__init__.py +32 -0
- ksadk/evaluation/a2a_adapter.py +9 -1
- ksadk/evaluation/adapters.py +18 -5
- ksadk/evaluation/agent_eval_client.py +395 -0
- ksadk/evaluation/cloud_binding.py +80 -0
- ksadk/evaluation/cloud_converter.py +194 -0
- ksadk/evaluation/cloud_service.py +199 -0
- ksadk/evaluation/contracts.py +73 -4
- ksadk/evaluation/evaluators.py +183 -17
- ksadk/evaluation/evidence.py +258 -0
- ksadk/evaluation/executor.py +31 -6
- ksadk/evaluation/local_adapter.py +550 -0
- ksadk/evaluation/service_env.py +27 -0
- ksadk/evaluation/studio_build_adapter.py +253 -0
- ksadk/evaluation/target.py +15 -6
- ksadk/events/__init__.py +13 -13
- ksadk/events/_v1_compat/__init__.py +1 -0
- ksadk/events/_v1_compat/models.py +299 -0
- ksadk/events/_v1_compat/parser.py +247 -0
- ksadk/events/_v1_compat/projection.py +753 -0
- ksadk/events/adapters/__init__.py +34 -0
- ksadk/events/adapters/_a2a_snapshot.py +622 -0
- ksadk/events/adapters/_a2a_support.py +275 -0
- ksadk/events/adapters/_codex_interactions.py +375 -0
- ksadk/events/adapters/_codex_items.py +801 -0
- ksadk/events/adapters/_codex_validators.py +311 -0
- ksadk/events/adapters/_langgraph_support.py +785 -0
- ksadk/events/adapters/a2a.py +841 -0
- ksadk/events/adapters/adk.py +503 -0
- ksadk/events/adapters/codex.py +717 -0
- ksadk/events/adapters/langgraph.py +745 -0
- ksadk/events/canonical.py +440 -0
- ksadk/events/canonical_replay.py +497 -0
- ksadk/events/canonical_store.py +468 -0
- ksadk/events/cold_recovery.py +257 -0
- ksadk/events/content.py +89 -0
- ksadk/events/identity.py +86 -0
- ksadk/events/pipeline.py +455 -0
- ksadk/events/projections.py +66 -0
- ksadk/events/reducer.py +661 -0
- ksadk/events/replay.py +3 -36
- ksadk/events/runtime_event.py +20 -265
- ksadk/events/session_event.py +324 -0
- ksadk/events/store.py +3 -316
- ksadk/events/v1_compat.py +41 -0
- ksadk/harness/runtime.py +160 -57
- ksadk/identity/resolver.py +4 -3
- ksadk/interaction/__init__.py +28 -0
- ksadk/interaction/contracts.py +136 -0
- ksadk/interaction/ledger.py +189 -0
- ksadk/interaction/provider.py +122 -0
- ksadk/interaction/providers/__init__.py +52 -0
- ksadk/interaction/providers/adk.py +40 -0
- ksadk/interaction/providers/codex.py +94 -0
- ksadk/interaction/providers/langgraph.py +58 -0
- ksadk/kernel/__init__.py +117 -0
- ksadk/kernel/authorization.py +258 -0
- ksadk/kernel/bootstrap.py +1074 -0
- ksadk/kernel/contract_fingerprints.py +61 -0
- ksadk/kernel/contracts.py +362 -0
- ksadk/kernel/control.py +219 -0
- ksadk/kernel/errors.py +79 -0
- ksadk/kernel/ingress.py +1102 -0
- ksadk/kernel/mapping.py +111 -0
- ksadk/kernel/memory_store.py +1133 -0
- ksadk/kernel/postgres_store.py +1756 -0
- ksadk/kernel/recovery.py +452 -0
- ksadk/kernel/runtime_identity.py +103 -0
- ksadk/kernel/sql/001_agent_kernel.sql +120 -0
- ksadk/kernel/sqlite_store.py +1410 -0
- ksadk/kernel/state.py +145 -0
- ksadk/kernel/store.py +232 -0
- ksadk/kernel/worker.py +953 -0
- ksadk/knowledge_base/client.py +7 -0
- ksadk/knowledge_base/service.py +32 -5
- ksadk/memory/__init__.py +61 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
- ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
- ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
- ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
- ksadk/memory/coordinator.py +461 -0
- ksadk/memory/events.py +202 -0
- ksadk/memory/extraction.py +228 -0
- ksadk/memory/ltm_backend_factory.py +16 -3
- ksadk/memory/models.py +316 -0
- ksadk/memory/policy.py +215 -0
- ksadk/memory/provider.py +47 -0
- ksadk/memory/provider_adapter.py +159 -0
- ksadk/memory/provider_resolver.py +71 -0
- ksadk/memory/providers/__init__.py +5 -0
- ksadk/memory/providers/local_sqlite.py +490 -0
- ksadk/memory/resolved_policy.py +145 -0
- ksadk/memory/service.py +188 -6
- ksadk/model_proxy/bootstrap.py +16 -9
- ksadk/model_proxy/detect.py +202 -9
- ksadk/model_proxy/namespace.py +39 -15
- ksadk/model_proxy/server.py +24 -5
- ksadk/model_proxy/transform.py +60 -2
- ksadk/observability/__init__.py +25 -0
- ksadk/observability/session_log.py +420 -0
- ksadk/observability/trajectory.py +105 -0
- ksadk/prompts/__init__.py +71 -0
- ksadk/prompts/compiler.py +215 -0
- ksadk/prompts/models.py +82 -0
- ksadk/prompts/projection.py +89 -0
- ksadk/prompts/resolved.py +137 -0
- ksadk/prompts/sources.py +233 -0
- ksadk/runners/_langgraph_runner_streams.py +822 -0
- ksadk/runners/adk_runner.py +5 -5
- ksadk/runners/base_runner.py +16 -0
- ksadk/runners/factory.py +1 -2
- ksadk/runners/langgraph_runner.py +198 -437
- ksadk/runtime/_runner_adapter/__init__.py +1 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
- ksadk/runtime/adapter.py +118 -2
- ksadk/runtime/conversation_execution.py +316 -182
- ksadk/runtime/executor.py +144 -3
- ksadk/runtime/factory.py +193 -7
- ksadk/runtime/hosted_finalizer.py +268 -0
- ksadk/runtime/launch.py +14 -1
- ksadk/runtime/preprocessing.py +25 -3
- ksadk/runtime/runner_adapter.py +288 -508
- ksadk/runtime/usage.py +33 -0
- ksadk/sandbox/backends/e2b.py +35 -2
- ksadk/server/composition.py +8 -0
- ksadk/server/factory.py +69 -6
- ksadk/server/routes/kernel_ingress.py +258 -0
- ksadk/server/routes/openai_compat.py +88 -8
- ksadk/server/routes/projection.py +35 -20
- ksadk/server/routes/run.py +94 -0
- ksadk/server/routes/sessions.py +2 -0
- ksadk/server/routes/streaming.py +5 -2
- ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
- ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
- ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
- ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
- ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
- ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
- ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
- ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
- ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
- ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
- ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
- ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
- ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
- ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
- ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
- ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
- ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
- ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
- ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
- ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
- ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
- ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
- ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
- ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
- ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
- ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
- ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
- ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
- ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
- ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
- ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
- ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
- ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
- ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
- ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
- ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
- ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
- ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
- ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
- ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
- ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
- ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
- ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
- ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
- ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
- ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
- ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
- ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
- ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
- ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
- ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
- ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
- ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
- ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
- ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
- ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
- ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
- ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
- ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
- ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
- ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
- ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
- ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
- ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
- ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
- ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
- ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
- ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
- ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
- ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
- ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
- ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
- ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
- ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +26 -9
- ksadk/sessions/_local_service_sync.py +688 -0
- ksadk/sessions/_local_tables.py +21 -0
- ksadk/sessions/_postgres_schema.py +305 -0
- ksadk/sessions/_postgres_tables.py +15 -0
- ksadk/sessions/base.py +80 -1
- ksadk/sessions/in_memory.py +53 -2
- ksadk/sessions/local_service.py +63 -604
- ksadk/sessions/postgres_service.py +58 -148
- ksadk/sessions/resilient.py +35 -1
- ksadk/studio/api.py +577 -25
- ksadk/studio/api_contracts.py +114 -5
- ksadk/studio/api_memory_routes.py +82 -0
- ksadk/studio/authoring.py +252 -27
- ksadk/studio/authoring_coordinator.py +799 -67
- ksadk/studio/builder.py +88 -11
- ksadk/studio/capabilities.py +17 -0
- ksadk/studio/cloud.py +1916 -95
- ksadk/studio/codex_agent_service.py +125 -14
- ksadk/studio/codex_authoring.py +469 -0
- ksadk/studio/codex_builder.py +138 -25
- ksadk/studio/codex_manifest.py +51 -22
- ksadk/studio/codex_run.py +46 -3
- ksadk/studio/compiler.py +1 -0
- ksadk/studio/contracts.py +114 -3
- ksadk/studio/event_store.py +25 -95
- ksadk/studio/framework_run.py +231 -3
- ksadk/studio/hosted_kernel.py +317 -0
- ksadk/studio/manifest_resolver.py +131 -0
- ksadk/studio/model_client.py +95 -9
- ksadk/studio/model_profile_service.py +1 -1
- ksadk/studio/operations.py +37 -5
- ksadk/studio/otel_trace.py +348 -42
- ksadk/studio/pcm_memory.py +85 -0
- ksadk/studio/repository.py +23 -2
- ksadk/studio/resource_catalog.py +43 -0
- ksadk/studio/run_service.py +563 -137
- ksadk/studio/runtime_source.py +42 -16
- ksadk/studio/service.py +1180 -72
- ksadk/studio/shared_web.py +32 -22
- ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
- ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
- ksadk/studio/static/index.html +2 -2
- ksadk/studio/templates.py +100 -8
- ksadk/studio/validator.py +19 -0
- ksadk/studio/workspace.py +30 -32
- ksadk/tracing/setup.py +304 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
- ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
- ksadk/events/parser.py +0 -191
- ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
- ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
- ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
- ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
- ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
- ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
- ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
- ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
- ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
- ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
- ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
- ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
- ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
- ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
- ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
- ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
- ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
- ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
- ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
- ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
- ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
- ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
- ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
- ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
- ksadk/server/static/assets/index-R52pJJvg.js +0 -177
- ksadk/server/static/assets/index-jJr3_955.css +0 -2
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
- ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
- ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
- ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
- ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
- ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
- ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
- ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
- ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
- ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
- ksadk/studio/evaluation.py +0 -192
- ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
- ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
ksadk/knowledge_base/client.py
CHANGED
|
@@ -82,6 +82,10 @@ class KnowledgeBaseClient(BaseModel):
|
|
|
82
82
|
score_threshold: float = 0.0
|
|
83
83
|
score_threshold_enabled: bool = False
|
|
84
84
|
reranking_enable: bool = False
|
|
85
|
+
# 最近一次检索失败原因(成功调用前置空,失败时填充)。供
|
|
86
|
+
# KnowledgeBaseService.build_context 区分"后端吞错返空"与"真无结果",
|
|
87
|
+
# 避免错误伪装成"未找到"注入模型上下文。
|
|
88
|
+
last_error: str = ""
|
|
85
89
|
|
|
86
90
|
_aicp_client: Any = None
|
|
87
91
|
|
|
@@ -188,6 +192,7 @@ class KnowledgeBaseClient(BaseModel):
|
|
|
188
192
|
try:
|
|
189
193
|
data = json.loads(response) if isinstance(response, str) else response
|
|
190
194
|
except (json.JSONDecodeError, TypeError):
|
|
195
|
+
self.last_error = f"Failed to parse response: {str(response)[:200]}"
|
|
191
196
|
logger.error(f"Failed to parse response: {str(response)[:200]}")
|
|
192
197
|
return []
|
|
193
198
|
|
|
@@ -230,6 +235,7 @@ class KnowledgeBaseClient(BaseModel):
|
|
|
230
235
|
f"Searching knowledge base: dataset_id={self.dataset_id}, " f"query='{query[:50]}'"
|
|
231
236
|
)
|
|
232
237
|
|
|
238
|
+
self.last_error = ""
|
|
233
239
|
try:
|
|
234
240
|
response = client.call("RetrieveKnowledge", params, options={"IsPostJson": True})
|
|
235
241
|
results = self._parse_response(response)
|
|
@@ -238,6 +244,7 @@ class KnowledgeBaseClient(BaseModel):
|
|
|
238
244
|
)
|
|
239
245
|
return results
|
|
240
246
|
except Exception as e:
|
|
247
|
+
self.last_error = str(e)
|
|
241
248
|
logger.error(f"Knowledge base search failed: {e}")
|
|
242
249
|
raise
|
|
243
250
|
|
ksadk/knowledge_base/service.py
CHANGED
|
@@ -44,6 +44,15 @@ class KnowledgeBaseService:
|
|
|
44
44
|
self._client = KnowledgeBaseClient.from_env()
|
|
45
45
|
return self._client
|
|
46
46
|
|
|
47
|
+
@property
|
|
48
|
+
def last_error(self) -> str:
|
|
49
|
+
"""最近一次检索失败原因(成功前置空,失败填充;含响应解析失败)。
|
|
50
|
+
|
|
51
|
+
供 ``build_context`` 区分"后端吞错/解析失败返空"与"真无结果"。
|
|
52
|
+
客户端尚未懒加载时视为无错误。
|
|
53
|
+
"""
|
|
54
|
+
return str(getattr(self._client, "last_error", "") or "")
|
|
55
|
+
|
|
47
56
|
def search(self, query: str, top_k: Optional[int] = None) -> list[KnowledgeBaseResult]:
|
|
48
57
|
return self._get_client().search(query, top_k)
|
|
49
58
|
|
|
@@ -54,13 +63,31 @@ class KnowledgeBaseService:
|
|
|
54
63
|
logger.error("search_knowledge failed: %s", exc)
|
|
55
64
|
return f"知识库检索失败: {exc}"
|
|
56
65
|
|
|
57
|
-
def build_context(
|
|
66
|
+
def build_context(
|
|
67
|
+
self,
|
|
68
|
+
query: str,
|
|
69
|
+
top_k: Optional[int] = None,
|
|
70
|
+
) -> dict[str, str] | None:
|
|
71
|
+
"""构造环境知识库上下文。失败时返回 ``formatted_text=""`` + 独立 ``error`` 字段,
|
|
72
|
+
不把错误字符串塞进 ``formatted_text``(避免错误伪装成知识库正文注入模型)。
|
|
73
|
+
|
|
74
|
+
- 检索抛错(网络/鉴权失败)→ except 捕获,返 ``error`` 字段。
|
|
75
|
+
- 响应解析失败返空列表(``_parse_response``)→ client ``last_error`` 非空,
|
|
76
|
+
返回 ``error`` 字段。
|
|
77
|
+
- 真无结果(检索正常返空)→ ``formatted_text`` 为"未找到…"(语义真实,可注入)。
|
|
78
|
+
"""
|
|
58
79
|
normalized = str(query or "").strip()
|
|
59
|
-
if not normalized:
|
|
60
|
-
return None
|
|
61
|
-
if not self.is_configured():
|
|
80
|
+
if not normalized or not self.is_configured():
|
|
62
81
|
return None
|
|
82
|
+
try:
|
|
83
|
+
results = self.search(normalized, top_k)
|
|
84
|
+
except Exception as exc:
|
|
85
|
+
logger.error("search_knowledge failed: %s", exc)
|
|
86
|
+
return {"query": normalized, "formatted_text": "", "error": str(exc)}
|
|
87
|
+
client_error = self.last_error
|
|
88
|
+
if not results and client_error:
|
|
89
|
+
return {"query": normalized, "formatted_text": "", "error": client_error}
|
|
63
90
|
return {
|
|
64
91
|
"query": normalized,
|
|
65
|
-
"formatted_text":
|
|
92
|
+
"formatted_text": format_knowledge_results(results),
|
|
66
93
|
}
|
ksadk/memory/__init__.py
CHANGED
|
@@ -54,4 +54,65 @@ def __getattr__(name):
|
|
|
54
54
|
from ksadk.memory.service import LongTermMemoryService
|
|
55
55
|
|
|
56
56
|
return LongTermMemoryService
|
|
57
|
+
# Memory v2(方案 §10):lazy import,避免在仅用旧 API 时强制加载 SQLite/tiktoken 依赖。
|
|
58
|
+
_v2_names = {
|
|
59
|
+
"MemoryRecord",
|
|
60
|
+
"MemoryCandidate",
|
|
61
|
+
"MemorySearchRequest",
|
|
62
|
+
"MemorySearchResult",
|
|
63
|
+
"MemoryCapabilities",
|
|
64
|
+
"CoreMemoryBlock",
|
|
65
|
+
"MemoryDeleteRequest",
|
|
66
|
+
"MemoryDeleteResult",
|
|
67
|
+
"MemoryProvider",
|
|
68
|
+
"MemoryPolicy",
|
|
69
|
+
"MemoryCoordinator",
|
|
70
|
+
"MemoryExtractor",
|
|
71
|
+
"SqliteMemoryProvider",
|
|
72
|
+
"build_search_request",
|
|
73
|
+
"recall_to_context_item",
|
|
74
|
+
"propose_memory_candidates",
|
|
75
|
+
}
|
|
76
|
+
if name in _v2_names:
|
|
77
|
+
if name in {
|
|
78
|
+
"MemoryRecord",
|
|
79
|
+
"MemoryCandidate",
|
|
80
|
+
"MemorySearchRequest",
|
|
81
|
+
"MemorySearchResult",
|
|
82
|
+
"MemoryCapabilities",
|
|
83
|
+
"CoreMemoryBlock",
|
|
84
|
+
"MemoryDeleteRequest",
|
|
85
|
+
"MemoryDeleteResult",
|
|
86
|
+
}:
|
|
87
|
+
from ksadk.memory import models as _models
|
|
88
|
+
|
|
89
|
+
return getattr(_models, name)
|
|
90
|
+
if name == "MemoryProvider":
|
|
91
|
+
from ksadk.memory.provider import MemoryProvider
|
|
92
|
+
|
|
93
|
+
return MemoryProvider
|
|
94
|
+
if name == "MemoryPolicy":
|
|
95
|
+
from ksadk.memory.policy import MemoryPolicy
|
|
96
|
+
|
|
97
|
+
return MemoryPolicy
|
|
98
|
+
if name == "MemoryCoordinator":
|
|
99
|
+
from ksadk.memory.coordinator import MemoryCoordinator
|
|
100
|
+
|
|
101
|
+
return MemoryCoordinator
|
|
102
|
+
if name in {"build_search_request", "recall_to_context_item"}:
|
|
103
|
+
from ksadk.memory import coordinator as _coord
|
|
104
|
+
|
|
105
|
+
return getattr(_coord, name)
|
|
106
|
+
if name == "MemoryExtractor":
|
|
107
|
+
from ksadk.memory.extraction import MemoryExtractor
|
|
108
|
+
|
|
109
|
+
return MemoryExtractor
|
|
110
|
+
if name == "propose_memory_candidates":
|
|
111
|
+
from ksadk.memory.extraction import propose_memory_candidates
|
|
112
|
+
|
|
113
|
+
return propose_memory_candidates
|
|
114
|
+
if name == "SqliteMemoryProvider":
|
|
115
|
+
from ksadk.memory.providers.local_sqlite import SqliteMemoryProvider
|
|
116
|
+
|
|
117
|
+
return SqliteMemoryProvider
|
|
57
118
|
raise AttributeError(f"module 'ksadk.memory' has no attribute {name!r}")
|
|
@@ -3,22 +3,48 @@
|
|
|
3
3
|
所有长期记忆后端必须继承此类并实现 save_memory / search_memory 方法。
|
|
4
4
|
|
|
5
5
|
参考 VeADK: veadk/memory/long_term_memory_backends/base_backend.py
|
|
6
|
+
|
|
7
|
+
扩展协议(技术改造方案 §7.3):
|
|
8
|
+
- search_records: 结构化检索,返回带 memory_id 的 LongTermMemoryRecord
|
|
9
|
+
- update_memory / delete_memory: 按 ID 原地更新/软删除
|
|
10
|
+
- capabilities: 声明 backend 支持的能力集合
|
|
11
|
+
基类对扩展方法提供默认实现:抛出 UnsupportedMemoryOperation 并在
|
|
12
|
+
capabilities 中不声明对应能力,旧 backend 无需改动即保持兼容。
|
|
6
13
|
"""
|
|
7
14
|
|
|
8
15
|
from abc import ABC, abstractmethod
|
|
9
|
-
from typing import List
|
|
16
|
+
from typing import List, Set
|
|
10
17
|
|
|
11
18
|
from pydantic import BaseModel
|
|
12
19
|
|
|
20
|
+
from ksadk.memory.models import (
|
|
21
|
+
LongTermMemoryRecord,
|
|
22
|
+
MemoryExtractionStatus,
|
|
23
|
+
MemoryMutationResult,
|
|
24
|
+
UnsupportedMemoryOperation,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# 能力常量(§7.3)
|
|
28
|
+
CAP_SEARCH = "search"
|
|
29
|
+
CAP_ADD = "add"
|
|
30
|
+
CAP_FLUSH = "flush"
|
|
31
|
+
CAP_STRUCTURED_SEARCH = "structured_search"
|
|
32
|
+
CAP_UPDATE = "update"
|
|
33
|
+
CAP_DELETE = "delete"
|
|
34
|
+
CAP_SESSION_STATUS = "session_status"
|
|
35
|
+
|
|
13
36
|
|
|
14
37
|
class BaseLongTermMemoryBackend(ABC, BaseModel):
|
|
15
38
|
"""长期记忆存储后端抽象基类
|
|
16
39
|
|
|
17
40
|
Attributes:
|
|
18
41
|
index: 索引/集合名称,用于隔离不同应用的记忆数据
|
|
42
|
+
last_error: 最近一次 search/save 失败的原因。成功调用前置空,失败时填充。
|
|
43
|
+
上层(LongTermMemoryService.build_context)据此区分"后端吞错返空"与"真无记忆"。
|
|
19
44
|
"""
|
|
20
45
|
|
|
21
46
|
index: str = ""
|
|
47
|
+
last_error: str = ""
|
|
22
48
|
|
|
23
49
|
@abstractmethod
|
|
24
50
|
def save_memory(self, user_id: str, event_strings: List[str], **kwargs) -> bool:
|
|
@@ -46,3 +72,72 @@ class BaseLongTermMemoryBackend(ABC, BaseModel):
|
|
|
46
72
|
匹配的记忆字符串列表
|
|
47
73
|
"""
|
|
48
74
|
pass
|
|
75
|
+
|
|
76
|
+
# ---- 扩展协议(可选能力,默认 unsupported) ----
|
|
77
|
+
|
|
78
|
+
def search_records(
|
|
79
|
+
self,
|
|
80
|
+
user_id: str,
|
|
81
|
+
query: str,
|
|
82
|
+
top_k: int = 5,
|
|
83
|
+
**kwargs,
|
|
84
|
+
) -> List[LongTermMemoryRecord]:
|
|
85
|
+
"""结构化检索:返回带服务端 memory_id 的记录列表。
|
|
86
|
+
|
|
87
|
+
不支持的 backend 抛出 UnsupportedMemoryOperation,
|
|
88
|
+
不得降级为返回正文 hash 伪造的 ID。
|
|
89
|
+
"""
|
|
90
|
+
raise UnsupportedMemoryOperation(
|
|
91
|
+
f"{type(self).__name__} does not support structured search"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def update_memory(
|
|
95
|
+
self,
|
|
96
|
+
*,
|
|
97
|
+
user_id: str,
|
|
98
|
+
memory_id: str,
|
|
99
|
+
content: str,
|
|
100
|
+
**kwargs,
|
|
101
|
+
) -> MemoryMutationResult:
|
|
102
|
+
"""按 memory_id 原地更新记忆正文。
|
|
103
|
+
|
|
104
|
+
不支持的 backend 抛出 UnsupportedMemoryOperation,
|
|
105
|
+
不得静默追加一条新记忆来模拟 update。
|
|
106
|
+
"""
|
|
107
|
+
raise UnsupportedMemoryOperation(f"{type(self).__name__} does not support update")
|
|
108
|
+
|
|
109
|
+
def delete_memory(
|
|
110
|
+
self,
|
|
111
|
+
*,
|
|
112
|
+
user_id: str,
|
|
113
|
+
memory_id: str,
|
|
114
|
+
**kwargs,
|
|
115
|
+
) -> MemoryMutationResult:
|
|
116
|
+
"""按 memory_id 软删除指定记忆。
|
|
117
|
+
|
|
118
|
+
不支持的 backend 抛出 UnsupportedMemoryOperation。
|
|
119
|
+
"""
|
|
120
|
+
raise UnsupportedMemoryOperation(f"{type(self).__name__} does not support delete")
|
|
121
|
+
|
|
122
|
+
def get_extraction_status(
|
|
123
|
+
self,
|
|
124
|
+
*,
|
|
125
|
+
user_id: str,
|
|
126
|
+
session_id: str,
|
|
127
|
+
) -> MemoryExtractionStatus:
|
|
128
|
+
"""查询 Session 后台提取状态(写后确认,§7.7)。
|
|
129
|
+
|
|
130
|
+
不支持的 backend 抛出 UnsupportedMemoryOperation。
|
|
131
|
+
"""
|
|
132
|
+
raise UnsupportedMemoryOperation(f"{type(self).__name__} does not support session status")
|
|
133
|
+
|
|
134
|
+
def capabilities(self) -> Set[str]:
|
|
135
|
+
"""声明本 backend 支持的能力集合。
|
|
136
|
+
|
|
137
|
+
基类默认只声明基础读写能力;子类按真实实现覆写,
|
|
138
|
+
声明必须与实际可用方法一致(§11.1)。
|
|
139
|
+
"""
|
|
140
|
+
return {CAP_SEARCH, CAP_ADD}
|
|
141
|
+
|
|
142
|
+
def has_capability(self, capability: str) -> bool:
|
|
143
|
+
return capability in self.capabilities()
|
|
@@ -52,10 +52,10 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
|
|
|
52
52
|
def model_post_init(self, __context) -> None:
|
|
53
53
|
if not self.base_url:
|
|
54
54
|
logger.warning(
|
|
55
|
-
"HttpLTMBackend: base_url is empty.
|
|
55
|
+
"HttpLTMBackend: base_url is empty. Set KSADK_LTM_HTTP_URL environment variable."
|
|
56
56
|
)
|
|
57
57
|
logger.info(
|
|
58
|
-
f"HttpLTMBackend initialized: base_url={self.base_url[:50]}...
|
|
58
|
+
f"HttpLTMBackend initialized: base_url={self.base_url[:50]}... index={self.index}"
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
@property
|
|
@@ -101,13 +101,13 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
|
|
|
101
101
|
response.raise_for_status()
|
|
102
102
|
|
|
103
103
|
logger.info(
|
|
104
|
-
f"Saved {len(event_strings)} events to remote memory service
|
|
104
|
+
f"Saved {len(event_strings)} events to remote memory service for user={user_id}"
|
|
105
105
|
)
|
|
106
106
|
return True
|
|
107
107
|
|
|
108
108
|
except httpx.HTTPStatusError as e:
|
|
109
109
|
logger.error(
|
|
110
|
-
f"HTTP error saving memory: {e.response.status_code}
|
|
110
|
+
f"HTTP error saving memory: {e.response.status_code} {e.response.text[:200]}"
|
|
111
111
|
)
|
|
112
112
|
return False
|
|
113
113
|
except Exception as e:
|
|
@@ -133,6 +133,7 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
|
|
|
133
133
|
"""
|
|
134
134
|
if not self.base_url:
|
|
135
135
|
logger.warning("HttpLTMBackend: base_url not configured, return empty results.")
|
|
136
|
+
self.last_error = "base_url not configured"
|
|
136
137
|
return []
|
|
137
138
|
|
|
138
139
|
try:
|
|
@@ -161,7 +162,7 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
|
|
|
161
162
|
|
|
162
163
|
except httpx.HTTPStatusError as e:
|
|
163
164
|
logger.error(
|
|
164
|
-
f"HTTP error searching memory: {e.response.status_code}
|
|
165
|
+
f"HTTP error searching memory: {e.response.status_code} {e.response.text[:200]}"
|
|
165
166
|
)
|
|
166
167
|
return []
|
|
167
168
|
except Exception as e:
|
|
@@ -173,3 +174,11 @@ class HttpLTMBackend(BaseLongTermMemoryBackend):
|
|
|
173
174
|
if self._client:
|
|
174
175
|
self._client.close()
|
|
175
176
|
self._client = None
|
|
177
|
+
|
|
178
|
+
# ------------------------------------------------------------------
|
|
179
|
+
# 扩展协议(方案 §7.3):HTTP backend 为框架预留,结构化能力未定。
|
|
180
|
+
# 远程 API 对接细节待提供(见 save_memory TODO),因此不声明
|
|
181
|
+
# structured/update/delete/session_status 能力;search_records 也不降级
|
|
182
|
+
# 伪造 ID,保持 base 的 UnsupportedMemoryOperation 默认行为。
|
|
183
|
+
# 远端 schema 确认后,在此接入对应端点并覆写 capabilities()。
|
|
184
|
+
# ------------------------------------------------------------------
|
|
@@ -2,15 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
使用简单的内存字典存储和文本匹配检索。
|
|
4
4
|
数据在进程退出后丢失,仅适用于开发和测试场景。
|
|
5
|
+
|
|
6
|
+
扩展协议(技术改造方案 §7.3):内存实现结构化检索与 update/delete,
|
|
7
|
+
用于本地开发和 fake 场景下的契约验证;不声明 flush/session_status 能力
|
|
8
|
+
(没有后台提取过程,写入即"可见")。
|
|
9
|
+
|
|
10
|
+
兼容设计:_storage 保存原始事件字符串不改写,search_memory 返回原始
|
|
11
|
+
字符串列表(保留旧契约,§7.5);结构化能力通过平行 ID 索引提供。
|
|
5
12
|
"""
|
|
6
13
|
|
|
14
|
+
import json
|
|
7
15
|
import logging
|
|
16
|
+
import uuid
|
|
8
17
|
from collections import defaultdict
|
|
9
|
-
from typing import List
|
|
18
|
+
from typing import List, Set
|
|
10
19
|
|
|
11
20
|
from pydantic import PrivateAttr
|
|
12
21
|
|
|
13
|
-
from ksadk.memory.adk.backends.base_ltm_backend import
|
|
22
|
+
from ksadk.memory.adk.backends.base_ltm_backend import (
|
|
23
|
+
CAP_ADD,
|
|
24
|
+
CAP_DELETE,
|
|
25
|
+
CAP_SEARCH,
|
|
26
|
+
CAP_STRUCTURED_SEARCH,
|
|
27
|
+
CAP_UPDATE,
|
|
28
|
+
BaseLongTermMemoryBackend,
|
|
29
|
+
)
|
|
30
|
+
from ksadk.memory.models import (
|
|
31
|
+
LongTermMemoryRecord,
|
|
32
|
+
MemoryMutationResult,
|
|
33
|
+
)
|
|
14
34
|
|
|
15
35
|
logger = logging.getLogger(__name__)
|
|
16
36
|
|
|
@@ -30,6 +50,12 @@ class InMemoryLTMBackend(BaseLongTermMemoryBackend):
|
|
|
30
50
|
"""
|
|
31
51
|
|
|
32
52
|
_storage: defaultdict[str, list[str]] = PrivateAttr(default_factory=lambda: defaultdict(list))
|
|
53
|
+
# memory_id -> 原始事件字符串(不改写 _storage,保留旧 search_memory 契约)。
|
|
54
|
+
_entry_ids: dict[str, str] = PrivateAttr(default_factory=dict)
|
|
55
|
+
# (user_id) -> {memory_id -> 原始事件字符串},用于快速定位与更新/删除。
|
|
56
|
+
_user_entry_index: defaultdict[str, dict[str, str]] = PrivateAttr(
|
|
57
|
+
default_factory=lambda: defaultdict(dict)
|
|
58
|
+
)
|
|
33
59
|
|
|
34
60
|
def model_post_init(self, __context) -> None:
|
|
35
61
|
# {user_id: [event_string, ...]}
|
|
@@ -40,7 +66,11 @@ class InMemoryLTMBackend(BaseLongTermMemoryBackend):
|
|
|
40
66
|
if not event_strings:
|
|
41
67
|
return True
|
|
42
68
|
|
|
43
|
-
|
|
69
|
+
for entry in event_strings:
|
|
70
|
+
memory_id = f"mem-{uuid.uuid4().hex[:12]}"
|
|
71
|
+
self._entry_ids[memory_id] = entry
|
|
72
|
+
self._user_entry_index[user_id][memory_id] = entry
|
|
73
|
+
self._storage[user_id].append(entry)
|
|
44
74
|
logger.debug(
|
|
45
75
|
f"Saved {len(event_strings)} events for user={user_id}, "
|
|
46
76
|
f"total={len(self._storage[user_id])}"
|
|
@@ -88,3 +118,106 @@ class InMemoryLTMBackend(BaseLongTermMemoryBackend):
|
|
|
88
118
|
f"found {len(results)} results from {len(user_memories)} total"
|
|
89
119
|
)
|
|
90
120
|
return results
|
|
121
|
+
|
|
122
|
+
# ---- 扩展协议实现(§7.3) ----
|
|
123
|
+
|
|
124
|
+
def capabilities(self) -> Set[str]:
|
|
125
|
+
return {
|
|
126
|
+
CAP_SEARCH,
|
|
127
|
+
CAP_ADD,
|
|
128
|
+
CAP_STRUCTURED_SEARCH,
|
|
129
|
+
CAP_UPDATE,
|
|
130
|
+
CAP_DELETE,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
def search_records(
|
|
134
|
+
self, user_id: str, query: str, top_k: int = 5, **kwargs
|
|
135
|
+
) -> List[LongTermMemoryRecord]:
|
|
136
|
+
"""结构化检索:复用关键词匹配,返回稳定生成的本地 ID。"""
|
|
137
|
+
entries = self.search_memory(user_id, query, top_k=top_k)
|
|
138
|
+
records: List[LongTermMemoryRecord] = []
|
|
139
|
+
for entry in entries:
|
|
140
|
+
memory_id = self._find_entry_id(user_id, entry)
|
|
141
|
+
if memory_id is None:
|
|
142
|
+
continue
|
|
143
|
+
records.append(
|
|
144
|
+
LongTermMemoryRecord(
|
|
145
|
+
memory_id=memory_id,
|
|
146
|
+
content=self._entry_text(entry),
|
|
147
|
+
score=None,
|
|
148
|
+
user_id=user_id,
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
return records
|
|
152
|
+
|
|
153
|
+
def update_memory(
|
|
154
|
+
self,
|
|
155
|
+
*,
|
|
156
|
+
user_id: str,
|
|
157
|
+
memory_id: str,
|
|
158
|
+
content: str,
|
|
159
|
+
**kwargs,
|
|
160
|
+
) -> MemoryMutationResult:
|
|
161
|
+
user_index = self._user_entry_index.get(user_id, {})
|
|
162
|
+
old_entry = user_index.get(memory_id)
|
|
163
|
+
if old_entry is None:
|
|
164
|
+
return MemoryMutationResult(ok=False, memory_id=memory_id, status="not_found")
|
|
165
|
+
new_entry = json.dumps(
|
|
166
|
+
{"role": "user", "parts": [{"text": content}]}, ensure_ascii=False
|
|
167
|
+
)
|
|
168
|
+
memories = self._storage[user_id]
|
|
169
|
+
for i, entry in enumerate(memories):
|
|
170
|
+
if entry is old_entry:
|
|
171
|
+
memories[i] = new_entry
|
|
172
|
+
break
|
|
173
|
+
self._entry_ids[memory_id] = new_entry
|
|
174
|
+
user_index[memory_id] = new_entry
|
|
175
|
+
return MemoryMutationResult(
|
|
176
|
+
ok=True,
|
|
177
|
+
memory_id=memory_id,
|
|
178
|
+
new_memory_id=memory_id,
|
|
179
|
+
status="updated",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def delete_memory(
|
|
183
|
+
self,
|
|
184
|
+
*,
|
|
185
|
+
user_id: str,
|
|
186
|
+
memory_id: str,
|
|
187
|
+
**kwargs,
|
|
188
|
+
) -> MemoryMutationResult:
|
|
189
|
+
user_index = self._user_entry_index.get(user_id, {})
|
|
190
|
+
entry = user_index.pop(memory_id, None)
|
|
191
|
+
if entry is None:
|
|
192
|
+
return MemoryMutationResult(
|
|
193
|
+
ok=True, memory_id=memory_id, status="already_absent", message="目标记忆已不存在"
|
|
194
|
+
)
|
|
195
|
+
self._entry_ids.pop(memory_id, None)
|
|
196
|
+
memories = self._storage.get(user_id, [])
|
|
197
|
+
try:
|
|
198
|
+
memories.remove(entry)
|
|
199
|
+
except ValueError:
|
|
200
|
+
pass
|
|
201
|
+
return MemoryMutationResult(ok=True, memory_id=memory_id, status="deleted")
|
|
202
|
+
|
|
203
|
+
def _find_entry_id(self, user_id: str, entry: str) -> str | None:
|
|
204
|
+
"""根据原始条目定位 memory_id(反向查表)。"""
|
|
205
|
+
for memory_id, stored in self._user_entry_index.get(user_id, {}).items():
|
|
206
|
+
if stored is entry or stored == entry:
|
|
207
|
+
return memory_id
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
# ---- 内部工具 ----
|
|
211
|
+
|
|
212
|
+
@staticmethod
|
|
213
|
+
def _entry_text(entry: str) -> str:
|
|
214
|
+
"""提取事件字符串里的正文(兼容 JSON 事件与纯文本)。"""
|
|
215
|
+
try:
|
|
216
|
+
payload = json.loads(entry)
|
|
217
|
+
except (json.JSONDecodeError, TypeError):
|
|
218
|
+
return entry
|
|
219
|
+
if isinstance(payload, dict):
|
|
220
|
+
parts = payload.get("parts")
|
|
221
|
+
if isinstance(parts, list) and parts and isinstance(parts[0], dict):
|
|
222
|
+
return str(parts[0].get("text", entry))
|
|
223
|
+
return entry
|