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/studio/static/index.html
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>AgentKit Studio</title>
|
|
7
|
-
<script type="module" crossorigin src="/static/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/static/assets/index-CYekR2Xv.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/static/assets/rolldown-runtime-hePW80VL.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/static/assets/react-vendor-BNmTiJ-I.js">
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/static/assets/index-
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/static/assets/index-BLpcdrgW.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
ksadk/studio/templates.py
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
from collections.abc import Iterable
|
|
6
7
|
|
|
7
8
|
import yaml # type: ignore[import-untyped]
|
|
8
9
|
|
|
9
10
|
from ksadk.studio.contracts import (
|
|
11
|
+
AgentBehaviorDesign,
|
|
10
12
|
AgentBindings,
|
|
11
13
|
AgentSpec,
|
|
12
14
|
AgentTemplateComposeRequest,
|
|
@@ -26,6 +28,87 @@ from ksadk.studio.workspace import Workspace
|
|
|
26
28
|
RESEARCH_SKILL_NAME = "deep-research-methodology"
|
|
27
29
|
RESEARCH_SKILL_VERSION = "1.0.0"
|
|
28
30
|
|
|
31
|
+
|
|
32
|
+
def _explicit_boundaries(goal: str) -> list[str]:
|
|
33
|
+
clauses = [item.strip() for item in re.split(r"[。;;\n]+", goal) if item.strip()]
|
|
34
|
+
boundary_markers = (
|
|
35
|
+
"必须",
|
|
36
|
+
"不得",
|
|
37
|
+
"不要",
|
|
38
|
+
"禁止",
|
|
39
|
+
"仅限",
|
|
40
|
+
"只能",
|
|
41
|
+
"确认",
|
|
42
|
+
"审批",
|
|
43
|
+
"敏感",
|
|
44
|
+
"隐私",
|
|
45
|
+
)
|
|
46
|
+
return [
|
|
47
|
+
clause
|
|
48
|
+
for clause in clauses
|
|
49
|
+
if any(marker in clause for marker in boundary_markers)
|
|
50
|
+
][:4]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _blank_behavior_design(goal: str, policy_template: str) -> AgentBehaviorDesign:
|
|
54
|
+
primary_goal = next(
|
|
55
|
+
(item.strip() for item in re.split(r"[。;;\n]+", goal) if item.strip()),
|
|
56
|
+
goal,
|
|
57
|
+
)
|
|
58
|
+
explicit = _explicit_boundaries(goal)
|
|
59
|
+
safety = list(explicit)
|
|
60
|
+
policy_boundary = (
|
|
61
|
+
"执行外部写入、生产变更或其他高风险操作前,说明影响并取得用户确认。"
|
|
62
|
+
if policy_template == "strict"
|
|
63
|
+
else "调用工具和外部服务时遵守当前权限策略,不宣称未实际完成的操作。"
|
|
64
|
+
)
|
|
65
|
+
if policy_boundary not in safety:
|
|
66
|
+
safety.append(policy_boundary)
|
|
67
|
+
return AgentBehaviorDesign(
|
|
68
|
+
role=f"以“{primary_goal[:80]}”为核心职责的智能助手",
|
|
69
|
+
objective=goal,
|
|
70
|
+
operating_principles=[
|
|
71
|
+
"优先理解当前目标、已知事实和成功标准,不把假设表述为事实。",
|
|
72
|
+
"信息不足或存在关键歧义时,先提出最少必要问题。",
|
|
73
|
+
"只在确有需要时调用已绑定能力,并检查工具输入与返回结果。",
|
|
74
|
+
],
|
|
75
|
+
workflow=[
|
|
76
|
+
"识别本次请求、约束条件和期望结果",
|
|
77
|
+
"检查信息是否充分并澄清关键缺口",
|
|
78
|
+
"制定并执行必要步骤,使用可用 Tool、MCP 或 Skill",
|
|
79
|
+
"核对结果、说明限制并按约定结构交付",
|
|
80
|
+
],
|
|
81
|
+
explicit_boundaries=explicit,
|
|
82
|
+
safety_boundaries=safety,
|
|
83
|
+
output_expectations=[
|
|
84
|
+
"回答准确、清晰、可执行,并区分事实、判断和建议。",
|
|
85
|
+
"涉及风险、假设或未完成事项时明确说明,不伪造执行结果。",
|
|
86
|
+
],
|
|
87
|
+
source_notes=[
|
|
88
|
+
"用户输入:核心目标和明确提出的边界",
|
|
89
|
+
"模板补充:通用执行流程、澄清原则和交付要求",
|
|
90
|
+
"平台策略:工具权限与高风险操作边界",
|
|
91
|
+
],
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _blank_system_prompt(design: AgentBehaviorDesign) -> str:
|
|
96
|
+
principles = "\n".join(f"- {item}" for item in design.operating_principles)
|
|
97
|
+
boundaries = "\n".join(f"- {item}" for item in design.safety_boundaries)
|
|
98
|
+
outputs = "\n".join(f"- {item}" for item in design.output_expectations)
|
|
99
|
+
return (
|
|
100
|
+
f"# 角色\n{design.role}\n\n"
|
|
101
|
+
f"# 核心目标\n{design.objective}\n\n"
|
|
102
|
+
f"# 工作原则\n{principles}\n\n"
|
|
103
|
+
f"# 安全与边界\n{boundaries}\n\n"
|
|
104
|
+
f"# 回答要求\n{outputs}"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _blank_task_prompt(design: AgentBehaviorDesign) -> str:
|
|
109
|
+
workflow = "\n".join(f"{index}. {item}" for index, item in enumerate(design.workflow, 1))
|
|
110
|
+
return f"每次收到请求时遵循以下执行契约:\n{workflow}"
|
|
111
|
+
|
|
29
112
|
_RESEARCH_SKILL_MANIFEST = {
|
|
30
113
|
"name": RESEARCH_SKILL_NAME,
|
|
31
114
|
"displayName": "深度调研方法",
|
|
@@ -180,7 +263,10 @@ def compose_blank_agent(
|
|
|
180
263
|
skills = [item for item in resources if item.kind == "skill"]
|
|
181
264
|
mcps = [item for item in resources if item.kind == "mcp"]
|
|
182
265
|
|
|
183
|
-
|
|
266
|
+
requested_primary_model = request.model_profile_id or next(
|
|
267
|
+
iter(request.model_profile_ids), None
|
|
268
|
+
)
|
|
269
|
+
model = _select_model(models, requested_primary_model)
|
|
184
270
|
selected_model_ids = _select_model_ids(
|
|
185
271
|
models, request.model_profile_id, request.model_profile_ids
|
|
186
272
|
)
|
|
@@ -199,12 +285,14 @@ def compose_blank_agent(
|
|
|
199
285
|
request.mcp_resource_ids,
|
|
200
286
|
kind="mcp",
|
|
201
287
|
)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
288
|
+
goal = request.goal.strip() or request.prompt.strip()
|
|
289
|
+
behavior_design = _blank_behavior_design(goal, request.policy_template)
|
|
290
|
+
# Explicit ``prompt`` is a compatibility path for callers that already own the
|
|
291
|
+
# final system instructions. Studio quick-create sends ``goal`` and therefore
|
|
292
|
+
# receives the compiled, reviewable behavior contract below.
|
|
293
|
+
system_prompt = request.prompt.strip() or _blank_system_prompt(behavior_design)
|
|
294
|
+
task_prompt = request.task_prompt.strip() or _blank_task_prompt(behavior_design)
|
|
295
|
+
description = request.description.strip() or goal[:160]
|
|
208
296
|
bindings = AgentBindings(
|
|
209
297
|
model_profile_id=model.resource_id,
|
|
210
298
|
model_profile_ids=selected_model_ids,
|
|
@@ -261,6 +349,7 @@ def compose_blank_agent(
|
|
|
261
349
|
return AgentTemplateComposition(
|
|
262
350
|
template_id="blank",
|
|
263
351
|
spec=spec,
|
|
352
|
+
behavior_design=behavior_design,
|
|
264
353
|
recommendations=recommendations,
|
|
265
354
|
warnings=[],
|
|
266
355
|
)
|
|
@@ -278,7 +367,10 @@ def compose_research_agent(
|
|
|
278
367
|
skills = [item for item in resources if item.kind == "skill"]
|
|
279
368
|
mcps = [item for item in resources if item.kind == "mcp"]
|
|
280
369
|
|
|
281
|
-
|
|
370
|
+
requested_primary_model = request.model_profile_id or next(
|
|
371
|
+
iter(request.model_profile_ids), None
|
|
372
|
+
)
|
|
373
|
+
model = _select_model(models, requested_primary_model)
|
|
282
374
|
selected_model_ids = _select_model_ids(
|
|
283
375
|
models, request.model_profile_id, request.model_profile_ids
|
|
284
376
|
)
|
ksadk/studio/validator.py
CHANGED
|
@@ -213,6 +213,25 @@ class AgentValidator:
|
|
|
213
213
|
)
|
|
214
214
|
)
|
|
215
215
|
|
|
216
|
+
# 方案 §5.2 / §6.1:ownership 必须与 runtime capability 兼容,不支持组合时报错不静默降级。
|
|
217
|
+
runtime_type = (
|
|
218
|
+
draft.spec.runtime.type if draft.spec.runtime
|
|
219
|
+
else draft.metadata.labels.get("agentkit.ksyun.com/framework", "")
|
|
220
|
+
)
|
|
221
|
+
ownership = draft.spec.context.ownership
|
|
222
|
+
try:
|
|
223
|
+
from ksadk.context_engine.capabilities import validate_ownership_for_runtime
|
|
224
|
+
validate_ownership_for_runtime(ownership, runtime_type=runtime_type)
|
|
225
|
+
except ValueError as exc:
|
|
226
|
+
diagnostics.append(
|
|
227
|
+
self._error(
|
|
228
|
+
"OWNERSHIP_CAPABILITY_MISMATCH",
|
|
229
|
+
str(exc),
|
|
230
|
+
"spec.context.ownership",
|
|
231
|
+
hint="按 Runtime capability 选择 ownership,不支持组合时 Build 会拒绝",
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
216
235
|
@staticmethod
|
|
217
236
|
def _error(
|
|
218
237
|
code: str,
|
ksadk/studio/workspace.py
CHANGED
|
@@ -15,16 +15,6 @@ class Workspace:
|
|
|
15
15
|
def __init__(self, root: Path | str) -> None:
|
|
16
16
|
self.root = Path(root).expanduser().resolve()
|
|
17
17
|
|
|
18
|
-
def matches_configured_root_path(self, path: str) -> bool:
|
|
19
|
-
"""Return whether a no-op workspace-open request names this exact root.
|
|
20
|
-
|
|
21
|
-
The local Studio daemon is intentionally bound to one workspace for its
|
|
22
|
-
lifetime. Comparing the advertised canonical string avoids resolving
|
|
23
|
-
a client-supplied pathname in the workspace-open endpoint.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
return path == os.fspath(self.root)
|
|
27
|
-
|
|
28
18
|
def initialize(self) -> None:
|
|
29
19
|
self.root.mkdir(parents=True, exist_ok=True)
|
|
30
20
|
for relative in (
|
|
@@ -67,28 +57,36 @@ class Workspace:
|
|
|
67
57
|
)
|
|
68
58
|
|
|
69
59
|
def resolve(self, relative: Path | str, *, must_exist: bool = False) -> Path:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if
|
|
91
|
-
|
|
60
|
+
raw = Path(relative)
|
|
61
|
+
# Reject lexical traversal before asking the filesystem to resolve any
|
|
62
|
+
# symlink. The real-path prefix check below also catches a path that is
|
|
63
|
+
# lexically inside the workspace but escapes through a symlink.
|
|
64
|
+
if ".." in raw.parts:
|
|
65
|
+
raise StudioError(
|
|
66
|
+
"WORKSPACE_PATH_FORBIDDEN",
|
|
67
|
+
"路径不在当前工作区内",
|
|
68
|
+
status_code=403,
|
|
69
|
+
details={"path": str(relative)},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
root_text = os.path.realpath(os.fspath(self.root))
|
|
73
|
+
candidate_input = raw if raw.is_absolute() else self.root / raw
|
|
74
|
+
candidate_text = os.path.realpath(os.fspath(candidate_input))
|
|
75
|
+
if candidate_text == root_text:
|
|
76
|
+
if must_exist and not self.root.exists():
|
|
77
|
+
raise FileNotFoundError(self.root)
|
|
78
|
+
return self.root
|
|
79
|
+
root_prefix = root_text.rstrip(os.sep) + os.sep
|
|
80
|
+
if not candidate_text.startswith(root_prefix):
|
|
81
|
+
raise StudioError(
|
|
82
|
+
"WORKSPACE_PATH_FORBIDDEN",
|
|
83
|
+
"路径不在当前工作区内",
|
|
84
|
+
status_code=403,
|
|
85
|
+
details={"path": str(relative)},
|
|
86
|
+
)
|
|
87
|
+
candidate = Path(candidate_text)
|
|
88
|
+
if must_exist and not candidate.exists():
|
|
89
|
+
raise FileNotFoundError(candidate)
|
|
92
90
|
return candidate
|
|
93
91
|
|
|
94
92
|
def relative(self, path: Path | str) -> str:
|
ksadk/tracing/setup.py
CHANGED
|
@@ -2,25 +2,48 @@
|
|
|
2
2
|
Tracing 初始化 - 标准 OTLP 双写(Langfuse 主 + CloudMonitor 次)
|
|
3
3
|
|
|
4
4
|
每个 Python Agent 进程一个 TracerProvider,挂两个 OTLP/HTTP protobuf BatchSpanProcessor,
|
|
5
|
-
消费同一批 ReadableSpan
|
|
5
|
+
消费同一批 ReadableSpan。仅为 CloudMonitor 克隆并补充兼容属性,不修改原始 span。
|
|
6
6
|
|
|
7
7
|
支持 ADK 自动插桩 via openinference-instrumentation-google-adk
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import atexit
|
|
11
11
|
import importlib
|
|
12
|
+
import json
|
|
12
13
|
import logging
|
|
13
14
|
import os
|
|
15
|
+
from collections.abc import Callable, Sequence
|
|
14
16
|
from dataclasses import dataclass
|
|
15
17
|
from typing import Any, Optional
|
|
16
18
|
from urllib.parse import unquote
|
|
17
19
|
|
|
20
|
+
from opentelemetry.sdk.resources import Resource
|
|
21
|
+
from opentelemetry.sdk.trace import ReadableSpan
|
|
18
22
|
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
|
|
19
23
|
|
|
20
24
|
from ksadk.tracing.exporters.inmemory_exporter import InMemoryExporter
|
|
21
25
|
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
28
|
+
_CLOUD_MONITOR_RESOURCE_SPAN_ATTRIBUTES = (
|
|
29
|
+
"agentengine.account_id",
|
|
30
|
+
"agentengine.agent_id",
|
|
31
|
+
"agentengine.framework",
|
|
32
|
+
"agentengine.langfuse_project_id",
|
|
33
|
+
"agentengine.region",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
_LANGFUSE_OBSERVATION_TYPES = {
|
|
37
|
+
"LLM": "generation",
|
|
38
|
+
"EMBEDDING": "embedding",
|
|
39
|
+
"CHAIN": "chain",
|
|
40
|
+
"AGENT": "agent",
|
|
41
|
+
"TOOL": "tool",
|
|
42
|
+
"RETRIEVER": "retriever",
|
|
43
|
+
"EVALUATOR": "evaluator",
|
|
44
|
+
"GUARDRAIL": "guardrail",
|
|
45
|
+
}
|
|
46
|
+
|
|
24
47
|
|
|
25
48
|
def _batch_span_processor_kwargs() -> dict:
|
|
26
49
|
"""BatchSpanProcessor 参数:限制单次 export 体积,避免 OTLP collector 413。
|
|
@@ -66,14 +89,16 @@ class _LoggingSpanExporter(SpanExporter):
|
|
|
66
89
|
endpoint: str,
|
|
67
90
|
service_name: str,
|
|
68
91
|
header_keys: list[str],
|
|
92
|
+
span_transform: Callable[[Sequence[ReadableSpan]], Sequence[ReadableSpan]] | None = None,
|
|
69
93
|
):
|
|
70
94
|
self._exporter = exporter
|
|
71
95
|
self._name = name
|
|
72
96
|
self._endpoint = endpoint
|
|
73
97
|
self._service_name = service_name
|
|
74
98
|
self._header_keys = header_keys
|
|
99
|
+
self._span_transform = span_transform
|
|
75
100
|
|
|
76
|
-
def export(self, spans) -> SpanExportResult:
|
|
101
|
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
77
102
|
span_count = len(spans)
|
|
78
103
|
logger.info(
|
|
79
104
|
"%s export started: endpoint=%s service_name=%s spans=%s headers=%s",
|
|
@@ -84,7 +109,8 @@ class _LoggingSpanExporter(SpanExporter):
|
|
|
84
109
|
",".join(self._header_keys),
|
|
85
110
|
)
|
|
86
111
|
try:
|
|
87
|
-
|
|
112
|
+
export_spans = self._span_transform(spans) if self._span_transform else spans
|
|
113
|
+
result = self._exporter.export(export_spans)
|
|
88
114
|
except Exception:
|
|
89
115
|
logger.exception(
|
|
90
116
|
"%s export failed: endpoint=%s service_name=%s spans=%s",
|
|
@@ -118,6 +144,280 @@ class _LoggingSpanExporter(SpanExporter):
|
|
|
118
144
|
return self._exporter.force_flush(timeout_millis)
|
|
119
145
|
|
|
120
146
|
|
|
147
|
+
def _coerce_int(value: Any) -> int:
|
|
148
|
+
if value is None or isinstance(value, bool):
|
|
149
|
+
return 0
|
|
150
|
+
try:
|
|
151
|
+
return int(value)
|
|
152
|
+
except (TypeError, ValueError):
|
|
153
|
+
try:
|
|
154
|
+
return int(float(value))
|
|
155
|
+
except (TypeError, ValueError):
|
|
156
|
+
return 0
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _span_context(span: Any) -> Any:
|
|
160
|
+
context = getattr(span, "context", None)
|
|
161
|
+
if context is not None:
|
|
162
|
+
return context
|
|
163
|
+
get_context = getattr(span, "get_span_context", None)
|
|
164
|
+
return get_context() if callable(get_context) else None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _span_id(span: Any) -> Optional[int]:
|
|
168
|
+
return getattr(_span_context(span), "span_id", None)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _trace_id(span: Any) -> Optional[int]:
|
|
172
|
+
return getattr(_span_context(span), "trace_id", None)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _parent_span_id(span: Any) -> Optional[int]:
|
|
176
|
+
return getattr(getattr(span, "parent", None), "span_id", None)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _span_token_usage(span: Any) -> dict[str, int]:
|
|
180
|
+
attributes = getattr(span, "attributes", None) or {}
|
|
181
|
+
return _token_usage(attributes)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _token_usage(attributes: dict[str, Any]) -> dict[str, int]:
|
|
185
|
+
input_tokens = _coerce_int(
|
|
186
|
+
attributes.get("gen_ai.usage.input_tokens")
|
|
187
|
+
or attributes.get("llm.usage.prompt_tokens")
|
|
188
|
+
or attributes.get("llm.token_count.prompt")
|
|
189
|
+
)
|
|
190
|
+
output_tokens = _coerce_int(
|
|
191
|
+
attributes.get("gen_ai.usage.output_tokens")
|
|
192
|
+
or attributes.get("llm.usage.completion_tokens")
|
|
193
|
+
or attributes.get("llm.token_count.completion")
|
|
194
|
+
)
|
|
195
|
+
total_tokens = _coerce_int(
|
|
196
|
+
attributes.get("gen_ai.usage.total_tokens")
|
|
197
|
+
or attributes.get("llm.usage.total_tokens")
|
|
198
|
+
or attributes.get("llm.token_count.total")
|
|
199
|
+
)
|
|
200
|
+
return {
|
|
201
|
+
"input": input_tokens,
|
|
202
|
+
"output": output_tokens,
|
|
203
|
+
"total": total_tokens or input_tokens + output_tokens,
|
|
204
|
+
"cache_read": _coerce_int(
|
|
205
|
+
attributes.get("gen_ai.usage.cache_read.input_tokens")
|
|
206
|
+
or attributes.get("llm.usage.cache_read.input_tokens")
|
|
207
|
+
),
|
|
208
|
+
"reasoning_output": _coerce_int(
|
|
209
|
+
attributes.get("gen_ai.usage.reasoning.output_tokens")
|
|
210
|
+
or attributes.get("llm.usage.reasoning_tokens")
|
|
211
|
+
),
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _first_span_attribute(attributes: dict[str, Any], *keys: str) -> Any:
|
|
216
|
+
for key in keys:
|
|
217
|
+
value = attributes.get(key)
|
|
218
|
+
if value not in (None, ""):
|
|
219
|
+
return value
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _add_langfuse_compatibility_attributes(attributes: dict[str, Any]) -> None:
|
|
224
|
+
usage = _token_usage(attributes)
|
|
225
|
+
observation_kind = str(attributes.get("openinference.span.kind") or "").upper()
|
|
226
|
+
observation_type = _LANGFUSE_OBSERVATION_TYPES.get(observation_kind)
|
|
227
|
+
if observation_type is None:
|
|
228
|
+
observation_type = "generation" if _has_nonzero_token_usage(usage) else "span"
|
|
229
|
+
attributes.setdefault("langfuse.observation.type", observation_type)
|
|
230
|
+
|
|
231
|
+
provider = _first_span_attribute(
|
|
232
|
+
attributes,
|
|
233
|
+
"gen_ai.provider.name",
|
|
234
|
+
"gen_ai.system",
|
|
235
|
+
"llm.provider",
|
|
236
|
+
)
|
|
237
|
+
if provider is not None:
|
|
238
|
+
attributes.setdefault("langfuse.observation.metadata.ls_provider", provider)
|
|
239
|
+
|
|
240
|
+
model = _first_span_attribute(
|
|
241
|
+
attributes,
|
|
242
|
+
"gen_ai.request.model",
|
|
243
|
+
"llm.model_name",
|
|
244
|
+
)
|
|
245
|
+
if model is not None:
|
|
246
|
+
attributes.setdefault("langfuse.observation.model.name", model)
|
|
247
|
+
attributes.setdefault("langfuse.observation.metadata.ls_model_name", model)
|
|
248
|
+
|
|
249
|
+
if _has_nonzero_token_usage(usage):
|
|
250
|
+
attributes.setdefault("gen_ai.usage.total_tokens", usage["total"])
|
|
251
|
+
usage_details = {
|
|
252
|
+
"input": usage["input"],
|
|
253
|
+
"output": usage["output"],
|
|
254
|
+
"total": usage["total"],
|
|
255
|
+
"input_cache_read": usage["cache_read"],
|
|
256
|
+
}
|
|
257
|
+
attributes["langfuse.observation.usage_details"] = json.dumps(
|
|
258
|
+
usage_details, separators=(",", ":"), sort_keys=True
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _has_nonzero_token_usage(usage: dict[str, int]) -> bool:
|
|
263
|
+
return any(value > 0 for value in usage.values())
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _clone_cloud_monitor_resource(resource: Resource | None) -> Resource | None:
|
|
267
|
+
if resource is None:
|
|
268
|
+
return None
|
|
269
|
+
attributes = dict(resource.attributes)
|
|
270
|
+
agent_id = str(attributes.get("agentengine.agent_id") or "").strip()
|
|
271
|
+
if not agent_id or attributes.get("service.name") == agent_id:
|
|
272
|
+
return resource
|
|
273
|
+
attributes["service.name"] = agent_id
|
|
274
|
+
return Resource(attributes, schema_url=resource.schema_url)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _clone_span(
|
|
278
|
+
span: ReadableSpan,
|
|
279
|
+
*,
|
|
280
|
+
attributes: dict[str, Any],
|
|
281
|
+
resource: Resource | None,
|
|
282
|
+
) -> ReadableSpan:
|
|
283
|
+
return ReadableSpan(
|
|
284
|
+
name=span.name,
|
|
285
|
+
context=span.context,
|
|
286
|
+
parent=span.parent,
|
|
287
|
+
resource=resource,
|
|
288
|
+
attributes=attributes,
|
|
289
|
+
events=span.events,
|
|
290
|
+
links=span.links,
|
|
291
|
+
kind=span.kind,
|
|
292
|
+
status=span.status,
|
|
293
|
+
start_time=span.start_time,
|
|
294
|
+
end_time=span.end_time,
|
|
295
|
+
instrumentation_scope=getattr(span, "instrumentation_scope", None),
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _prepare_cloud_monitor_spans(
|
|
300
|
+
spans: Sequence[ReadableSpan],
|
|
301
|
+
) -> Sequence[ReadableSpan]:
|
|
302
|
+
"""Clone CloudMonitor spans with resource identity and root usage compatibility."""
|
|
303
|
+
if not spans:
|
|
304
|
+
return spans
|
|
305
|
+
span_list = list(spans)
|
|
306
|
+
session_ids_by_trace: dict[Optional[int], Any] = {}
|
|
307
|
+
for span in span_list:
|
|
308
|
+
attributes = dict(getattr(span, "attributes", None) or {})
|
|
309
|
+
session_id = _first_span_attribute(
|
|
310
|
+
attributes,
|
|
311
|
+
"session.id",
|
|
312
|
+
"langfuse.session.id",
|
|
313
|
+
"ksadk.session_id",
|
|
314
|
+
)
|
|
315
|
+
if session_id is not None:
|
|
316
|
+
session_ids_by_trace.setdefault(_trace_id(span), session_id)
|
|
317
|
+
span_keys = {
|
|
318
|
+
(_trace_id(span), _span_id(span)) for span in span_list if _span_id(span) is not None
|
|
319
|
+
}
|
|
320
|
+
children_by_parent: dict[tuple[Optional[int], int], list[ReadableSpan]] = {}
|
|
321
|
+
roots: list[ReadableSpan] = []
|
|
322
|
+
for span in span_list:
|
|
323
|
+
span_id = _span_id(span)
|
|
324
|
+
if span_id is None:
|
|
325
|
+
continue
|
|
326
|
+
trace_id = _trace_id(span)
|
|
327
|
+
parent_id = _parent_span_id(span)
|
|
328
|
+
if parent_id is None or (trace_id, parent_id) not in span_keys:
|
|
329
|
+
roots.append(span)
|
|
330
|
+
else:
|
|
331
|
+
children_by_parent.setdefault((trace_id, parent_id), []).append(span)
|
|
332
|
+
|
|
333
|
+
usage_by_span = {
|
|
334
|
+
(_trace_id(span), _span_id(span)): _span_token_usage(span)
|
|
335
|
+
for span in span_list
|
|
336
|
+
if _span_id(span) is not None
|
|
337
|
+
}
|
|
338
|
+
has_token_descendant_cache: dict[tuple[Optional[int], int], bool] = {}
|
|
339
|
+
|
|
340
|
+
def has_token_descendant(span: ReadableSpan) -> bool:
|
|
341
|
+
key = (_trace_id(span), _span_id(span))
|
|
342
|
+
if key[1] is None:
|
|
343
|
+
return False
|
|
344
|
+
if key in has_token_descendant_cache:
|
|
345
|
+
return has_token_descendant_cache[key]
|
|
346
|
+
result = any(
|
|
347
|
+
_has_nonzero_token_usage(usage_by_span.get((_trace_id(child), _span_id(child)), {}))
|
|
348
|
+
or has_token_descendant(child)
|
|
349
|
+
for child in children_by_parent.get(key, [])
|
|
350
|
+
)
|
|
351
|
+
has_token_descendant_cache[key] = result
|
|
352
|
+
return result
|
|
353
|
+
|
|
354
|
+
def collect_leaf_usage(span: ReadableSpan, aggregate: dict[str, int]) -> None:
|
|
355
|
+
key = (_trace_id(span), _span_id(span))
|
|
356
|
+
own_usage = usage_by_span.get(key, {})
|
|
357
|
+
if _has_nonzero_token_usage(own_usage) and not has_token_descendant(span):
|
|
358
|
+
for usage_key, value in own_usage.items():
|
|
359
|
+
aggregate[usage_key] += value
|
|
360
|
+
return
|
|
361
|
+
for child in children_by_parent.get(key, []):
|
|
362
|
+
collect_leaf_usage(child, aggregate)
|
|
363
|
+
|
|
364
|
+
root_usage: dict[int, dict[str, int]] = {}
|
|
365
|
+
for root in roots:
|
|
366
|
+
aggregate = {
|
|
367
|
+
"input": 0,
|
|
368
|
+
"output": 0,
|
|
369
|
+
"total": 0,
|
|
370
|
+
"cache_read": 0,
|
|
371
|
+
"reasoning_output": 0,
|
|
372
|
+
}
|
|
373
|
+
collect_leaf_usage(root, aggregate)
|
|
374
|
+
root_usage[id(root)] = aggregate
|
|
375
|
+
|
|
376
|
+
resource_cache: dict[int, Resource | None] = {}
|
|
377
|
+
transformed: list[ReadableSpan] = []
|
|
378
|
+
usage_attributes = {
|
|
379
|
+
"gen_ai.usage.input_tokens": "input",
|
|
380
|
+
"gen_ai.usage.output_tokens": "output",
|
|
381
|
+
"gen_ai.usage.total_tokens": "total",
|
|
382
|
+
"gen_ai.usage.cache_read.input_tokens": "cache_read",
|
|
383
|
+
"gen_ai.usage.reasoning.output_tokens": "reasoning_output",
|
|
384
|
+
}
|
|
385
|
+
for span in span_list:
|
|
386
|
+
original_resource = getattr(span, "resource", None)
|
|
387
|
+
original_resource_attributes = getattr(original_resource, "attributes", {}) or {}
|
|
388
|
+
resource_key = id(original_resource)
|
|
389
|
+
if resource_key not in resource_cache:
|
|
390
|
+
resource_cache[resource_key] = _clone_cloud_monitor_resource(original_resource)
|
|
391
|
+
resource = resource_cache[resource_key]
|
|
392
|
+
attributes = dict(getattr(span, "attributes", None) or {})
|
|
393
|
+
resource_attributes = getattr(resource, "attributes", {}) or {}
|
|
394
|
+
for key in _CLOUD_MONITOR_RESOURCE_SPAN_ATTRIBUTES:
|
|
395
|
+
value = resource_attributes.get(key)
|
|
396
|
+
if value not in (None, ""):
|
|
397
|
+
attributes.setdefault(f"gen_ai.{key}", value)
|
|
398
|
+
agent_name = original_resource_attributes.get(
|
|
399
|
+
"agentengine.agent_name"
|
|
400
|
+
) or original_resource_attributes.get("service.name")
|
|
401
|
+
if agent_name not in (None, ""):
|
|
402
|
+
attributes.setdefault("gen_ai.agentengine.agent_name", agent_name)
|
|
403
|
+
aggregate = root_usage.get(id(span), {})
|
|
404
|
+
for attribute, usage_key in usage_attributes.items():
|
|
405
|
+
if _coerce_int(attributes.get(attribute)) <= 0 and aggregate.get(usage_key, 0) > 0:
|
|
406
|
+
attributes[attribute] = aggregate[usage_key]
|
|
407
|
+
|
|
408
|
+
session_id = session_ids_by_trace.get(_trace_id(span))
|
|
409
|
+
if session_id is not None:
|
|
410
|
+
attributes.setdefault("session.id", session_id)
|
|
411
|
+
_add_langfuse_compatibility_attributes(attributes)
|
|
412
|
+
|
|
413
|
+
original_attributes = dict(getattr(span, "attributes", None) or {})
|
|
414
|
+
if resource is original_resource and attributes == original_attributes:
|
|
415
|
+
transformed.append(span)
|
|
416
|
+
else:
|
|
417
|
+
transformed.append(_clone_span(span, attributes=attributes, resource=resource))
|
|
418
|
+
return transformed
|
|
419
|
+
|
|
420
|
+
|
|
121
421
|
def _parse_otlp_headers(raw: str) -> dict[str, str]:
|
|
122
422
|
"""Parse OTEL_EXPORTER_OTLP_HEADERS into an HTTP headers dict."""
|
|
123
423
|
headers: dict[str, str] = {}
|
|
@@ -409,6 +709,7 @@ def setup_tracing(
|
|
|
409
709
|
endpoint=cloud_monitor_config.endpoint,
|
|
410
710
|
service_name=cloud_monitor_config.service_name,
|
|
411
711
|
header_keys=sorted(cloud_monitor_config.headers),
|
|
712
|
+
span_transform=_prepare_cloud_monitor_spans,
|
|
412
713
|
)
|
|
413
714
|
_register_span_processor(
|
|
414
715
|
provider,
|
ksadk/version.py
CHANGED