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,199 @@
|
|
|
1
|
+
"""Cloud EvalSet publication orchestration shared by CLI and Studio."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Protocol
|
|
8
|
+
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
|
|
11
|
+
from .cloud_binding import CloudBinding, CloudBindingStore
|
|
12
|
+
from .cloud_converter import (
|
|
13
|
+
CloudDatasetSnapshot,
|
|
14
|
+
evalset_from_dataset_snapshot,
|
|
15
|
+
evalset_to_dataset_snapshot,
|
|
16
|
+
)
|
|
17
|
+
from .contracts import CloudDatasetRef, DataPolicy, EvalSetVersion, EvaluationModel
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CloudEvalSetPreviewError(ValueError):
|
|
21
|
+
"""Raised before an EvalSet body is allowed to leave the local process."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CloudEvalSetPublishResult(EvaluationModel):
|
|
25
|
+
"""Provider acknowledgement for one immutable Dataset snapshot."""
|
|
26
|
+
|
|
27
|
+
dataset_id: str = Field(min_length=1)
|
|
28
|
+
dataset_version: int = Field(ge=1)
|
|
29
|
+
project_id: str | None = None
|
|
30
|
+
schema_hash: str = Field(min_length=64, max_length=64)
|
|
31
|
+
content_digest: str = Field(min_length=64, max_length=64)
|
|
32
|
+
row_count: int = Field(ge=0)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CloudEvalSetPullResult(EvaluationModel):
|
|
36
|
+
"""One validated immutable cloud snapshot and its normalized local form."""
|
|
37
|
+
|
|
38
|
+
snapshot: CloudDatasetSnapshot
|
|
39
|
+
evalset: EvalSetVersion
|
|
40
|
+
cloud_dataset: CloudDatasetRef
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CloudEvalSetCatalogItem(EvaluationModel):
|
|
44
|
+
"""One immutable Dataset version exposed by the cloud catalog."""
|
|
45
|
+
|
|
46
|
+
dataset_id: str = Field(min_length=1)
|
|
47
|
+
name: str = Field(min_length=1)
|
|
48
|
+
project_id: str | None = None
|
|
49
|
+
version: int = Field(ge=1)
|
|
50
|
+
schema_hash: str = Field(min_length=64, max_length=64)
|
|
51
|
+
content_digest: str = Field(min_length=64, max_length=64)
|
|
52
|
+
row_count: int = Field(ge=0)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class CloudDatasetClient(Protocol):
|
|
56
|
+
"""Minimal provider contract required before a snapshot can be published."""
|
|
57
|
+
|
|
58
|
+
async def publish_snapshot(
|
|
59
|
+
self,
|
|
60
|
+
snapshot: CloudDatasetSnapshot,
|
|
61
|
+
*,
|
|
62
|
+
dataset_id: str | None,
|
|
63
|
+
base_version: int | None,
|
|
64
|
+
idempotency_key: str,
|
|
65
|
+
) -> CloudEvalSetPublishResult: ...
|
|
66
|
+
|
|
67
|
+
async def read_snapshot(
|
|
68
|
+
self,
|
|
69
|
+
dataset_id: str,
|
|
70
|
+
version: int,
|
|
71
|
+
*,
|
|
72
|
+
project_id: str | None = None,
|
|
73
|
+
) -> CloudDatasetSnapshot: ...
|
|
74
|
+
|
|
75
|
+
async def list_datasets(
|
|
76
|
+
self,
|
|
77
|
+
*,
|
|
78
|
+
project_id: str | None = None,
|
|
79
|
+
) -> list[CloudEvalSetCatalogItem]: ...
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class CloudEvalSetService:
|
|
83
|
+
"""Validate, publish, and bind EvalSets without changing runtime execution."""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
workspace_root: str | Path,
|
|
88
|
+
client: CloudDatasetClient,
|
|
89
|
+
*,
|
|
90
|
+
provider: str = "agent-eval/evalsmith",
|
|
91
|
+
):
|
|
92
|
+
self.bindings = CloudBindingStore(workspace_root)
|
|
93
|
+
self.client = client
|
|
94
|
+
self.provider = provider
|
|
95
|
+
|
|
96
|
+
def preview(self, evalset: EvalSetVersion, *, data_policy: DataPolicy) -> CloudDatasetSnapshot:
|
|
97
|
+
"""Return the exact outgoing snapshot or fail before any network request."""
|
|
98
|
+
|
|
99
|
+
if data_policy is DataPolicy.LOCAL_ONLY:
|
|
100
|
+
raise CloudEvalSetPreviewError("DataPolicy=local_only 禁止上传 EvalSet 正文")
|
|
101
|
+
if data_policy is DataPolicy.METADATA_ONLY:
|
|
102
|
+
raise CloudEvalSetPreviewError(
|
|
103
|
+
"DataPolicy=metadata_only 不能发布包含 Case 正文的 EvalSet"
|
|
104
|
+
)
|
|
105
|
+
if data_policy is not DataPolicy.FULL_TRACE:
|
|
106
|
+
raise CloudEvalSetPreviewError("当前端云评测集发布仅支持显式 DataPolicy=full_trace")
|
|
107
|
+
return evalset_to_dataset_snapshot(evalset)
|
|
108
|
+
|
|
109
|
+
async def publish(
|
|
110
|
+
self,
|
|
111
|
+
evalset: EvalSetVersion,
|
|
112
|
+
*,
|
|
113
|
+
evalset_path: str,
|
|
114
|
+
data_policy: DataPolicy,
|
|
115
|
+
dataset_id: str | None = None,
|
|
116
|
+
idempotency_key: str | None = None,
|
|
117
|
+
) -> CloudEvalSetPublishResult:
|
|
118
|
+
"""Publish a full snapshot to an existing Dataset and advance its binding."""
|
|
119
|
+
|
|
120
|
+
if idempotency_key is not None and not idempotency_key.strip():
|
|
121
|
+
raise ValueError("Idempotency-Key 不能为空")
|
|
122
|
+
snapshot = self.preview(evalset, data_policy=data_policy)
|
|
123
|
+
existing = self.bindings.read(evalset_path)
|
|
124
|
+
requested_dataset_id = str(dataset_id or "").strip() or None
|
|
125
|
+
target_dataset_id = requested_dataset_id or (existing.dataset_id if existing else None)
|
|
126
|
+
if target_dataset_id is None:
|
|
127
|
+
raise ValueError("datasetId is required for the first publish of an EvalSet")
|
|
128
|
+
resolved_idempotency_key = idempotency_key or self._idempotency_key(
|
|
129
|
+
target_dataset_id,
|
|
130
|
+
snapshot.content_digest,
|
|
131
|
+
)
|
|
132
|
+
result = await self.client.publish_snapshot(
|
|
133
|
+
snapshot,
|
|
134
|
+
dataset_id=target_dataset_id,
|
|
135
|
+
base_version=None,
|
|
136
|
+
idempotency_key=resolved_idempotency_key,
|
|
137
|
+
)
|
|
138
|
+
if result.dataset_id != target_dataset_id:
|
|
139
|
+
raise CloudEvalSetPreviewError("云端返回的 datasetId 与目标 Dataset 不一致")
|
|
140
|
+
if result.content_digest != snapshot.content_digest:
|
|
141
|
+
raise CloudEvalSetPreviewError("云端返回的 contentDigest 与本地预检结果不一致")
|
|
142
|
+
if result.schema_hash != snapshot.schema_hash:
|
|
143
|
+
raise CloudEvalSetPreviewError("云端返回的 schemaHash 与本地预检结果不一致")
|
|
144
|
+
if result.row_count != len(snapshot.rows):
|
|
145
|
+
raise CloudEvalSetPreviewError("云端返回的 RowCount 与本地预检结果不一致")
|
|
146
|
+
self.bindings.write(
|
|
147
|
+
CloudBinding(
|
|
148
|
+
evalset_path=evalset_path,
|
|
149
|
+
content_digest=snapshot.content_digest,
|
|
150
|
+
provider=self.provider,
|
|
151
|
+
project_id=result.project_id,
|
|
152
|
+
dataset_id=result.dataset_id,
|
|
153
|
+
dataset_version=result.dataset_version,
|
|
154
|
+
schema_hash=result.schema_hash,
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
def _idempotency_key(dataset_id: str, content_digest: str) -> str:
|
|
161
|
+
identity = f"{dataset_id}:{content_digest}".encode("utf-8")
|
|
162
|
+
return f"ksadk-evalset-{hashlib.sha256(identity).hexdigest()}"
|
|
163
|
+
|
|
164
|
+
async def pull(
|
|
165
|
+
self,
|
|
166
|
+
*,
|
|
167
|
+
dataset_id: str,
|
|
168
|
+
version: int,
|
|
169
|
+
project_id: str | None = None,
|
|
170
|
+
) -> CloudEvalSetPullResult:
|
|
171
|
+
"""Read and validate one immutable Dataset version before execution."""
|
|
172
|
+
|
|
173
|
+
if not dataset_id.strip() or version < 1:
|
|
174
|
+
raise ValueError("datasetId 和 version 必须有效")
|
|
175
|
+
snapshot = await self.client.read_snapshot(
|
|
176
|
+
dataset_id,
|
|
177
|
+
version,
|
|
178
|
+
project_id=project_id,
|
|
179
|
+
)
|
|
180
|
+
evalset = evalset_from_dataset_snapshot(snapshot)
|
|
181
|
+
if evalset.content_digest != snapshot.content_digest:
|
|
182
|
+
raise CloudEvalSetPreviewError("云端 snapshot 的 contentDigest 校验失败")
|
|
183
|
+
reference = CloudDatasetRef(
|
|
184
|
+
provider=self.provider,
|
|
185
|
+
project_id=project_id,
|
|
186
|
+
dataset_id=dataset_id,
|
|
187
|
+
version=version,
|
|
188
|
+
schema_hash=snapshot.schema_hash,
|
|
189
|
+
content_digest=snapshot.content_digest,
|
|
190
|
+
row_count=len(snapshot.rows),
|
|
191
|
+
)
|
|
192
|
+
return CloudEvalSetPullResult(
|
|
193
|
+
snapshot=snapshot,
|
|
194
|
+
evalset=evalset,
|
|
195
|
+
cloud_dataset=reference,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
async def catalog(self, *, project_id: str | None = None) -> list[CloudEvalSetCatalogItem]:
|
|
199
|
+
return await self.client.list_datasets(project_id=project_id)
|
ksadk/evaluation/contracts.py
CHANGED
|
@@ -58,8 +58,11 @@ class AssertionType(str, Enum):
|
|
|
58
58
|
RUNTIME_MAX_LATENCY_MS = "runtime.maxLatencyMs"
|
|
59
59
|
RUNTIME_MAX_INPUT_TOKENS = "runtime.maxInputTokens"
|
|
60
60
|
RUNTIME_MAX_OUTPUT_TOKENS = "runtime.maxOutputTokens"
|
|
61
|
+
RUNTIME_MAX_TOTAL_TOKENS = "runtime.maxTotalTokens"
|
|
61
62
|
TOOL_CALLED = "tool.called"
|
|
62
63
|
TOOL_NOT_CALLED = "tool.notCalled"
|
|
64
|
+
TOOL_SUCCEEDED = "tool.succeeded"
|
|
65
|
+
TOOL_SEQUENCE = "tool.sequence"
|
|
63
66
|
|
|
64
67
|
|
|
65
68
|
class DataPolicy(str, Enum):
|
|
@@ -134,6 +137,13 @@ class AssertionSpec(EvaluationModel):
|
|
|
134
137
|
raise ValueError(f"{self.type} 的 value 必须是非负数字")
|
|
135
138
|
if self.value < 0:
|
|
136
139
|
raise ValueError(f"{self.type} 的 value 必须是非负数字")
|
|
140
|
+
elif self.type is AssertionType.TOOL_SEQUENCE:
|
|
141
|
+
if (
|
|
142
|
+
not isinstance(self.value, list)
|
|
143
|
+
or not self.value
|
|
144
|
+
or any(not isinstance(item, str) or not item.strip() for item in self.value)
|
|
145
|
+
):
|
|
146
|
+
raise ValueError("tool.sequence 的 value 必须是非空工具名称数组")
|
|
137
147
|
elif not isinstance(self.value, str):
|
|
138
148
|
raise ValueError(f"{self.type} 的 value 必须是字符串")
|
|
139
149
|
return self
|
|
@@ -155,9 +165,21 @@ class EvalCase(EvaluationModel):
|
|
|
155
165
|
data = dict(value)
|
|
156
166
|
if "input" in data and "turns" not in data:
|
|
157
167
|
turn = {"input": data.pop("input")}
|
|
158
|
-
for field in (
|
|
168
|
+
for field in (
|
|
169
|
+
"expected_output",
|
|
170
|
+
"expectedOutput",
|
|
171
|
+
"reference_output",
|
|
172
|
+
"referenceOutput",
|
|
173
|
+
"expected_tools",
|
|
174
|
+
"expectedTools",
|
|
175
|
+
):
|
|
159
176
|
if field in data:
|
|
160
|
-
|
|
177
|
+
normalized_field = (
|
|
178
|
+
"expected_output"
|
|
179
|
+
if field in {"reference_output", "referenceOutput"}
|
|
180
|
+
else field
|
|
181
|
+
)
|
|
182
|
+
turn[normalized_field] = data.pop(field)
|
|
161
183
|
data["turns"] = [turn]
|
|
162
184
|
return data
|
|
163
185
|
|
|
@@ -200,6 +222,18 @@ class EvalSetVersion(EvaluationModel):
|
|
|
200
222
|
return _content_digest(payload)
|
|
201
223
|
|
|
202
224
|
|
|
225
|
+
class CloudDatasetRef(EvaluationModel):
|
|
226
|
+
"""Immutable reference to the exact cloud Dataset snapshot used by a run."""
|
|
227
|
+
|
|
228
|
+
provider: str = Field(min_length=1, max_length=256)
|
|
229
|
+
project_id: str | None = Field(default=None, min_length=1, max_length=256)
|
|
230
|
+
dataset_id: str = Field(min_length=1, max_length=256)
|
|
231
|
+
version: int = Field(ge=1)
|
|
232
|
+
schema_hash: str = Field(min_length=64, max_length=64)
|
|
233
|
+
content_digest: str = Field(min_length=64, max_length=64)
|
|
234
|
+
row_count: int = Field(default=0, ge=0)
|
|
235
|
+
|
|
236
|
+
|
|
203
237
|
# ---------------------------------------------------------------------------
|
|
204
238
|
# Target identity & references
|
|
205
239
|
# ---------------------------------------------------------------------------
|
|
@@ -262,6 +296,7 @@ class EvaluationRequest(EvaluationModel):
|
|
|
262
296
|
target: TargetRef
|
|
263
297
|
config: EvaluationConfig = Field(default_factory=EvaluationConfig)
|
|
264
298
|
report_dir: str | None = Field(default=None, min_length=1, max_length=2048)
|
|
299
|
+
cloud_dataset: CloudDatasetRef | None = None
|
|
265
300
|
|
|
266
301
|
|
|
267
302
|
class EvalRunSpec(EvaluationModel):
|
|
@@ -273,6 +308,7 @@ class EvalRunSpec(EvaluationModel):
|
|
|
273
308
|
config: EvaluationConfig = Field(default_factory=EvaluationConfig)
|
|
274
309
|
environment_digest: str = Field(default="", max_length=128)
|
|
275
310
|
attempt: int = Field(default=1, ge=1)
|
|
311
|
+
cloud_dataset: CloudDatasetRef | None = None
|
|
276
312
|
|
|
277
313
|
|
|
278
314
|
# ---------------------------------------------------------------------------
|
|
@@ -288,11 +324,25 @@ class TraceRef(EvaluationModel):
|
|
|
288
324
|
run_id: str | None = None
|
|
289
325
|
trace_id: str | None = None
|
|
290
326
|
root_span_id: str | None = None
|
|
327
|
+
session_id: str | None = None
|
|
328
|
+
invocation_id: str | None = None
|
|
329
|
+
seq_start: int | None = Field(default=None, ge=1)
|
|
330
|
+
seq_end: int | None = Field(default=None, ge=1)
|
|
331
|
+
remote_task_id: str | None = None
|
|
291
332
|
seq_id: int | None = Field(default=None, ge=1)
|
|
292
333
|
|
|
293
334
|
@model_validator(mode="after")
|
|
294
335
|
def require_reference(self) -> "TraceRef":
|
|
295
|
-
if not any(
|
|
336
|
+
if not any(
|
|
337
|
+
(
|
|
338
|
+
self.run_id,
|
|
339
|
+
self.trace_id,
|
|
340
|
+
self.session_id,
|
|
341
|
+
self.invocation_id,
|
|
342
|
+
self.remote_task_id,
|
|
343
|
+
self.seq_id,
|
|
344
|
+
)
|
|
345
|
+
):
|
|
296
346
|
raise ValueError("TraceRef 至少需要一个可查询 ID")
|
|
297
347
|
return self
|
|
298
348
|
|
|
@@ -306,6 +356,16 @@ class UsageSnapshot(EvaluationModel):
|
|
|
306
356
|
reported: bool = False
|
|
307
357
|
|
|
308
358
|
|
|
359
|
+
class ToolCallEvidence(EvaluationModel):
|
|
360
|
+
"""Non-sensitive projection of one runtime tool invocation."""
|
|
361
|
+
|
|
362
|
+
call_id: str = Field(min_length=1, max_length=256)
|
|
363
|
+
name: str = Field(min_length=1, max_length=256)
|
|
364
|
+
status: Literal["SUCCEEDED", "ERROR", "INCOMPLETE"]
|
|
365
|
+
seq_start: int | None = Field(default=None, ge=1)
|
|
366
|
+
seq_end: int | None = Field(default=None, ge=1)
|
|
367
|
+
|
|
368
|
+
|
|
309
369
|
class TargetRun(EvaluationModel):
|
|
310
370
|
"""Normalized result returned by a target adapter for one case."""
|
|
311
371
|
|
|
@@ -316,6 +376,8 @@ class TargetRun(EvaluationModel):
|
|
|
316
376
|
error_code: str | None = None
|
|
317
377
|
error_message: str | None = None
|
|
318
378
|
trace_ref: TraceRef | None = None
|
|
379
|
+
trace_refs: list[TraceRef] = Field(default_factory=list)
|
|
380
|
+
tool_calls: list[ToolCallEvidence] = Field(default_factory=list)
|
|
319
381
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
320
382
|
|
|
321
383
|
|
|
@@ -377,7 +439,14 @@ class EvalRunReport(EvaluationModel):
|
|
|
377
439
|
self.summary = self._summarize_cases()
|
|
378
440
|
expected = self.compute_digest()
|
|
379
441
|
if self.report_digest and self.report_digest != expected:
|
|
380
|
-
|
|
442
|
+
legacy_payload = self.model_dump(
|
|
443
|
+
mode="json", by_alias=False, exclude={"report_digest"}
|
|
444
|
+
)
|
|
445
|
+
legacy_payload["spec"].pop("cloud_dataset", None)
|
|
446
|
+
if self.spec.cloud_dataset is not None or self.report_digest != _content_digest(
|
|
447
|
+
legacy_payload
|
|
448
|
+
):
|
|
449
|
+
raise ValueError("reportDigest 与规范化报告内容不一致")
|
|
381
450
|
self.report_digest = expected
|
|
382
451
|
return self
|
|
383
452
|
|
ksadk/evaluation/evaluators.py
CHANGED
|
@@ -49,11 +49,15 @@ class _EvaluatorDefinition:
|
|
|
49
49
|
|
|
50
50
|
REFERENCE_MATCH_EVALUATOR = "reference_match@v1"
|
|
51
51
|
LLM_JUDGE_EVALUATOR = "llm_judge@v1"
|
|
52
|
-
|
|
52
|
+
BUSINESS_STANDARD_EVALUATOR = "business_standard@v1"
|
|
53
|
+
LEGACY_ASSERTION_EVALUATORS = (
|
|
53
54
|
"response_contract@v1",
|
|
54
55
|
"runtime_budget@v1",
|
|
55
56
|
"tool_trajectory@v1",
|
|
56
57
|
)
|
|
58
|
+
# Kept for external imports only. Empty evaluator selection uses the
|
|
59
|
+
# data-derived automatic plan in _automatic_evaluator_names instead.
|
|
60
|
+
DEFAULT_EVALUATORS = LEGACY_ASSERTION_EVALUATORS
|
|
57
61
|
_RESPONSE_MATCH_THRESHOLD = 0.8
|
|
58
62
|
_TOKEN_PATTERN = re.compile(r"[A-Za-z0-9_]+|[\u4e00-\u9fff]")
|
|
59
63
|
|
|
@@ -67,7 +71,7 @@ def evaluate_case(
|
|
|
67
71
|
"""Run selected evaluators in request order."""
|
|
68
72
|
|
|
69
73
|
context = _EvaluationContext(case, target_run, config or EvaluationConfig())
|
|
70
|
-
evaluators = _resolve_evaluators(evaluator_names)
|
|
74
|
+
evaluators = _resolve_evaluators(evaluator_names, context)
|
|
71
75
|
return _run_evaluators(context, evaluators)
|
|
72
76
|
|
|
73
77
|
|
|
@@ -82,14 +86,58 @@ async def evaluate_case_async(
|
|
|
82
86
|
return await asyncio.to_thread(evaluate_case, case, target_run, evaluator_names, config)
|
|
83
87
|
|
|
84
88
|
|
|
85
|
-
def _resolve_evaluators(
|
|
86
|
-
|
|
89
|
+
def _resolve_evaluators(
|
|
90
|
+
evaluator_names: list[str], context: _EvaluationContext
|
|
91
|
+
) -> list[_EvaluatorDefinition]:
|
|
92
|
+
selected_names = evaluator_names or _automatic_evaluator_names(context.case, context.config)
|
|
87
93
|
unsupported_names = [name for name in selected_names if name not in _EVALUATOR_REGISTRY]
|
|
88
94
|
if unsupported_names:
|
|
89
95
|
raise ValueError(f"不支持的评估器: {', '.join(unsupported_names)}")
|
|
90
96
|
return [_EVALUATOR_REGISTRY[name] for name in selected_names]
|
|
91
97
|
|
|
92
98
|
|
|
99
|
+
def resolve_evaluator_plan(
|
|
100
|
+
cases: list[EvalCase],
|
|
101
|
+
evaluator_names: list[str],
|
|
102
|
+
config: EvaluationConfig,
|
|
103
|
+
) -> list[str]:
|
|
104
|
+
"""Return the explicit or data-derived evaluator plan for an EvalSet."""
|
|
105
|
+
|
|
106
|
+
if evaluator_names:
|
|
107
|
+
unsupported_names = [name for name in evaluator_names if name not in _EVALUATOR_REGISTRY]
|
|
108
|
+
if unsupported_names:
|
|
109
|
+
raise ValueError(f"不支持的评估器: {', '.join(unsupported_names)}")
|
|
110
|
+
return list(evaluator_names)
|
|
111
|
+
|
|
112
|
+
plan: list[str] = []
|
|
113
|
+
for case in cases:
|
|
114
|
+
for evaluator_name in _automatic_evaluator_names(case, config):
|
|
115
|
+
if evaluator_name not in plan:
|
|
116
|
+
plan.append(evaluator_name)
|
|
117
|
+
return plan
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _automatic_evaluator_names(case: EvalCase, config: EvaluationConfig) -> list[str]:
|
|
121
|
+
"""Select only the evaluators whose business standard is present in a Case."""
|
|
122
|
+
|
|
123
|
+
names: list[str] = []
|
|
124
|
+
if _response_assertions(case):
|
|
125
|
+
names.append("response_contract@v1")
|
|
126
|
+
if _runtime_assertions(case):
|
|
127
|
+
names.append("runtime_budget@v1")
|
|
128
|
+
if _tool_requirements(case):
|
|
129
|
+
names.append("tool_trajectory@v1")
|
|
130
|
+
|
|
131
|
+
if _final_expected_output(case):
|
|
132
|
+
if _judge_unavailable_reason(config) is None:
|
|
133
|
+
names.insert(0, LLM_JUDGE_EVALUATOR)
|
|
134
|
+
else:
|
|
135
|
+
names.insert(0, REFERENCE_MATCH_EVALUATOR)
|
|
136
|
+
elif not _response_assertions(case):
|
|
137
|
+
names.insert(0, BUSINESS_STANDARD_EVALUATOR)
|
|
138
|
+
return names
|
|
139
|
+
|
|
140
|
+
|
|
93
141
|
def _run_evaluators(
|
|
94
142
|
context: _EvaluationContext,
|
|
95
143
|
evaluators: list[_EvaluatorDefinition],
|
|
@@ -159,6 +207,21 @@ def _evaluate_llm_judge(
|
|
|
159
207
|
return [_judge_score_metric(score, context.config.judge_model)]
|
|
160
208
|
|
|
161
209
|
|
|
210
|
+
def _evaluate_business_standard(context: _EvaluationContext) -> list[MetricResult]:
|
|
211
|
+
"""Prevent execution-only Cases from being reported as business-quality passes."""
|
|
212
|
+
|
|
213
|
+
return [
|
|
214
|
+
MetricResult(
|
|
215
|
+
name="response_quality",
|
|
216
|
+
status=MetricStatus.UNAVAILABLE,
|
|
217
|
+
evidence={
|
|
218
|
+
"evaluator": BUSINESS_STANDARD_EVALUATOR,
|
|
219
|
+
"reason": "Case 未提供响应业务标准",
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
|
|
162
225
|
def _evaluate_response_contract(
|
|
163
226
|
context: _EvaluationContext,
|
|
164
227
|
) -> list[MetricResult]:
|
|
@@ -184,24 +247,102 @@ def _evaluate_runtime_budget(
|
|
|
184
247
|
def _evaluate_tool_trajectory(
|
|
185
248
|
context: _EvaluationContext,
|
|
186
249
|
) -> list[MetricResult]:
|
|
187
|
-
"""
|
|
250
|
+
"""Evaluate tool requirements against normalized RuntimeEvent evidence."""
|
|
188
251
|
|
|
189
252
|
requirements = _tool_requirements(context.case)
|
|
190
253
|
if not requirements:
|
|
191
254
|
return []
|
|
192
255
|
|
|
193
|
-
|
|
194
|
-
|
|
256
|
+
if context.target_run.trace_ref is None:
|
|
257
|
+
return [
|
|
258
|
+
MetricResult(
|
|
259
|
+
name="tool_trajectory",
|
|
260
|
+
status=MetricStatus.UNAVAILABLE,
|
|
261
|
+
required=required,
|
|
262
|
+
evidence={
|
|
263
|
+
"assertion": assertion_type,
|
|
264
|
+
"tool": expected,
|
|
265
|
+
"reason": "Target 未提供可查询的标准化工具轨迹",
|
|
266
|
+
},
|
|
267
|
+
)
|
|
268
|
+
for assertion_type, expected, required in requirements
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
results: list[MetricResult] = []
|
|
272
|
+
for assertion_type, expected, required in requirements:
|
|
273
|
+
results.append(
|
|
274
|
+
_tool_metric(
|
|
275
|
+
assertion_type,
|
|
276
|
+
expected,
|
|
277
|
+
required,
|
|
278
|
+
context.target_run.tool_calls,
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
return results
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _tool_metric(
|
|
285
|
+
assertion_type: str,
|
|
286
|
+
expected: str | list[str],
|
|
287
|
+
required: bool,
|
|
288
|
+
tool_calls: list[Any],
|
|
289
|
+
) -> MetricResult:
|
|
290
|
+
if assertion_type == AssertionType.TOOL_SEQUENCE.value:
|
|
291
|
+
expected_sequence = list(expected) if isinstance(expected, list) else []
|
|
292
|
+
ordered_calls = sorted(
|
|
293
|
+
(call for call in tool_calls if call.status == "SUCCEEDED"),
|
|
294
|
+
key=lambda call: call.seq_start if call.seq_start is not None else float("inf"),
|
|
295
|
+
)
|
|
296
|
+
actual_sequence = [call.name for call in ordered_calls]
|
|
297
|
+
matched_calls = _ordered_tool_subsequence(ordered_calls, expected_sequence)
|
|
298
|
+
passed = len(matched_calls) == len(expected_sequence)
|
|
299
|
+
return MetricResult(
|
|
195
300
|
name="tool_trajectory",
|
|
196
|
-
status=MetricStatus.
|
|
301
|
+
status=MetricStatus.PASS if passed else MetricStatus.FAIL,
|
|
302
|
+
score=1.0 if passed else 0.0,
|
|
197
303
|
required=required,
|
|
198
304
|
evidence={
|
|
199
305
|
"assertion": assertion_type,
|
|
200
|
-
"
|
|
306
|
+
"expectedSequence": expected_sequence,
|
|
307
|
+
"actualSequence": actual_sequence,
|
|
308
|
+
"matchedCallIds": matched_calls,
|
|
201
309
|
},
|
|
202
310
|
)
|
|
203
|
-
|
|
204
|
-
|
|
311
|
+
|
|
312
|
+
tool_name = str(expected)
|
|
313
|
+
matched = [call for call in tool_calls if call.name == tool_name]
|
|
314
|
+
if assertion_type == AssertionType.TOOL_NOT_CALLED.value:
|
|
315
|
+
passed = not matched
|
|
316
|
+
matched_ids = [call.call_id for call in matched]
|
|
317
|
+
elif assertion_type == AssertionType.TOOL_SUCCEEDED.value:
|
|
318
|
+
matched_ids = [call.call_id for call in matched if call.status == "SUCCEEDED"]
|
|
319
|
+
passed = bool(matched_ids)
|
|
320
|
+
else:
|
|
321
|
+
matched_ids = [call.call_id for call in matched]
|
|
322
|
+
passed = bool(matched_ids)
|
|
323
|
+
return MetricResult(
|
|
324
|
+
name="tool_trajectory",
|
|
325
|
+
status=MetricStatus.PASS if passed else MetricStatus.FAIL,
|
|
326
|
+
score=1.0 if passed else 0.0,
|
|
327
|
+
required=required,
|
|
328
|
+
evidence={
|
|
329
|
+
"assertion": assertion_type,
|
|
330
|
+
"tool": tool_name,
|
|
331
|
+
"matchedCallIds": matched_ids,
|
|
332
|
+
},
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _ordered_tool_subsequence(tool_calls: list[Any], expected_sequence: list[str]) -> list[str]:
|
|
337
|
+
matched_call_ids: list[str] = []
|
|
338
|
+
expected_index = 0
|
|
339
|
+
for call in tool_calls:
|
|
340
|
+
if expected_index == len(expected_sequence):
|
|
341
|
+
break
|
|
342
|
+
if call.name == expected_sequence[expected_index]:
|
|
343
|
+
matched_call_ids.append(call.call_id)
|
|
344
|
+
expected_index += 1
|
|
345
|
+
return matched_call_ids
|
|
205
346
|
|
|
206
347
|
|
|
207
348
|
def _response_assertions(case: EvalCase) -> list[AssertionSpec]:
|
|
@@ -220,16 +361,37 @@ def _runtime_assertions(case: EvalCase) -> list[AssertionSpec]:
|
|
|
220
361
|
]
|
|
221
362
|
|
|
222
363
|
|
|
223
|
-
def _tool_requirements(case: EvalCase) -> list[tuple[str, bool]]:
|
|
364
|
+
def _tool_requirements(case: EvalCase) -> list[tuple[str, str | list[str], bool]]:
|
|
224
365
|
requirements = [
|
|
225
|
-
(
|
|
366
|
+
(
|
|
367
|
+
assertion.type.value,
|
|
368
|
+
assertion.value,
|
|
369
|
+
assertion.required,
|
|
370
|
+
)
|
|
226
371
|
for assertion in case.assertions
|
|
227
|
-
if assertion.type
|
|
372
|
+
if assertion.type
|
|
373
|
+
in {
|
|
374
|
+
AssertionType.TOOL_CALLED,
|
|
375
|
+
AssertionType.TOOL_NOT_CALLED,
|
|
376
|
+
AssertionType.TOOL_SUCCEEDED,
|
|
377
|
+
AssertionType.TOOL_SEQUENCE,
|
|
378
|
+
}
|
|
228
379
|
]
|
|
229
|
-
requirements.extend(
|
|
380
|
+
requirements.extend(
|
|
381
|
+
("tool.expected", str(tool.get("name") or ""), True)
|
|
382
|
+
for turn in case.turns
|
|
383
|
+
for tool in turn.expected_tools
|
|
384
|
+
if str(tool.get("name") or "").strip()
|
|
385
|
+
)
|
|
230
386
|
return requirements
|
|
231
387
|
|
|
232
388
|
|
|
389
|
+
def evaluate_tool_trajectory(case: EvalCase, target_run: TargetRun) -> list[MetricResult]:
|
|
390
|
+
"""Compatibility entry point for direct deterministic tool evaluation."""
|
|
391
|
+
|
|
392
|
+
return _evaluate_tool_trajectory(_EvaluationContext(case, target_run, EvaluationConfig()))
|
|
393
|
+
|
|
394
|
+
|
|
233
395
|
def _response_metric(assertion: AssertionSpec, output: str) -> MetricResult:
|
|
234
396
|
reason = ""
|
|
235
397
|
if assertion.type is AssertionType.RESPONSE_EQUALS:
|
|
@@ -262,6 +424,8 @@ def _runtime_metric(assertion: AssertionSpec, target_run: TargetRun) -> MetricRe
|
|
|
262
424
|
actual = None
|
|
263
425
|
elif assertion.type is AssertionType.RUNTIME_MAX_INPUT_TOKENS:
|
|
264
426
|
actual = target_run.usage.input_tokens
|
|
427
|
+
elif assertion.type is AssertionType.RUNTIME_MAX_TOTAL_TOKENS:
|
|
428
|
+
actual = target_run.usage.total_tokens
|
|
265
429
|
else:
|
|
266
430
|
actual = target_run.usage.output_tokens
|
|
267
431
|
|
|
@@ -429,8 +593,7 @@ def _run_llm_judge(
|
|
|
429
593
|
metric = GEval(
|
|
430
594
|
name="Response quality",
|
|
431
595
|
criteria=(
|
|
432
|
-
"Determine whether the actual output is factually correct "
|
|
433
|
-
"based on the expected output."
|
|
596
|
+
"Determine whether the actual output is factually correct based on the expected output."
|
|
434
597
|
),
|
|
435
598
|
evaluation_params=[
|
|
436
599
|
LLMTestCaseParams.INPUT,
|
|
@@ -451,6 +614,9 @@ def _run_llm_judge(
|
|
|
451
614
|
|
|
452
615
|
|
|
453
616
|
_EVALUATORS = (
|
|
617
|
+
_EvaluatorDefinition(
|
|
618
|
+
BUSINESS_STANDARD_EVALUATOR, "response_quality", _evaluate_business_standard
|
|
619
|
+
),
|
|
454
620
|
_EvaluatorDefinition("response_contract@v1", "response_contract", _evaluate_response_contract),
|
|
455
621
|
_EvaluatorDefinition("runtime_budget@v1", "runtime_budget", _evaluate_runtime_budget),
|
|
456
622
|
_EvaluatorDefinition("tool_trajectory@v1", "tool_trajectory", _evaluate_tool_trajectory),
|