agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__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.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
- agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +180 -11
- ksadk/a2a/bootstrap.py +332 -0
- ksadk/a2a/card.py +15 -0
- ksadk/a2a/context_store.py +203 -0
- ksadk/a2a/control_plane.py +1056 -0
- ksadk/a2a/credential.py +99 -0
- ksadk/a2a/event_adapter.py +149 -0
- ksadk/a2a/executor.py +361 -137
- ksadk/a2a/external_transport.py +516 -0
- ksadk/a2a/identity.py +304 -0
- ksadk/a2a/ids.py +22 -0
- ksadk/a2a/langgraph.py +285 -0
- ksadk/a2a/resume_store.py +201 -0
- ksadk/a2a/routes.py +112 -0
- ksadk/a2a/server.py +102 -62
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +1122 -0
- ksadk/a2a/task_adapter.py +423 -0
- ksadk/a2a/task_event_dispatcher.py +167 -0
- ksadk/a2a/task_event_outbox.py +326 -0
- ksadk/a2a/task_store.py +206 -0
- ksadk/a2a_card.py +101 -0
- ksadk/a2ui/__init__.py +27 -0
- ksadk/a2ui/core.py +186 -0
- ksadk/a2ui/models.py +166 -0
- ksadk/a2ui/renderer.py +198 -0
- ksadk/agui/__init__.py +9 -0
- ksadk/agui/a2ui_projection.py +92 -0
- ksadk/agui/agent.py +872 -0
- ksadk/agui/config.py +92 -0
- ksadk/agui/routes.py +47 -0
- ksadk/api/client.py +238 -204
- ksadk/api/environment.py +27 -0
- ksadk/builders/__init__.py +2 -0
- ksadk/builders/base.py +16 -14
- ksadk/builders/code_builder.py +375 -270
- ksadk/builders/container_builder.py +240 -200
- ksadk/builders/framework_requirements.py +31 -12
- ksadk/builders/ks3_uploader.py +60 -29
- ksadk/builders/managed_runtime_builder.py +173 -0
- ksadk/builders/mcp_builder.py +91 -92
- ksadk/builders/requirements_utils.py +1 -5
- ksadk/cli/__init__.py +97 -171
- ksadk/cli/cmd_a2a.py +255 -84
- ksadk/cli/cmd_agent.py +19 -5
- ksadk/cli/cmd_build.py +158 -29
- ksadk/cli/cmd_completion.py +47 -35
- ksadk/cli/cmd_config.py +267 -198
- ksadk/cli/cmd_create.py +507 -740
- ksadk/cli/cmd_dashboard.py +120 -45
- ksadk/cli/cmd_deploy.py +135 -59
- ksadk/cli/cmd_destroy.py +44 -14
- ksadk/cli/cmd_eval.py +386 -0
- ksadk/cli/cmd_files.py +63 -24
- ksadk/cli/cmd_hermes.py +313 -135
- ksadk/cli/cmd_invoke.py +184 -122
- ksadk/cli/cmd_launch.py +76 -28
- ksadk/cli/cmd_mcp.py +98 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +564 -268
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +58 -73
- ksadk/cli/cmd_status.py +80 -31
- ksadk/cli/cmd_studio.py +110 -0
- ksadk/cli/cmd_version.py +20 -13
- ksadk/cli/cmd_web.py +95 -18
- ksadk/cli/deploy_utils.py +20 -20
- ksadk/cli/dry_run.py +7 -4
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/error_utils.py +37 -12
- ksadk/cli/global_options.py +4 -3
- ksadk/cli/local_runtime.py +1 -2
- ksadk/cli/model_catalog.py +6 -2
- ksadk/cli/network_options.py +41 -19
- ksadk/cli/resource_common.py +6 -2
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/storage.py +0 -1
- ksadk/cli/ui.py +11 -3
- ksadk/cli/workflow_common.py +52 -18
- ksadk/codex/__init__.py +12 -0
- ksadk/codex/client.py +1042 -0
- ksadk/codex/phase.py +143 -0
- ksadk/codex/runtime.py +940 -0
- ksadk/common/aicp_env.py +0 -1
- ksadk/common/auth.py +2 -2
- ksadk/common/constants.py +1 -3
- ksadk/common/kop_client.py +232 -0
- ksadk/common/llm_utils.py +17 -13
- ksadk/compat/__init__.py +5 -0
- ksadk/compat/adk_compat.py +200 -0
- ksadk/compat/copilotkit_a2ui.py +330 -0
- ksadk/configs/__init__.py +12 -14
- ksadk/configs/env_registry.py +661 -116
- ksadk/configs/global_config.py +3 -2
- ksadk/configs/settings.py +133 -209
- ksadk/conversations/__init__.py +85 -92
- ksadk/conversations/attachment_storage.py +16 -11
- ksadk/conversations/attachments.py +44 -36
- ksadk/conversations/compaction_pipeline.py +12 -12
- ksadk/conversations/compaction_prompt.py +9 -5
- ksadk/conversations/context.py +221 -6
- ksadk/conversations/message_projection.py +438 -51
- ksadk/conversations/model_context.py +12 -6
- ksadk/conversations/model_options.py +0 -1
- ksadk/conversations/normalize.py +20 -16
- ksadk/conversations/reasoning_markup.py +1 -1
- ksadk/conversations/runtime.py +339 -5255
- ksadk/conversations/runtime_compaction.py +333 -0
- ksadk/conversations/runtime_constants.py +27 -0
- ksadk/conversations/runtime_governance.py +185 -0
- ksadk/conversations/runtime_input.py +660 -0
- ksadk/conversations/runtime_invocation.py +395 -0
- ksadk/conversations/runtime_metadata.py +475 -0
- ksadk/conversations/runtime_observability.py +367 -0
- ksadk/conversations/runtime_payloads.py +272 -0
- ksadk/conversations/runtime_persistence.py +440 -0
- ksadk/conversations/runtime_preparation.py +426 -0
- ksadk/conversations/runtime_resume.py +836 -0
- ksadk/conversations/runtime_stream_events.py +902 -0
- ksadk/conversations/runtime_streaming.py +700 -0
- ksadk/conversations/semantic_summary.py +17 -5
- ksadk/conversations/session_title.py +6 -3
- ksadk/deployment/__init__.py +15 -16
- ksadk/deployment/agent_access.py +12 -23
- ksadk/deployment/base.py +51 -38
- ksadk/deployment/managed_runtime.py +33 -0
- ksadk/deployment/manager.py +85 -71
- ksadk/deployment/providers/serverless.py +379 -210
- ksadk/deployment/registry.py +38 -32
- ksadk/deployment/state.py +11 -11
- ksadk/deployment/ui_config.py +1 -1
- ksadk/detection/__init__.py +1 -1
- ksadk/detection/detector.py +146 -83
- ksadk/detection/mcp_detector.py +9 -6
- ksadk/evaluation/__init__.py +84 -0
- ksadk/evaluation/a2a_adapter.py +444 -0
- ksadk/evaluation/adapters.py +54 -0
- ksadk/evaluation/contracts.py +409 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +461 -0
- ksadk/evaluation/executor.py +118 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/target.py +73 -0
- ksadk/events/__init__.py +25 -0
- ksadk/events/parser.py +191 -0
- ksadk/events/replay.py +37 -0
- ksadk/events/runtime_event.py +273 -0
- ksadk/events/store.py +317 -0
- ksadk/harness/__init__.py +35 -0
- ksadk/harness/app.py +269 -0
- ksadk/harness/config.py +181 -0
- ksadk/harness/reasoner.py +105 -0
- ksadk/harness/runtime.py +364 -0
- ksadk/harness/sandbox.py +141 -0
- ksadk/harness/tools.py +222 -0
- ksadk/hermes_terminal.py +28 -12
- ksadk/identity/resolver.py +15 -9
- ksadk/ids.py +32 -0
- ksadk/knowledge_base/adk_tool.py +1 -1
- ksadk/knowledge_base/client.py +38 -33
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +195 -0
- ksadk/markdown.py +5 -2
- ksadk/mcp_runtime/__init__.py +9 -6
- ksadk/memory/__init__.py +1 -1
- ksadk/memory/adk/__init__.py +2 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
- ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
- ksadk/memory/adk/backends/sdk_ltm_backend.py +36 -36
- ksadk/memory/adk/long_term_memory.py +34 -37
- ksadk/memory/adk/resilient_session_service.py +4 -5
- ksadk/memory/adk/short_term_memory.py +12 -21
- ksadk/memory/adk_tool.py +3 -2
- ksadk/memory/backends/base.py +19 -19
- ksadk/memory/backends/memory.py +24 -25
- ksadk/memory/backends/redis.py +31 -30
- ksadk/memory/langchain_tool.py +2 -1
- ksadk/memory/manager.py +52 -47
- ksadk/memory/service.py +16 -6
- ksadk/memory/tool.py +4 -0
- ksadk/model_policy.py +15 -7
- ksadk/model_proxy/__init__.py +51 -0
- ksadk/model_proxy/bootstrap.py +80 -0
- ksadk/model_proxy/cache.py +165 -0
- ksadk/model_proxy/config.py +66 -0
- ksadk/model_proxy/demo_webui.py +115 -0
- ksadk/model_proxy/detect.py +145 -0
- ksadk/model_proxy/gate.py +68 -0
- ksadk/model_proxy/namespace.py +201 -0
- ksadk/model_proxy/protocol_proxy.py +22 -0
- ksadk/model_proxy/server.py +443 -0
- ksadk/model_proxy/transform.py +735 -0
- ksadk/openclaw_gateway.py +29 -14
- ksadk/runners/adk_runner.py +354 -146
- ksadk/runners/base_runner.py +71 -29
- ksadk/runners/deepagents_runner.py +0 -1
- ksadk/runners/factory.py +18 -5
- ksadk/runners/langchain_runner.py +29 -742
- ksadk/runners/langgraph_runner.py +417 -97
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +134 -33
- ksadk/runners/usage_accumulator.py +1 -0
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +23 -7
- ksadk/runtime/__init__.py +67 -0
- ksadk/runtime/adapter.py +412 -0
- ksadk/runtime/conversation_execution.py +553 -0
- ksadk/runtime/executor.py +251 -0
- ksadk/runtime/factory.py +156 -0
- ksadk/runtime/framework_adapters.py +118 -0
- ksadk/runtime/launch.py +48 -0
- ksadk/runtime/preprocessing.py +131 -0
- ksadk/runtime/runner_adapter.py +1032 -0
- ksadk/runtime/runner_loading.py +36 -0
- ksadk/runtime_context.py +18 -3
- ksadk/runtime_state.py +0 -1
- ksadk/sandbox/__init__.py +4 -1
- ksadk/sandbox/backends/__init__.py +4 -1
- ksadk/sandbox/backends/e2b.py +13 -5
- ksadk/sandbox/backends/local_process.py +11 -3
- ksadk/sandbox/base.py +10 -15
- ksadk/sandbox/factory.py +14 -4
- ksadk/sandbox/registry.py +57 -6
- ksadk/server/__init__.py +39 -4
- ksadk/server/api_models.py +15 -5
- ksadk/server/app.py +24 -3928
- ksadk/server/composition.py +47 -0
- ksadk/server/factory.py +622 -0
- ksadk/server/routes/__init__.py +1 -0
- ksadk/server/routes/checkpoint_resolution.py +116 -0
- ksadk/server/routes/common.py +682 -0
- ksadk/server/routes/control.py +331 -0
- ksadk/server/routes/dependencies.py +98 -0
- ksadk/server/routes/misc.py +425 -0
- ksadk/server/routes/models.py +451 -0
- ksadk/server/routes/openai_compat.py +229 -0
- ksadk/server/routes/projection.py +848 -0
- ksadk/server/routes/routers.py +52 -0
- ksadk/server/routes/run.py +519 -0
- ksadk/server/routes/sessions.py +644 -0
- ksadk/server/routes/streaming.py +265 -0
- ksadk/server/routes/workspace.py +414 -0
- ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
- ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
- ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
- ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
- ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
- ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
- ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
- ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
- ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
- ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
- ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
- ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
- ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
- ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
- ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
- ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
- ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
- ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
- ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
- ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
- ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
- ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
- ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
- ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
- ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
- ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
- ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
- ksadk/server/static/assets/index-R52pJJvg.js +177 -0
- ksadk/server/static/assets/index-jJr3_955.css +2 -0
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
- ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
- ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/server/terminal_sessions.py +111 -58
- ksadk/sessions/__init__.py +69 -19
- ksadk/sessions/base.py +31 -11
- ksadk/sessions/continuity.py +2 -3
- ksadk/sessions/in_memory.py +46 -2
- ksadk/sessions/local_service.py +148 -17
- ksadk/sessions/postgres_service.py +143 -25
- ksadk/sessions/resilient.py +36 -8
- ksadk/skills/models.py +17 -5
- ksadk/skills/package_store.py +5 -2
- ksadk/skills/runtime/agent.py +20 -5
- ksadk/skills/runtime/artifacts.py +1 -5
- ksadk/skills/runtime/backends/e2b.py +10 -4
- ksadk/skills/runtime/backends/local.py +15 -3
- ksadk/skills/runtime/base.py +1 -2
- ksadk/skills/runtime/executor.py +10 -3
- ksadk/skills/runtime/factory.py +4 -2
- ksadk/skills/runtime/loader.py +1 -2
- ksadk/skills/runtime/registry.py +15 -10
- ksadk/skills/runtime/request.py +3 -1
- ksadk/skills/service_client.py +47 -16
- ksadk/skills/tool_defs.py +15 -10
- ksadk/studio/__init__.py +5 -0
- ksadk/studio/agent_avatar_assets.py +120 -0
- ksadk/studio/agent_lifecycle.py +51 -0
- ksadk/studio/api.py +1046 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +177 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/authoring.py +524 -0
- ksadk/studio/authoring_coordinator.py +285 -0
- ksadk/studio/builder.py +320 -0
- ksadk/studio/capabilities.py +351 -0
- ksadk/studio/cloud.py +278 -0
- ksadk/studio/codex_agent_service.py +583 -0
- ksadk/studio/codex_builder.py +324 -0
- ksadk/studio/codex_manifest.py +278 -0
- ksadk/studio/codex_run.py +352 -0
- ksadk/studio/compiler.py +189 -0
- ksadk/studio/contracts.py +644 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/evaluation.py +192 -0
- ksadk/studio/event_store.py +284 -0
- ksadk/studio/framework_run.py +114 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +543 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +243 -0
- ksadk/studio/otel_trace.py +754 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +306 -0
- ksadk/studio/resource_catalog.py +1217 -0
- ksadk/studio/run_service.py +707 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +339 -0
- ksadk/studio/service.py +1141 -0
- ksadk/studio/shared_web.py +994 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
- ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
- ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
- ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- ksadk/studio/static/index.html +15 -0
- ksadk/studio/templates.py +630 -0
- ksadk/studio/validator.py +246 -0
- ksadk/studio/workspace.py +137 -0
- ksadk/terminal_exec_policy.py +3 -4
- ksadk/tools/gateway.py +101 -15
- ksadk/tools/result_budget.py +12 -4
- ksadk/toolsets/__init__.py +161 -37
- ksadk/toolsets/a2a.py +426 -0
- ksadk/toolsets/platform.py +21 -5
- ksadk/toolsets/sandbox.py +82 -16
- ksadk/toolsets/skills.py +200 -39
- ksadk/toolsets/web.py +123 -26
- ksadk/toolsets/workspace.py +212 -101
- ksadk/tracing/__init__.py +19 -21
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/setup.py +135 -506
- ksadk/tracing/span_utils.py +0 -41
- ksadk/tui/loop.py +78 -30
- ksadk/tui/stream_render.py +1 -0
- ksadk/ui_config.py +5 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/runners/utils/langfuse.py +0 -230
- ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
- ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
- ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
- ksadk/server/static/assets/channel-CTuydARX.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
- ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
- ksadk/server/static/assets/index-DAURbCsP.css +0 -1
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
- ksadk/tracing/exporters/langfuse_exporter.py +0 -445
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/top_level.txt +0 -0
ksadk/a2a/identity.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""Trusted Gateway identity boundary for inbound A2A protocol requests."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, Awaitable, Callable
|
|
8
|
+
|
|
9
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
10
|
+
from starlette.requests import Request
|
|
11
|
+
from starlette.responses import JSONResponse
|
|
12
|
+
|
|
13
|
+
from ksadk.a2a.context_store import A2AContextIdentity
|
|
14
|
+
from ksadk.a2a.ids import require_a2a_resource_id
|
|
15
|
+
|
|
16
|
+
_CALLER_PRINCIPAL_TYPES = frozenset({"user", "runtime", "service"})
|
|
17
|
+
_AUTHN_MODE_BY_CALLER_TYPE = {
|
|
18
|
+
"user": "api_key",
|
|
19
|
+
"runtime": "workload_permit",
|
|
20
|
+
"service": "gateway_service",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class A2AIngressTargetBinding:
|
|
26
|
+
"""The Gateway-verified destination to which one Runtime is bound."""
|
|
27
|
+
|
|
28
|
+
account_id: str
|
|
29
|
+
tenant_id: str
|
|
30
|
+
agent_id: str
|
|
31
|
+
runtime_id: str
|
|
32
|
+
a2a_agent_id: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class A2AIngressIdentity:
|
|
37
|
+
"""Caller identity verified by Gateway mTLS and target-bound forwarding."""
|
|
38
|
+
|
|
39
|
+
account_id: str
|
|
40
|
+
tenant_id: str
|
|
41
|
+
caller_principal_type: str
|
|
42
|
+
caller_principal_id: str
|
|
43
|
+
target_agent_id: str
|
|
44
|
+
target_runtime_id: str
|
|
45
|
+
target_a2a_agent_id: str
|
|
46
|
+
authn_mode: str
|
|
47
|
+
caller_runtime_id: str | None = None
|
|
48
|
+
|
|
49
|
+
def validate(self) -> None:
|
|
50
|
+
values = {
|
|
51
|
+
"account_id": self.account_id,
|
|
52
|
+
"caller_principal_type": self.caller_principal_type,
|
|
53
|
+
"caller_principal_id": self.caller_principal_id,
|
|
54
|
+
"target_agent_id": self.target_agent_id,
|
|
55
|
+
"target_runtime_id": self.target_runtime_id,
|
|
56
|
+
"target_a2a_agent_id": self.target_a2a_agent_id,
|
|
57
|
+
"authn_mode": self.authn_mode,
|
|
58
|
+
}
|
|
59
|
+
missing = [name for name, value in values.items() if not str(value).strip()]
|
|
60
|
+
if missing:
|
|
61
|
+
raise PermissionError(
|
|
62
|
+
"verified Gateway identity is missing " + ", ".join(missing)
|
|
63
|
+
)
|
|
64
|
+
if self.caller_principal_type not in _CALLER_PRINCIPAL_TYPES:
|
|
65
|
+
raise PermissionError("verified Gateway identity has an unsupported caller type")
|
|
66
|
+
if self.authn_mode != _AUTHN_MODE_BY_CALLER_TYPE[self.caller_principal_type]:
|
|
67
|
+
raise PermissionError("verified Gateway identity has an invalid authentication mode")
|
|
68
|
+
if self.caller_principal_type == "runtime" and not str(
|
|
69
|
+
self.caller_runtime_id or ""
|
|
70
|
+
).strip():
|
|
71
|
+
raise PermissionError("verified runtime caller identity is missing caller runtime")
|
|
72
|
+
if self.caller_principal_type != "runtime" and self.caller_runtime_id is not None:
|
|
73
|
+
raise PermissionError("verified non-runtime caller identity includes a caller runtime")
|
|
74
|
+
for field_name, limit in (
|
|
75
|
+
("account_id", 64),
|
|
76
|
+
("tenant_id", 64),
|
|
77
|
+
("caller_principal_type", 16),
|
|
78
|
+
("caller_principal_id", 128),
|
|
79
|
+
("target_agent_id", 64),
|
|
80
|
+
("target_runtime_id", 64),
|
|
81
|
+
("target_a2a_agent_id", 64),
|
|
82
|
+
("authn_mode", 32),
|
|
83
|
+
("caller_runtime_id", 64),
|
|
84
|
+
):
|
|
85
|
+
value = getattr(self, field_name)
|
|
86
|
+
if value is not None and len(str(value)) > limit:
|
|
87
|
+
raise PermissionError(
|
|
88
|
+
f"verified Gateway identity field {field_name} exceeds {limit} characters"
|
|
89
|
+
)
|
|
90
|
+
if not self.target_agent_id.startswith("ar-"):
|
|
91
|
+
raise PermissionError("verified Gateway identity has an invalid target Agent")
|
|
92
|
+
try:
|
|
93
|
+
require_a2a_resource_id(
|
|
94
|
+
self.target_a2a_agent_id,
|
|
95
|
+
"a2a-agent-",
|
|
96
|
+
field_name="verified Gateway identity target A2A Agent",
|
|
97
|
+
)
|
|
98
|
+
except ValueError as exc:
|
|
99
|
+
raise PermissionError(
|
|
100
|
+
"verified Gateway identity has an invalid target A2A Agent"
|
|
101
|
+
) from exc
|
|
102
|
+
|
|
103
|
+
def context_identity(self) -> A2AContextIdentity:
|
|
104
|
+
return A2AContextIdentity(
|
|
105
|
+
account_id=self.account_id,
|
|
106
|
+
tenant_id=self.tenant_id,
|
|
107
|
+
caller_principal_type=self.caller_principal_type,
|
|
108
|
+
caller_principal_id=self.caller_principal_id,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def owner_key(self) -> str:
|
|
112
|
+
return "/".join(
|
|
113
|
+
(
|
|
114
|
+
self.account_id.strip(),
|
|
115
|
+
self.tenant_id.strip(),
|
|
116
|
+
self.caller_principal_type.strip(),
|
|
117
|
+
self.caller_principal_id.strip(),
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class GatewayIdentityVerifier(ABC):
|
|
123
|
+
"""Platform adapter that verifies Gateway mTLS and target-bound forwarding."""
|
|
124
|
+
|
|
125
|
+
@abstractmethod
|
|
126
|
+
async def verify(self, request: Request) -> A2AIngressIdentity:
|
|
127
|
+
raise NotImplementedError
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class GatewayProbeVerifier(ABC):
|
|
131
|
+
"""Verifies a trusted Gateway/server probe for the Runtime-local AgentCard."""
|
|
132
|
+
|
|
133
|
+
@abstractmethod
|
|
134
|
+
async def verify_probe(self, request: Request) -> None:
|
|
135
|
+
raise NotImplementedError
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class CallableGatewayIdentityVerifier(GatewayIdentityVerifier):
|
|
139
|
+
"""Small adapter for product injectors and tests."""
|
|
140
|
+
|
|
141
|
+
def __init__(
|
|
142
|
+
self,
|
|
143
|
+
verify: Callable[[Request], Awaitable[A2AIngressIdentity]],
|
|
144
|
+
) -> None:
|
|
145
|
+
self._verify = verify
|
|
146
|
+
|
|
147
|
+
async def verify(self, request: Request) -> A2AIngressIdentity:
|
|
148
|
+
return await self._verify(request)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class CallableGatewayProbeVerifier(GatewayProbeVerifier):
|
|
152
|
+
"""Small probe-verifier adapter for product injectors and tests."""
|
|
153
|
+
|
|
154
|
+
def __init__(self, verify_probe: Callable[[Request], Awaitable[None]]) -> None:
|
|
155
|
+
self._verify_probe = verify_probe
|
|
156
|
+
|
|
157
|
+
async def verify_probe(self, request: Request) -> None:
|
|
158
|
+
await self._verify_probe(request)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class A2ATrustedIdentityResolver:
|
|
162
|
+
"""Reads only identity written by verified ingress middleware."""
|
|
163
|
+
|
|
164
|
+
state_key = "a2a_identity"
|
|
165
|
+
|
|
166
|
+
def __init__(
|
|
167
|
+
self,
|
|
168
|
+
*,
|
|
169
|
+
expected_account_id: str | None = None,
|
|
170
|
+
expected_tenant_id: str | None = None,
|
|
171
|
+
expected_target_agent_id: str | None = None,
|
|
172
|
+
expected_target_runtime_id: str | None = None,
|
|
173
|
+
expected_target_a2a_agent_id: str | None = None,
|
|
174
|
+
target_binding: A2AIngressTargetBinding | None = None,
|
|
175
|
+
) -> None:
|
|
176
|
+
if target_binding is not None:
|
|
177
|
+
if any(
|
|
178
|
+
value is not None
|
|
179
|
+
for value in (
|
|
180
|
+
expected_account_id,
|
|
181
|
+
expected_tenant_id,
|
|
182
|
+
expected_target_agent_id,
|
|
183
|
+
expected_target_runtime_id,
|
|
184
|
+
expected_target_a2a_agent_id,
|
|
185
|
+
)
|
|
186
|
+
):
|
|
187
|
+
raise ValueError(
|
|
188
|
+
"pass target_binding or individual expected target fields, not both"
|
|
189
|
+
)
|
|
190
|
+
expected_account_id = target_binding.account_id
|
|
191
|
+
expected_tenant_id = target_binding.tenant_id
|
|
192
|
+
expected_target_agent_id = target_binding.agent_id
|
|
193
|
+
expected_target_runtime_id = target_binding.runtime_id
|
|
194
|
+
expected_target_a2a_agent_id = target_binding.a2a_agent_id
|
|
195
|
+
self._expected_account_id = str(expected_account_id or "").strip() or None
|
|
196
|
+
self._expected_tenant_id = expected_tenant_id
|
|
197
|
+
self._expected_target_agent_id = str(expected_target_agent_id or "").strip() or None
|
|
198
|
+
self._expected_target_runtime_id = str(expected_target_runtime_id or "").strip() or None
|
|
199
|
+
self._expected_target_a2a_agent_id = str(expected_target_a2a_agent_id or "").strip() or None
|
|
200
|
+
|
|
201
|
+
def resolve(self, request: Request) -> A2AIngressIdentity:
|
|
202
|
+
raw_state = request.scope.get("state")
|
|
203
|
+
state = raw_state if isinstance(raw_state, dict) else {}
|
|
204
|
+
identity = state.get(self.state_key)
|
|
205
|
+
if not isinstance(identity, A2AIngressIdentity):
|
|
206
|
+
raise PermissionError("verified Gateway identity is required for inbound A2A")
|
|
207
|
+
self._validate(identity)
|
|
208
|
+
return identity
|
|
209
|
+
|
|
210
|
+
def _validate(self, identity: A2AIngressIdentity) -> None:
|
|
211
|
+
identity.validate()
|
|
212
|
+
identity.context_identity().canonical_owner()
|
|
213
|
+
if (
|
|
214
|
+
self._expected_account_id is not None
|
|
215
|
+
and identity.account_id != self._expected_account_id
|
|
216
|
+
):
|
|
217
|
+
raise PermissionError("verified Gateway identity targets a different account")
|
|
218
|
+
if (
|
|
219
|
+
self._expected_tenant_id is not None
|
|
220
|
+
and identity.tenant_id != self._expected_tenant_id
|
|
221
|
+
):
|
|
222
|
+
raise PermissionError("verified Gateway identity targets a different tenant")
|
|
223
|
+
if (
|
|
224
|
+
self._expected_target_agent_id is not None
|
|
225
|
+
and identity.target_agent_id != self._expected_target_agent_id
|
|
226
|
+
):
|
|
227
|
+
raise PermissionError("verified Gateway identity targets a different Agent")
|
|
228
|
+
if (
|
|
229
|
+
self._expected_target_runtime_id is not None
|
|
230
|
+
and identity.target_runtime_id != self._expected_target_runtime_id
|
|
231
|
+
):
|
|
232
|
+
raise PermissionError("verified Gateway identity targets a different Runtime")
|
|
233
|
+
if (
|
|
234
|
+
self._expected_target_a2a_agent_id is not None
|
|
235
|
+
and identity.target_a2a_agent_id != self._expected_target_a2a_agent_id
|
|
236
|
+
):
|
|
237
|
+
raise PermissionError("verified Gateway identity targets a different A2A Agent")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class A2AGatewayIdentityMiddleware(BaseHTTPMiddleware):
|
|
241
|
+
"""Verify Gateway identity before JSON-RPC or HTTP+JSON A2A handlers run."""
|
|
242
|
+
|
|
243
|
+
def __init__(
|
|
244
|
+
self,
|
|
245
|
+
app: Any,
|
|
246
|
+
*,
|
|
247
|
+
verifier: GatewayIdentityVerifier,
|
|
248
|
+
probe_verifier: GatewayProbeVerifier | None = None,
|
|
249
|
+
expected_account_id: str | None = None,
|
|
250
|
+
expected_tenant_id: str | None = None,
|
|
251
|
+
expected_target_agent_id: str | None = None,
|
|
252
|
+
expected_target_runtime_id: str | None = None,
|
|
253
|
+
expected_target_a2a_agent_id: str | None = None,
|
|
254
|
+
target_binding: A2AIngressTargetBinding | None = None,
|
|
255
|
+
) -> None:
|
|
256
|
+
super().__init__(app)
|
|
257
|
+
self._verifier = verifier
|
|
258
|
+
self._probe_verifier = probe_verifier
|
|
259
|
+
self._identity_resolver = A2ATrustedIdentityResolver(
|
|
260
|
+
expected_account_id=expected_account_id,
|
|
261
|
+
expected_tenant_id=expected_tenant_id,
|
|
262
|
+
expected_target_agent_id=expected_target_agent_id,
|
|
263
|
+
expected_target_runtime_id=expected_target_runtime_id,
|
|
264
|
+
expected_target_a2a_agent_id=expected_target_a2a_agent_id,
|
|
265
|
+
target_binding=target_binding,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
async def dispatch(self, request: Request, call_next: Any):
|
|
269
|
+
if request.url.path == "/.well-known/agent-card.json":
|
|
270
|
+
try:
|
|
271
|
+
if self._probe_verifier is None:
|
|
272
|
+
raise PermissionError("A2A AgentCard probe verifier is not configured")
|
|
273
|
+
await self._probe_verifier.verify_probe(request)
|
|
274
|
+
except Exception:
|
|
275
|
+
return JSONResponse(
|
|
276
|
+
status_code=401,
|
|
277
|
+
content={"detail": "trusted Gateway identity is required for A2A AgentCard"},
|
|
278
|
+
)
|
|
279
|
+
elif request.url.path == "/a2a/jsonrpc" or request.url.path.startswith("/a2a/v1/"):
|
|
280
|
+
try:
|
|
281
|
+
identity = await self._verifier.verify(request)
|
|
282
|
+
self._identity_resolver._validate(identity)
|
|
283
|
+
except Exception:
|
|
284
|
+
return JSONResponse(
|
|
285
|
+
status_code=401,
|
|
286
|
+
content={"detail": "verified Gateway identity is required for inbound A2A"},
|
|
287
|
+
)
|
|
288
|
+
raw_state = request.scope.get("state")
|
|
289
|
+
state = raw_state if isinstance(raw_state, dict) else {}
|
|
290
|
+
state[A2ATrustedIdentityResolver.state_key] = identity
|
|
291
|
+
request.scope["state"] = state
|
|
292
|
+
return await call_next(request)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
__all__ = [
|
|
296
|
+
"A2AGatewayIdentityMiddleware",
|
|
297
|
+
"A2AIngressTargetBinding",
|
|
298
|
+
"A2AIngressIdentity",
|
|
299
|
+
"A2ATrustedIdentityResolver",
|
|
300
|
+
"CallableGatewayIdentityVerifier",
|
|
301
|
+
"CallableGatewayProbeVerifier",
|
|
302
|
+
"GatewayIdentityVerifier",
|
|
303
|
+
"GatewayProbeVerifier",
|
|
304
|
+
]
|
ksadk/a2a/ids.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""AgentEngine A2A resource ID validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
_UUID4_HEX = re.compile(r"^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_a2a_resource_id(value: str, prefix: str) -> bool:
|
|
11
|
+
"""Return whether value is `<prefix><uuid4_hex>` with a lowercase compact UUID."""
|
|
12
|
+
return value.startswith(prefix) and bool(_UUID4_HEX.fullmatch(value.removeprefix(prefix)))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def require_a2a_resource_id(value: str, prefix: str, *, field_name: str) -> str:
|
|
16
|
+
"""Validate an AgentEngine A2A resource ID and return it unchanged."""
|
|
17
|
+
if not is_a2a_resource_id(value, prefix):
|
|
18
|
+
raise ValueError(f"{field_name} must use the {prefix}<uuid4_hex> format")
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ["is_a2a_resource_id", "require_a2a_resource_id"]
|
ksadk/a2a/langgraph.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""LangGraph 编排方调远端 A2A agent 的流式 helper。
|
|
2
|
+
|
|
3
|
+
ADK 有原生 ``RemoteA2aAgent``;LangGraph 没有等价封装。这里提供结构化流式
|
|
4
|
+
helper,保留远端 ``thinking`` / ``text`` 类型,并可直接写入 LangGraph custom
|
|
5
|
+
stream,无需 task store、无需 Space 绑定——直接走 a2a-sdk 原生 client。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import inspect
|
|
11
|
+
import uuid
|
|
12
|
+
from collections.abc import AsyncIterator
|
|
13
|
+
from typing import Any, Callable, Literal, TypedDict
|
|
14
|
+
|
|
15
|
+
import httpx
|
|
16
|
+
from a2a.client import ClientCallContext, ClientConfig, create_client
|
|
17
|
+
from a2a.client.card_resolver import A2ACardResolver
|
|
18
|
+
from a2a.types import Message, Part, Role, SendMessageRequest
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class A2AStreamEvent(TypedDict):
|
|
22
|
+
"""可直接交给 LangGraph custom writer 的远端流式事件。"""
|
|
23
|
+
|
|
24
|
+
type: Literal["thinking", "text"]
|
|
25
|
+
delta: str
|
|
26
|
+
replace: bool
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _base_url(agent_card_url: str) -> str:
|
|
30
|
+
"""从 agent_card_url 取 base_url(剥掉 well-known 路径后缀)。"""
|
|
31
|
+
url = agent_card_url.strip().rstrip("/")
|
|
32
|
+
for suffix in ("/.well-known/agent-card.json", "/.well-known/agent.json"):
|
|
33
|
+
if url.endswith(suffix):
|
|
34
|
+
url = url[: -len(suffix)]
|
|
35
|
+
return url
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def stream_a2a_agent_events(
|
|
39
|
+
agent_card_url: str,
|
|
40
|
+
message: str,
|
|
41
|
+
*,
|
|
42
|
+
httpx_client: httpx.AsyncClient | None = None,
|
|
43
|
+
timeout: float = 60.0,
|
|
44
|
+
) -> AsyncIterator[A2AStreamEvent]:
|
|
45
|
+
"""流式调用远端 A2A agent,保留 reasoning 与正文的结构化类型。
|
|
46
|
+
|
|
47
|
+
在 langgraph node 里配合 custom stream 透传::
|
|
48
|
+
|
|
49
|
+
from langgraph.config import get_stream_writer
|
|
50
|
+
from ksadk.a2a.langgraph import stream_a2a_agent_events
|
|
51
|
+
|
|
52
|
+
async def call_demo(state, config):
|
|
53
|
+
writer = get_stream_writer()
|
|
54
|
+
collected = ""
|
|
55
|
+
async for event in stream_a2a_agent_events(
|
|
56
|
+
"http://127.0.0.1:8094", state["query"]
|
|
57
|
+
):
|
|
58
|
+
writer(event)
|
|
59
|
+
if event["type"] == "text":
|
|
60
|
+
collected += event["delta"]
|
|
61
|
+
return {"result": collected}
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
agent_card_url: 远端 agent 的 base URL(如 ``http://127.0.0.1:8094``)
|
|
65
|
+
或完整 agent-card.json URL。
|
|
66
|
+
message: 发给远端 agent 的用户消息。
|
|
67
|
+
httpx_client: 可选复用的 ``httpx.AsyncClient``;不传则内部创建。
|
|
68
|
+
timeout: httpx 请求超时秒数。
|
|
69
|
+
|
|
70
|
+
Yields:
|
|
71
|
+
``{"type": "thinking"|"text", "delta": str, "replace": bool}``。
|
|
72
|
+
reasoning 由 ``adk_thought`` Part metadata 识别,status_update 不伪装
|
|
73
|
+
成思考;``replace`` 标识权威快照替换既有同类输出。
|
|
74
|
+
"""
|
|
75
|
+
own_client = httpx_client is None
|
|
76
|
+
client_httpx = httpx_client or httpx.AsyncClient(timeout=timeout)
|
|
77
|
+
try:
|
|
78
|
+
resolver = A2ACardResolver(httpx_client=client_httpx, base_url=_base_url(agent_card_url))
|
|
79
|
+
card = await resolver.get_agent_card()
|
|
80
|
+
client = await create_client(
|
|
81
|
+
agent=card,
|
|
82
|
+
client_config=ClientConfig(httpx_client=client_httpx, streaming=True),
|
|
83
|
+
)
|
|
84
|
+
request = SendMessageRequest(
|
|
85
|
+
message=Message(
|
|
86
|
+
role=Role.ROLE_USER,
|
|
87
|
+
parts=[Part(text=message)],
|
|
88
|
+
message_id=f"lg-a2a-{uuid.uuid4().hex}",
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
snapshots: dict[tuple[str, str], str] = {}
|
|
92
|
+
async for event in client.send_message(request, context=ClientCallContext()):
|
|
93
|
+
for stream_event in _extract_artifact_events(event, snapshots):
|
|
94
|
+
yield stream_event
|
|
95
|
+
finally:
|
|
96
|
+
if own_client:
|
|
97
|
+
await client_httpx.aclose()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def stream_a2a_agent(
|
|
101
|
+
agent_card_url: str,
|
|
102
|
+
message: str,
|
|
103
|
+
*,
|
|
104
|
+
httpx_client: httpx.AsyncClient | None = None,
|
|
105
|
+
timeout: float = 60.0,
|
|
106
|
+
) -> AsyncIterator[str]:
|
|
107
|
+
"""兼容旧接口:只产出正文文本增量,reasoning 不混入最终答案。
|
|
108
|
+
|
|
109
|
+
该字符串流无法表达 ``replace`` 语义。需要正确处理远端权威快照的调用方
|
|
110
|
+
应使用 ``stream_a2a_agent_events`` 或 ``stream_a2a_agent_to_writer``。
|
|
111
|
+
"""
|
|
112
|
+
async for event in stream_a2a_agent_events(
|
|
113
|
+
agent_card_url,
|
|
114
|
+
message,
|
|
115
|
+
httpx_client=httpx_client,
|
|
116
|
+
timeout=timeout,
|
|
117
|
+
):
|
|
118
|
+
if event["type"] == "text":
|
|
119
|
+
yield event["delta"]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def stream_a2a_agent_to_writer(
|
|
123
|
+
agent_card_url: str,
|
|
124
|
+
message: str,
|
|
125
|
+
*,
|
|
126
|
+
writer: Callable[[A2AStreamEvent], Any] | None = None,
|
|
127
|
+
httpx_client: httpx.AsyncClient | None = None,
|
|
128
|
+
timeout: float = 60.0,
|
|
129
|
+
) -> str:
|
|
130
|
+
"""将远端 typed events 写入 LangGraph custom stream,返回纯正文结果。
|
|
131
|
+
|
|
132
|
+
``writer`` 不传时自动使用当前 LangGraph node 的 ``get_stream_writer()``。
|
|
133
|
+
demo 只需调用本函数,无需了解 A2A wire artifact、append 或快照去重。
|
|
134
|
+
"""
|
|
135
|
+
if writer is None:
|
|
136
|
+
from langgraph.config import get_stream_writer
|
|
137
|
+
|
|
138
|
+
writer = get_stream_writer()
|
|
139
|
+
|
|
140
|
+
output: list[str] = []
|
|
141
|
+
async for event in stream_a2a_agent_events(
|
|
142
|
+
agent_card_url,
|
|
143
|
+
message,
|
|
144
|
+
httpx_client=httpx_client,
|
|
145
|
+
timeout=timeout,
|
|
146
|
+
):
|
|
147
|
+
write_result = writer(event)
|
|
148
|
+
if inspect.isawaitable(write_result):
|
|
149
|
+
await write_result
|
|
150
|
+
if event["type"] == "text":
|
|
151
|
+
if event["replace"]:
|
|
152
|
+
output = [event["delta"]]
|
|
153
|
+
else:
|
|
154
|
+
output.append(event["delta"])
|
|
155
|
+
return "".join(output)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _extract_artifact_events(
|
|
159
|
+
event: Any,
|
|
160
|
+
snapshots: dict[tuple[str, str], str],
|
|
161
|
+
) -> list[A2AStreamEvent]:
|
|
162
|
+
"""从 A2A artifact_update 提取去重后的 typed events。
|
|
163
|
+
|
|
164
|
+
快照按 ``(artifact_id, type)`` 隔离,避免 reasoning 与 response 互相覆盖。
|
|
165
|
+
``append=False`` 按累计快照 diff,``append=True`` 按增量直接透传。
|
|
166
|
+
"""
|
|
167
|
+
which = None
|
|
168
|
+
try:
|
|
169
|
+
which = event.WhichOneof("payload")
|
|
170
|
+
except Exception: # 非 protobuf 或结构不符时静默跳过
|
|
171
|
+
which = None
|
|
172
|
+
if which == "artifact_update":
|
|
173
|
+
artifact_update = getattr(event, "artifact_update", None)
|
|
174
|
+
artifact = getattr(artifact_update, "artifact", None)
|
|
175
|
+
if artifact is None:
|
|
176
|
+
return []
|
|
177
|
+
return _events_from_artifact(
|
|
178
|
+
artifact,
|
|
179
|
+
append=bool(getattr(artifact_update, "append", False)),
|
|
180
|
+
snapshots=snapshots,
|
|
181
|
+
)
|
|
182
|
+
if which == "task":
|
|
183
|
+
result: list[A2AStreamEvent] = []
|
|
184
|
+
task = getattr(event, "task", None)
|
|
185
|
+
for artifact in getattr(task, "artifacts", []):
|
|
186
|
+
result.extend(_events_from_artifact(artifact, append=False, snapshots=snapshots))
|
|
187
|
+
return result
|
|
188
|
+
if which == "message":
|
|
189
|
+
message = getattr(event, "message", None)
|
|
190
|
+
if message is None:
|
|
191
|
+
return []
|
|
192
|
+
artifact = _MessageArtifact(message)
|
|
193
|
+
return _events_from_artifact(artifact, append=False, snapshots=snapshots)
|
|
194
|
+
return []
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class _MessageArtifact:
|
|
198
|
+
"""把直接 Message 响应投影成 artifact-like 对象复用解析逻辑。"""
|
|
199
|
+
|
|
200
|
+
def __init__(self, message: Any) -> None:
|
|
201
|
+
self.artifact_id = f"message:{getattr(message, 'message_id', '') or 'anonymous'}"
|
|
202
|
+
self.name = "response"
|
|
203
|
+
self.parts = getattr(message, "parts", [])
|
|
204
|
+
self.metadata = getattr(message, "metadata", None)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _events_from_artifact(
|
|
208
|
+
artifact: Any,
|
|
209
|
+
*,
|
|
210
|
+
append: bool,
|
|
211
|
+
snapshots: dict[tuple[str, str], str],
|
|
212
|
+
) -> list[A2AStreamEvent]:
|
|
213
|
+
"""解析一个 artifact-like 对象,支持增量、累计快照和权威替换。"""
|
|
214
|
+
|
|
215
|
+
artifact_id = str(
|
|
216
|
+
getattr(artifact, "artifact_id", "")
|
|
217
|
+
or getattr(artifact, "name", "")
|
|
218
|
+
or "anonymous"
|
|
219
|
+
)
|
|
220
|
+
artifact_is_reasoning = (
|
|
221
|
+
str(getattr(artifact, "name", "")).lower() == "reasoning"
|
|
222
|
+
or _metadata_flag(getattr(artifact, "metadata", None), "adk_thought")
|
|
223
|
+
)
|
|
224
|
+
grouped: list[tuple[str, str, bool]] = []
|
|
225
|
+
for part in getattr(artifact, "parts", []):
|
|
226
|
+
text = str(getattr(part, "text", "") or "")
|
|
227
|
+
if not text:
|
|
228
|
+
continue
|
|
229
|
+
event_type = (
|
|
230
|
+
"thinking"
|
|
231
|
+
if artifact_is_reasoning
|
|
232
|
+
or _metadata_flag(getattr(part, "metadata", None), "adk_thought")
|
|
233
|
+
else "text"
|
|
234
|
+
)
|
|
235
|
+
replaces_output = _metadata_flag(
|
|
236
|
+
getattr(part, "metadata", None),
|
|
237
|
+
"ksadk_output_snapshot",
|
|
238
|
+
)
|
|
239
|
+
if grouped and grouped[-1][0] == event_type:
|
|
240
|
+
grouped[-1] = (
|
|
241
|
+
event_type,
|
|
242
|
+
grouped[-1][1] + text,
|
|
243
|
+
grouped[-1][2] or replaces_output,
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
grouped.append((event_type, text, replaces_output))
|
|
247
|
+
|
|
248
|
+
result: list[A2AStreamEvent] = []
|
|
249
|
+
for event_type, text, replaces_output in grouped:
|
|
250
|
+
key = (artifact_id, event_type)
|
|
251
|
+
snapshot = snapshots.get(key, "")
|
|
252
|
+
if append:
|
|
253
|
+
delta = text
|
|
254
|
+
snapshots[key] = snapshot + text
|
|
255
|
+
replace = False
|
|
256
|
+
else:
|
|
257
|
+
delta = text[len(snapshot) :] if text.startswith(snapshot) else text
|
|
258
|
+
snapshots[key] = text
|
|
259
|
+
replace = replaces_output or bool(snapshot and not text.startswith(snapshot))
|
|
260
|
+
if delta:
|
|
261
|
+
result.append(
|
|
262
|
+
{
|
|
263
|
+
"type": event_type, # type: ignore[typeddict-item]
|
|
264
|
+
"delta": delta,
|
|
265
|
+
"replace": replace,
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
return result
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _metadata_flag(metadata: Any, key: str) -> bool:
|
|
272
|
+
if metadata is None:
|
|
273
|
+
return False
|
|
274
|
+
try:
|
|
275
|
+
return bool(metadata[key])
|
|
276
|
+
except (KeyError, TypeError, ValueError):
|
|
277
|
+
return False
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
__all__ = [
|
|
281
|
+
"A2AStreamEvent",
|
|
282
|
+
"stream_a2a_agent",
|
|
283
|
+
"stream_a2a_agent_events",
|
|
284
|
+
"stream_a2a_agent_to_writer",
|
|
285
|
+
]
|