agentengine-sdk-python 0.6.9__py3-none-any.whl → 0.7.0__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.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/METADATA +6 -4
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/RECORD +123 -128
- ksadk/api/client.py +19 -0
- ksadk/cli/cmd_create.py +34 -7
- ksadk/cli/cmd_hermes.py +6 -2
- ksadk/cli/cmd_invoke.py +208 -41
- ksadk/cli/cmd_mcp.py +6 -1
- ksadk/cli/cmd_openclaw.py +6 -2
- ksadk/cli/cmd_run.py +13 -6
- ksadk/cli/network_options.py +5 -3
- ksadk/configs/env_registry.py +1 -0
- ksadk/conversations/message_projection.py +269 -0
- ksadk/conversations/model_context.py +11 -1
- ksadk/conversations/runtime.py +81 -13
- ksadk/deployment/base.py +1 -1
- ksadk/deployment/providers/serverless.py +18 -8
- ksadk/memory/adk/resilient_session_service.py +268 -0
- ksadk/memory/adk/short_term_memory.py +21 -6
- ksadk/runners/adk_runner.py +974 -115
- ksadk/runners/langchain_runner.py +219 -7
- ksadk/runners/langgraph_runner.py +145 -12
- ksadk/runners/remote_runner.py +143 -18
- ksadk/server/app.py +148 -6
- ksadk/server/static/assets/{ArtifactsPanel-C0meuOdi.js → ArtifactsPanel-D-SSn2ee.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-BbI4HPtJ.js → CodeBlock-P16VOLYi.js} +1 -1
- ksadk/server/static/assets/{MathMessageMarkdown-DMLQYoLp.js → MathMessageMarkdown-CdGiQWHR.js} +1 -1
- ksadk/server/static/assets/{MermaidBlock-DCR8WZ17.js → MermaidBlock-IOcal8cm.js} +6 -6
- ksadk/server/static/assets/{NativeTerminalPanel-CKUvWhUz.js → NativeTerminalPanel-DGSDWsei.js} +1 -1
- ksadk/server/static/assets/{arc-bj2rZnP5.js → arc-BA13sHc9.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-2u1ltDIj.js → architectureDiagram-3BPJPVTR-DzJzgVoL.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-UTdVcZhn.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-Dix0GD4m.js} +1 -1
- ksadk/server/static/assets/channel-CTuydARX.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-C2JxuZ42.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-Be57QPgD.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-WjSuy4nv.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-BOovP-b5.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DOmZNmWj.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-DzwlodkU.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.js → chunk-ND2GUHAM-DMqAikEU.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-_N8bJ-EU.js → cose-bilkent-S5V4N54A-Zhy2GBnP.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-CbKrtIUz.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-BRApgQRK.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-D0xbnDiK.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-CVugVI9L.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-DozUGgee.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-ZOYsKlru.js} +1 -1
- ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-2hInLS_J.js} +1 -1
- ksadk/server/static/assets/{dist-BMKM66B4.js → dist-8EEyGgDx.js} +1 -1
- ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-8_SfK1IP.js} +1 -1
- ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-B0NP_Z0z.js} +1 -1
- ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-BIJVDMuB.js} +1 -1
- ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-BmlMPsCc.js} +1 -1
- ksadk/server/static/assets/{dist-D99wf6_X.js → dist-BrCcQ47T.js} +1 -1
- ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-BssVmjIo.js} +1 -1
- ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-C3HIIaN5.js} +1 -1
- ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-CPBtqRTW.js} +1 -1
- ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CkEKJFtk.js} +1 -1
- ksadk/server/static/assets/{dist-B67uF2uK.js → dist-D-JMdWPQ.js} +1 -1
- ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-D-xOD048.js} +1 -1
- ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-D57wnGWd.js} +1 -1
- ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-DL_NKc_z.js} +1 -1
- ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-DMezvBGJ.js} +1 -1
- ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-DmFaq_Iy.js} +1 -1
- ksadk/server/static/assets/{dist-DLMjL01k.js → dist-Dp0k7bWI.js} +1 -1
- ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DpKtt4Bo.js} +1 -1
- ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-DyOkZmze.js} +1 -1
- ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-DyWsb78Q.js} +1 -1
- ksadk/server/static/assets/{dist-S4klkK5W.js → dist-gX6GOLKq.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.js → erDiagram-TEJ5UH35-Bk-9bgE5.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-DtTmUan-.js → flowDiagram-I6XJVG4X-Dn9kZVuZ.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.js → ganttDiagram-6RSMTGT7-BGxXlJqm.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-BR5s6Pws.js → gitGraphDiagram-PVQCEYII-C7Q0X-wi.js} +1 -1
- ksadk/server/static/assets/{index-BH7-pC0B.js → index-C00Lwg3L.js} +6 -6
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-DYPc5cvh.js → infoDiagram-5YYISTIA-BO_CSqq5.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-eYLPLd46.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-De9geSb0.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-Cs80kQto.js} +1 -1
- ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DSrCFD8n.js} +1 -1
- ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CN9sbKTF.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-DlgSBU8l.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.js → mindmap-definition-RKZ34NQL-9afz7Oug.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPnuCB8c.js → pieDiagram-4H26LBE5-DPv_VstN.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.js → quadrantDiagram-W4KKPZXB-o77wZ1XK.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-mjB_yYkW.js → requirementDiagram-4Y6WPE33-BYVw81VC.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-B7nZFMKT.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-BGRRrFw5.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.js → stateDiagram-AJRCARHV-BUfHCoPs.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-CwSec0Wa.js → timeline-definition-PNZ67QCA-FbCTGrf9.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-agCVobiF.js → vennDiagram-CIIHVFJN-DWgwtPsJ.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-2x8G-tTM.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.js → xychartDiagram-2RQKCTM6-Drma3AYl.js} +1 -1
- ksadk/server/static/index.html +1 -1
- ksadk/sessions/__init__.py +17 -21
- ksadk/sessions/continuity.py +28 -4
- ksadk/sessions/postgres_service.py +77 -14
- ksadk/sessions/resilience.py +52 -0
- ksadk/sessions/resilient.py +360 -0
- ksadk/toolsets/__init__.py +2 -0
- ksadk/toolsets/workspace.py +49 -10
- ksadk/tracing/setup.py +63 -0
- ksadk/tui/__init__.py +22 -7
- ksadk/tui/loop.py +1877 -0
- ksadk/tui/stream_render.py +68 -0
- ksadk/version.py +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
- ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
- ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
- ksadk/tui/app.py +0 -400
- ksadk/tui/clipboard.py +0 -100
- ksadk/tui/widgets/__init__.py +0 -22
- ksadk/tui/widgets/assistant.py +0 -89
- ksadk/tui/widgets/base.py +0 -54
- ksadk/tui/widgets/chat_input.py +0 -246
- ksadk/tui/widgets/history.py +0 -156
- ksadk/tui/widgets/system.py +0 -87
- ksadk/tui/widgets/thinking.py +0 -110
- ksadk/tui/widgets/tool_call.py +0 -161
- ksadk/tui/widgets/user.py +0 -37
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, Sequence
|
|
4
|
+
from typing import Any
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def project_session_messages(
|
|
9
|
+
events: Sequence[Mapping[str, Any]],
|
|
10
|
+
*,
|
|
11
|
+
include_reasoning: bool = False,
|
|
12
|
+
include_tool_events: bool = False,
|
|
13
|
+
include_attachments: bool = True,
|
|
14
|
+
) -> list[dict[str, Any]]:
|
|
15
|
+
"""Project persisted runtime events into the chat history contract."""
|
|
16
|
+
|
|
17
|
+
groups: dict[str, list[Mapping[str, Any]]] = {}
|
|
18
|
+
for event in sorted(events, key=lambda item: int(item.get("SeqId") or 0)):
|
|
19
|
+
invocation_id = str(event.get("InvocationId") or f"seq:{event.get('SeqId')}")
|
|
20
|
+
groups.setdefault(invocation_id, []).append(event)
|
|
21
|
+
|
|
22
|
+
messages: list[dict[str, Any]] = []
|
|
23
|
+
for group in groups.values():
|
|
24
|
+
messages.extend(
|
|
25
|
+
_project_event_group(
|
|
26
|
+
group,
|
|
27
|
+
include_reasoning=include_reasoning,
|
|
28
|
+
include_tool_events=include_tool_events,
|
|
29
|
+
include_attachments=include_attachments,
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
return sorted(messages, key=lambda item: int(item.get("SeqId") or 0))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _project_event_group(
|
|
36
|
+
events: Sequence[Mapping[str, Any]],
|
|
37
|
+
*,
|
|
38
|
+
include_reasoning: bool,
|
|
39
|
+
include_tool_events: bool,
|
|
40
|
+
include_attachments: bool,
|
|
41
|
+
) -> list[dict[str, Any]]:
|
|
42
|
+
reasoning = [
|
|
43
|
+
{"text": _event_text(event), "SeqId": event.get("SeqId")}
|
|
44
|
+
for event in events
|
|
45
|
+
if event.get("EventType") == "reasoning" and _event_text(event)
|
|
46
|
+
]
|
|
47
|
+
tool_events = _project_tool_events(events) if include_tool_events else []
|
|
48
|
+
projected: list[dict[str, Any]] = []
|
|
49
|
+
assistant_seen = False
|
|
50
|
+
|
|
51
|
+
for event in events:
|
|
52
|
+
event_type = str(event.get("EventType") or "")
|
|
53
|
+
if event_type == "user_message":
|
|
54
|
+
message = _base_message(event, "user")
|
|
55
|
+
if include_attachments:
|
|
56
|
+
attachments = _event_attachments(event)
|
|
57
|
+
if attachments:
|
|
58
|
+
message["Attachments"] = attachments
|
|
59
|
+
projected.append(message)
|
|
60
|
+
elif event_type == "assistant_message":
|
|
61
|
+
message = _base_message(event, "assistant")
|
|
62
|
+
if include_reasoning and reasoning:
|
|
63
|
+
message["Reasoning"] = reasoning
|
|
64
|
+
if tool_events:
|
|
65
|
+
message["ToolEvents"] = tool_events
|
|
66
|
+
projected.append(message)
|
|
67
|
+
assistant_seen = True
|
|
68
|
+
|
|
69
|
+
if not assistant_seen and (reasoning or tool_events):
|
|
70
|
+
anchor = next(
|
|
71
|
+
(
|
|
72
|
+
event
|
|
73
|
+
for event in reversed(events)
|
|
74
|
+
if event.get("EventType")
|
|
75
|
+
in {"assistant_stream_snapshot", "approval_request", "tool_call", "reasoning"}
|
|
76
|
+
),
|
|
77
|
+
events[-1],
|
|
78
|
+
)
|
|
79
|
+
message = _base_message(anchor, "assistant", content="")
|
|
80
|
+
if include_reasoning and reasoning:
|
|
81
|
+
message["Reasoning"] = reasoning
|
|
82
|
+
if tool_events:
|
|
83
|
+
message["ToolEvents"] = tool_events
|
|
84
|
+
projected.append(message)
|
|
85
|
+
|
|
86
|
+
return projected
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _base_message(
|
|
90
|
+
event: Mapping[str, Any],
|
|
91
|
+
role: str,
|
|
92
|
+
*,
|
|
93
|
+
content: str | None = None,
|
|
94
|
+
) -> dict[str, Any]:
|
|
95
|
+
metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
|
|
96
|
+
message: dict[str, Any] = {
|
|
97
|
+
"MessageId": event.get("EventId"),
|
|
98
|
+
"Role": role,
|
|
99
|
+
"Content": {"text": _event_text(event) if content is None else content},
|
|
100
|
+
"Timestamp": event.get("Timestamp"),
|
|
101
|
+
"SeqId": event.get("SeqId"),
|
|
102
|
+
"InvocationId": event.get("InvocationId"),
|
|
103
|
+
}
|
|
104
|
+
for target, *sources in (
|
|
105
|
+
("ResponseId", "response_id", "ResponseId"),
|
|
106
|
+
("TraceId", "trace_id", "TraceId"),
|
|
107
|
+
("RootSpanId", "root_span_id", "rootSpanId", "RootSpanId"),
|
|
108
|
+
):
|
|
109
|
+
value = next((metadata.get(source) for source in sources if metadata.get(source)), None)
|
|
110
|
+
if value:
|
|
111
|
+
message[target] = str(value)
|
|
112
|
+
return message
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _event_text(event: Mapping[str, Any]) -> str:
|
|
116
|
+
content = event.get("Content")
|
|
117
|
+
if isinstance(content, str):
|
|
118
|
+
return content
|
|
119
|
+
if not isinstance(content, Mapping):
|
|
120
|
+
return ""
|
|
121
|
+
if content.get("text") is not None:
|
|
122
|
+
return str(content.get("text") or "")
|
|
123
|
+
return "".join(
|
|
124
|
+
str(part.get("text") or "")
|
|
125
|
+
for part in content.get("parts") or []
|
|
126
|
+
if isinstance(part, Mapping)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _project_tool_events(events: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]:
|
|
131
|
+
projected: list[dict[str, Any]] = []
|
|
132
|
+
pending_calls: list[tuple[Mapping[str, Any], dict[str, Any]]] = []
|
|
133
|
+
|
|
134
|
+
for event in events:
|
|
135
|
+
event_type = str(event.get("EventType") or "")
|
|
136
|
+
metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
|
|
137
|
+
if event_type == "tool_call":
|
|
138
|
+
entry = _tool_event_from_call(event, metadata)
|
|
139
|
+
projected.append(entry)
|
|
140
|
+
pending_calls.append((event, entry))
|
|
141
|
+
continue
|
|
142
|
+
if event_type != "tool_result":
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
name = str(metadata.get("tool_name") or "tool")
|
|
146
|
+
match = next(
|
|
147
|
+
(
|
|
148
|
+
item
|
|
149
|
+
for item in reversed(pending_calls)
|
|
150
|
+
if item[1]["Name"] == name and item[1]["Status"] == "running"
|
|
151
|
+
),
|
|
152
|
+
None,
|
|
153
|
+
)
|
|
154
|
+
if match is None:
|
|
155
|
+
entry = _tool_event_from_call(event, metadata)
|
|
156
|
+
projected.append(entry)
|
|
157
|
+
else:
|
|
158
|
+
entry = match[1]
|
|
159
|
+
output = metadata.get("tool_output", _event_text(event))
|
|
160
|
+
entry["Status"] = (
|
|
161
|
+
"failed" if isinstance(output, Mapping) and output.get("ok") is False else "completed"
|
|
162
|
+
)
|
|
163
|
+
entry["Result"] = output
|
|
164
|
+
entry["ResultSeqId"] = event.get("SeqId")
|
|
165
|
+
|
|
166
|
+
projected.extend(_project_approval_events(events))
|
|
167
|
+
return projected
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _tool_event_from_call(
|
|
171
|
+
event: Mapping[str, Any], metadata: Mapping[str, Any]
|
|
172
|
+
) -> dict[str, Any]:
|
|
173
|
+
tool_receipt = metadata.get("tool_receipt")
|
|
174
|
+
call_id = metadata.get("call_id") or metadata.get("run_id")
|
|
175
|
+
if not call_id and isinstance(tool_receipt, Mapping):
|
|
176
|
+
call_id = tool_receipt.get("tool_call_id")
|
|
177
|
+
return {
|
|
178
|
+
"SeqId": event.get("SeqId"),
|
|
179
|
+
"Type": "tool_call",
|
|
180
|
+
"Name": str(metadata.get("tool_name") or "tool"),
|
|
181
|
+
"Args": metadata.get("tool_args"),
|
|
182
|
+
"Status": "running",
|
|
183
|
+
"ToolCallId": call_id,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _project_approval_events(events: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]:
|
|
188
|
+
responses: dict[str, Mapping[str, Any]] = {}
|
|
189
|
+
for event in events:
|
|
190
|
+
if event.get("EventType") != "approval_response":
|
|
191
|
+
continue
|
|
192
|
+
metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
|
|
193
|
+
resume_input = metadata.get("resume_input")
|
|
194
|
+
if not isinstance(resume_input, Mapping):
|
|
195
|
+
continue
|
|
196
|
+
request_id = str(
|
|
197
|
+
resume_input.get("approval_request_id")
|
|
198
|
+
or resume_input.get("interrupt_id")
|
|
199
|
+
or resume_input.get("id")
|
|
200
|
+
or ""
|
|
201
|
+
)
|
|
202
|
+
responses[request_id] = event
|
|
203
|
+
|
|
204
|
+
approvals: list[dict[str, Any]] = []
|
|
205
|
+
for request in events:
|
|
206
|
+
if request.get("EventType") != "approval_request":
|
|
207
|
+
continue
|
|
208
|
+
metadata = request.get("Metadata") if isinstance(request.get("Metadata"), Mapping) else {}
|
|
209
|
+
interrupt_info = metadata.get("interrupt_info")
|
|
210
|
+
if not isinstance(interrupt_info, Mapping):
|
|
211
|
+
interrupt_info = {}
|
|
212
|
+
request_id = str(
|
|
213
|
+
interrupt_info.get("approval_request_id") or interrupt_info.get("id") or ""
|
|
214
|
+
)
|
|
215
|
+
response = responses.get(request_id)
|
|
216
|
+
response_metadata = (
|
|
217
|
+
response.get("Metadata")
|
|
218
|
+
if response is not None and isinstance(response.get("Metadata"), Mapping)
|
|
219
|
+
else {}
|
|
220
|
+
)
|
|
221
|
+
resume_input = response_metadata.get("resume_input")
|
|
222
|
+
if not isinstance(resume_input, Mapping):
|
|
223
|
+
resume_input = {}
|
|
224
|
+
status = "paused" if response is None else (
|
|
225
|
+
"approved"
|
|
226
|
+
if resume_input.get("approve") or resume_input.get("approved")
|
|
227
|
+
else "denied"
|
|
228
|
+
)
|
|
229
|
+
entry: dict[str, Any] = {
|
|
230
|
+
"SeqId": request.get("SeqId"),
|
|
231
|
+
"Type": "approval",
|
|
232
|
+
"Name": str(interrupt_info.get("tool_name") or "approval"),
|
|
233
|
+
"Status": status,
|
|
234
|
+
"ApprovalRequestId": request_id or None,
|
|
235
|
+
}
|
|
236
|
+
arguments = (
|
|
237
|
+
interrupt_info.get("arguments")
|
|
238
|
+
or interrupt_info.get("tool_args")
|
|
239
|
+
or interrupt_info.get("args")
|
|
240
|
+
)
|
|
241
|
+
if arguments is not None:
|
|
242
|
+
entry["Args"] = arguments
|
|
243
|
+
approvals.append(entry)
|
|
244
|
+
return approvals
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _event_attachments(event: Mapping[str, Any]) -> list[dict[str, Any]]:
|
|
248
|
+
metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
|
|
249
|
+
attachments: list[dict[str, Any]] = []
|
|
250
|
+
seen: set[str] = set()
|
|
251
|
+
for item in metadata.get("attachments") or []:
|
|
252
|
+
if not isinstance(item, Mapping):
|
|
253
|
+
continue
|
|
254
|
+
file_uri = str(item.get("file_uri") or item.get("fileUri") or "")
|
|
255
|
+
if not file_uri or file_uri in seen:
|
|
256
|
+
continue
|
|
257
|
+
mime = str(item.get("mime_type") or item.get("mimeType") or "")
|
|
258
|
+
attachments.append(
|
|
259
|
+
{
|
|
260
|
+
"file_uri": file_uri,
|
|
261
|
+
"name": str(item.get("display_name") or item.get("displayName") or ""),
|
|
262
|
+
"mime": mime,
|
|
263
|
+
"size": int(item.get("size_bytes") or item.get("sizeBytes") or 0),
|
|
264
|
+
"is_image": mime.startswith("image/"),
|
|
265
|
+
"url": "/agentengine/api/v1/AttachmentContent?FileUri=" + quote(file_uri, safe=""),
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
seen.add(file_uri)
|
|
269
|
+
return attachments
|
|
@@ -182,6 +182,16 @@ def estimate_text_tokens(text: str) -> int:
|
|
|
182
182
|
return max(1, cjk_tokens + math.ceil(ascii_chars / 4))
|
|
183
183
|
|
|
184
184
|
|
|
185
|
+
def get_context_window_tokens(model_metadata: Mapping[str, Any] | None = None) -> int:
|
|
186
|
+
limits = dict(DEFAULT_MODEL_LIMITS)
|
|
187
|
+
if isinstance(model_metadata, Mapping):
|
|
188
|
+
limits.update(dict(model_metadata.get("limits") or {}))
|
|
189
|
+
if model_metadata.get("context_window_tokens"):
|
|
190
|
+
limits["context_window_tokens"] = model_metadata["context_window_tokens"]
|
|
191
|
+
|
|
192
|
+
return _coerce_positive_int(limits.get("context_window_tokens")) or DEFAULT_CONTEXT_WINDOW_TOKENS
|
|
193
|
+
|
|
194
|
+
|
|
185
195
|
def get_effective_context_window_tokens(model_metadata: Mapping[str, Any] | None = None) -> int:
|
|
186
196
|
limits = dict(DEFAULT_MODEL_LIMITS)
|
|
187
197
|
if isinstance(model_metadata, Mapping):
|
|
@@ -191,7 +201,7 @@ def get_effective_context_window_tokens(model_metadata: Mapping[str, Any] | None
|
|
|
191
201
|
if model_metadata.get("max_output_tokens"):
|
|
192
202
|
limits["max_output_tokens"] = model_metadata["max_output_tokens"]
|
|
193
203
|
|
|
194
|
-
context_window =
|
|
204
|
+
context_window = get_context_window_tokens(model_metadata)
|
|
195
205
|
max_output_tokens = _coerce_positive_int(limits.get("max_output_tokens")) or DEFAULT_MAX_OUTPUT_TOKENS
|
|
196
206
|
reserved_tokens = min(max_output_tokens, AUTOCOMPACT_SUMMARY_RESERVE_TOKENS)
|
|
197
207
|
return max(1, context_window - reserved_tokens)
|
ksadk/conversations/runtime.py
CHANGED
|
@@ -534,6 +534,59 @@ def _set_conversation_output_attributes(span: Any | None, output_text: str | Non
|
|
|
534
534
|
_set_span_attribute(span, key, text)
|
|
535
535
|
|
|
536
536
|
|
|
537
|
+
def _set_conversation_usage_attributes(
|
|
538
|
+
span: Any | None,
|
|
539
|
+
usage: Mapping[str, Any] | None,
|
|
540
|
+
) -> None:
|
|
541
|
+
normalized = _normalize_usage_payload(usage)
|
|
542
|
+
if not normalized:
|
|
543
|
+
return
|
|
544
|
+
|
|
545
|
+
def _usage_int(value: Any) -> int:
|
|
546
|
+
try:
|
|
547
|
+
return int(value or 0)
|
|
548
|
+
except (TypeError, ValueError):
|
|
549
|
+
return 0
|
|
550
|
+
|
|
551
|
+
input_tokens = _usage_int(normalized.get("input_tokens"))
|
|
552
|
+
output_tokens = _usage_int(normalized.get("output_tokens"))
|
|
553
|
+
total_tokens = _usage_int(normalized.get("total_tokens") or (input_tokens + output_tokens))
|
|
554
|
+
input_details = normalized.get("input_token_details")
|
|
555
|
+
output_details = normalized.get("output_token_details")
|
|
556
|
+
cache_read_tokens = 0
|
|
557
|
+
reasoning_tokens = 0
|
|
558
|
+
if isinstance(input_details, Mapping):
|
|
559
|
+
cache_read_tokens = _usage_int(
|
|
560
|
+
input_details.get("cache_read")
|
|
561
|
+
or input_details.get("cached")
|
|
562
|
+
or input_details.get("cached_tokens")
|
|
563
|
+
)
|
|
564
|
+
if isinstance(output_details, Mapping):
|
|
565
|
+
reasoning_tokens = _usage_int(
|
|
566
|
+
output_details.get("reasoning")
|
|
567
|
+
or output_details.get("reasoning_tokens")
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
attributes = {
|
|
571
|
+
"gen_ai.usage.input_tokens": input_tokens,
|
|
572
|
+
"gen_ai.usage.output_tokens": output_tokens,
|
|
573
|
+
"gen_ai.usage.total_tokens": total_tokens,
|
|
574
|
+
"llm.usage.prompt_tokens": input_tokens,
|
|
575
|
+
"llm.usage.completion_tokens": output_tokens,
|
|
576
|
+
"llm.usage.total_tokens": total_tokens,
|
|
577
|
+
}
|
|
578
|
+
if cache_read_tokens:
|
|
579
|
+
attributes["gen_ai.usage.cache_read.input_tokens"] = cache_read_tokens
|
|
580
|
+
attributes["llm.usage.cache_read.input_tokens"] = cache_read_tokens
|
|
581
|
+
if reasoning_tokens:
|
|
582
|
+
attributes["gen_ai.usage.reasoning.output_tokens"] = reasoning_tokens
|
|
583
|
+
attributes["llm.usage.reasoning_tokens"] = reasoning_tokens
|
|
584
|
+
|
|
585
|
+
for key, value in attributes.items():
|
|
586
|
+
if value:
|
|
587
|
+
_set_span_attribute(span, key, value)
|
|
588
|
+
|
|
589
|
+
|
|
537
590
|
def _set_conversation_span_attributes(
|
|
538
591
|
span: Any,
|
|
539
592
|
*,
|
|
@@ -3862,6 +3915,7 @@ async def invoke_conversation_once(
|
|
|
3862
3915
|
(result.get("metadata") or {}).get("last_usage")
|
|
3863
3916
|
) or (result_usage if result_usage else {})
|
|
3864
3917
|
_set_conversation_output_attributes(span, output_text)
|
|
3918
|
+
_set_conversation_usage_attributes(span, result_usage)
|
|
3865
3919
|
result_agentengine_metadata = _extract_agentengine_metadata(result)
|
|
3866
3920
|
assistant_metadata: dict[str, Any] = {
|
|
3867
3921
|
**trace_metadata,
|
|
@@ -4148,6 +4202,7 @@ async def _iter_conversation_turn_events(
|
|
|
4148
4202
|
)
|
|
4149
4203
|
|
|
4150
4204
|
accumulated_text = ""
|
|
4205
|
+
accumulated_reasoning_parts: list[str] = []
|
|
4151
4206
|
emitted_anything = False
|
|
4152
4207
|
emitted_response_artifacts = False
|
|
4153
4208
|
saw_final_chunk = False
|
|
@@ -4157,6 +4212,20 @@ async def _iter_conversation_turn_events(
|
|
|
4157
4212
|
stream_usage: dict[str, Any] = {}
|
|
4158
4213
|
stream_last_usage: dict[str, Any] = {}
|
|
4159
4214
|
reasoning_disabled = _model_options_disable_reasoning(prepared.model_options)
|
|
4215
|
+
|
|
4216
|
+
async def _persist_accumulated_reasoning() -> None:
|
|
4217
|
+
if not accumulated_reasoning_parts:
|
|
4218
|
+
return
|
|
4219
|
+
reasoning = "".join(accumulated_reasoning_parts)
|
|
4220
|
+
accumulated_reasoning_parts.clear()
|
|
4221
|
+
await append_reasoning_event(
|
|
4222
|
+
session_id=prepared.session_id,
|
|
4223
|
+
author=runner_name,
|
|
4224
|
+
text=reasoning,
|
|
4225
|
+
invocation_id=prepared.invocation_id,
|
|
4226
|
+
session_service_provider=provider,
|
|
4227
|
+
)
|
|
4228
|
+
|
|
4160
4229
|
for attempt in range(2):
|
|
4161
4230
|
try:
|
|
4162
4231
|
runtime_context.history = list(prepared.history)
|
|
@@ -4237,12 +4306,8 @@ async def _iter_conversation_turn_events(
|
|
|
4237
4306
|
responses_output
|
|
4238
4307
|
):
|
|
4239
4308
|
if semantic_event.get("type") == "thinking":
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
author=runner_name,
|
|
4243
|
-
text=str(semantic_event.get("delta") or ""),
|
|
4244
|
-
invocation_id=prepared.invocation_id,
|
|
4245
|
-
session_service_provider=provider,
|
|
4309
|
+
accumulated_reasoning_parts.append(
|
|
4310
|
+
str(semantic_event.get("delta") or "")
|
|
4246
4311
|
)
|
|
4247
4312
|
emitted_anything = True
|
|
4248
4313
|
yield semantic_event
|
|
@@ -4252,13 +4317,7 @@ async def _iter_conversation_turn_events(
|
|
|
4252
4317
|
continue
|
|
4253
4318
|
delta = str(chunk.get("delta", ""))
|
|
4254
4319
|
if delta:
|
|
4255
|
-
|
|
4256
|
-
session_id=prepared.session_id,
|
|
4257
|
-
author=runner_name,
|
|
4258
|
-
text=delta,
|
|
4259
|
-
invocation_id=prepared.invocation_id,
|
|
4260
|
-
session_service_provider=provider,
|
|
4261
|
-
)
|
|
4320
|
+
accumulated_reasoning_parts.append(delta)
|
|
4262
4321
|
emitted_anything = True
|
|
4263
4322
|
emitted_response_artifacts = True
|
|
4264
4323
|
yield {"type": "thinking", "delta": delta}
|
|
@@ -4295,6 +4354,7 @@ async def _iter_conversation_turn_events(
|
|
|
4295
4354
|
session_service_provider=provider,
|
|
4296
4355
|
)
|
|
4297
4356
|
emitted_anything = True
|
|
4357
|
+
await _persist_accumulated_reasoning()
|
|
4298
4358
|
yield {
|
|
4299
4359
|
"type": "tool_call",
|
|
4300
4360
|
"name": chunk.get("tool_name"),
|
|
@@ -4391,6 +4451,7 @@ async def _iter_conversation_turn_events(
|
|
|
4391
4451
|
run_trigger=run_trigger,
|
|
4392
4452
|
)
|
|
4393
4453
|
emitted_anything = True
|
|
4454
|
+
await _persist_accumulated_reasoning()
|
|
4394
4455
|
yield {
|
|
4395
4456
|
"type": "interrupt",
|
|
4396
4457
|
"interrupt_info": approval_interrupt_info,
|
|
@@ -4448,6 +4509,7 @@ async def _iter_conversation_turn_events(
|
|
|
4448
4509
|
governance, chunk.get("tool_output", "")
|
|
4449
4510
|
)
|
|
4450
4511
|
emitted_anything = True
|
|
4512
|
+
await _persist_accumulated_reasoning()
|
|
4451
4513
|
yield {
|
|
4452
4514
|
"type": "tool_result",
|
|
4453
4515
|
"name": chunk.get("tool_name"),
|
|
@@ -4497,6 +4559,7 @@ async def _iter_conversation_turn_events(
|
|
|
4497
4559
|
stream_last_usage = _normalize_usage_payload(chunk_last) or stream_usage
|
|
4498
4560
|
break
|
|
4499
4561
|
except asyncio.CancelledError:
|
|
4562
|
+
await _persist_accumulated_reasoning()
|
|
4500
4563
|
await append_run_status_event(
|
|
4501
4564
|
session_id=prepared.session_id,
|
|
4502
4565
|
author=runner_name,
|
|
@@ -4541,6 +4604,7 @@ async def _iter_conversation_turn_events(
|
|
|
4541
4604
|
run_mode=run_mode,
|
|
4542
4605
|
run_trigger=run_trigger,
|
|
4543
4606
|
)
|
|
4607
|
+
await _persist_accumulated_reasoning()
|
|
4544
4608
|
yield {"type": "error", "message": str(circuit_exc) or "Agent 运行失败"}
|
|
4545
4609
|
return
|
|
4546
4610
|
if checkpoint:
|
|
@@ -4594,6 +4658,7 @@ async def _iter_conversation_turn_events(
|
|
|
4594
4658
|
run_mode=run_mode,
|
|
4595
4659
|
run_trigger=run_trigger,
|
|
4596
4660
|
)
|
|
4661
|
+
await _persist_accumulated_reasoning()
|
|
4597
4662
|
yield {"type": "error", "message": str(exc) or "Agent 运行失败"}
|
|
4598
4663
|
return
|
|
4599
4664
|
|
|
@@ -4628,6 +4693,7 @@ async def _iter_conversation_turn_events(
|
|
|
4628
4693
|
run_mode=run_mode,
|
|
4629
4694
|
run_trigger=run_trigger,
|
|
4630
4695
|
)
|
|
4696
|
+
await _persist_accumulated_reasoning()
|
|
4631
4697
|
_finish_span()
|
|
4632
4698
|
yield {
|
|
4633
4699
|
"type": "error",
|
|
@@ -4641,6 +4707,7 @@ async def _iter_conversation_turn_events(
|
|
|
4641
4707
|
return
|
|
4642
4708
|
_set_conversation_output_attributes(span, accumulated_text)
|
|
4643
4709
|
|
|
4710
|
+
await _persist_accumulated_reasoning()
|
|
4644
4711
|
await append_conversation_event(
|
|
4645
4712
|
session_id=prepared.session_id,
|
|
4646
4713
|
author=runner_name,
|
|
@@ -4674,6 +4741,7 @@ async def _iter_conversation_turn_events(
|
|
|
4674
4741
|
run_mode=run_mode,
|
|
4675
4742
|
run_trigger=run_trigger,
|
|
4676
4743
|
)
|
|
4744
|
+
_set_conversation_usage_attributes(span, assistant_metadata.get("usage"))
|
|
4677
4745
|
_finish_span()
|
|
4678
4746
|
yield {
|
|
4679
4747
|
"type": "completed",
|
ksadk/deployment/base.py
CHANGED
|
@@ -41,7 +41,7 @@ class NetworkConfig(BaseModel):
|
|
|
41
41
|
"""网络配置"""
|
|
42
42
|
access_type: str = "public" # public | private
|
|
43
43
|
enable_https: bool = True
|
|
44
|
-
enable_public_access: bool =
|
|
44
|
+
enable_public_access: Optional[bool] = None
|
|
45
45
|
enable_vpc_access: bool = False
|
|
46
46
|
vpc_id: str = ""
|
|
47
47
|
subnet_id: str = ""
|
|
@@ -261,22 +261,32 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
261
261
|
json.dump(payload, f, indent=2, ensure_ascii=False)
|
|
262
262
|
|
|
263
263
|
@staticmethod
|
|
264
|
-
def _serialize_network_config(
|
|
264
|
+
def _serialize_network_config(
|
|
265
|
+
target: DeployTarget, *, is_update: bool = False
|
|
266
|
+
) -> Optional[Dict[str, Any]]:
|
|
265
267
|
network = getattr(target, "network", None)
|
|
266
268
|
if network is None:
|
|
267
269
|
return None
|
|
268
270
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
public_access = getattr(network, "enable_public_access", None)
|
|
272
|
+
# 三态:None=未指定。create 默认开公网;update 未指定则不发 network 字段(保留服务端现有配置)。
|
|
273
|
+
if public_access is None:
|
|
274
|
+
public_value: Optional[bool] = None if is_update else True
|
|
275
|
+
else:
|
|
276
|
+
public_value = bool(public_access)
|
|
277
|
+
|
|
278
|
+
payload: Dict[str, Any] = {}
|
|
279
|
+
if public_value is not None:
|
|
280
|
+
payload["enable_public_access"] = public_value
|
|
281
|
+
if bool(getattr(network, "enable_vpc_access", False)):
|
|
282
|
+
payload["enable_vpc_access"] = True
|
|
273
283
|
|
|
274
284
|
for field in ("vpc_id", "subnet_id", "security_group_id", "availability_zone"):
|
|
275
285
|
value = str(getattr(network, field, "") or "").strip()
|
|
276
286
|
if value:
|
|
277
287
|
payload[field] = value
|
|
278
288
|
|
|
279
|
-
if not payload
|
|
289
|
+
if not payload:
|
|
280
290
|
return None
|
|
281
291
|
return payload
|
|
282
292
|
|
|
@@ -710,7 +720,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
710
720
|
else:
|
|
711
721
|
click.echo(f" 📦 更新环境变量: {len(env_vars)} 项 from 全局配置")
|
|
712
722
|
|
|
713
|
-
network_config = self._serialize_network_config(target)
|
|
723
|
+
network_config = self._serialize_network_config(target, is_update=True)
|
|
714
724
|
if network_config:
|
|
715
725
|
update_data["network"] = network_config
|
|
716
726
|
|
|
@@ -829,7 +839,7 @@ class ServerlessProvider(BaseDeployProvider):
|
|
|
829
839
|
if env_vars:
|
|
830
840
|
request_data["env_vars"] = env_vars
|
|
831
841
|
|
|
832
|
-
network_config = self._serialize_network_config(target)
|
|
842
|
+
network_config = self._serialize_network_config(target, is_update=False)
|
|
833
843
|
if network_config:
|
|
834
844
|
request_data["network"] = network_config
|
|
835
845
|
|