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,68 @@
|
|
|
1
|
+
"""TUI 流式渲染共享纯函数。
|
|
2
|
+
|
|
3
|
+
从 RemoteRunner.stream 的归一化 chunk 提取 delta/usage/终止信号,格式化 usage 文本。
|
|
4
|
+
被 loop.py(交互 TUI)和 cmd_invoke._invoke_once(-m 单次)共用,保证两路径渲染口径一致。
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import Any, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def clean_response(text: str) -> str:
|
|
13
|
+
"""清理 LLM 响应中的内部调试伪影。
|
|
14
|
+
|
|
15
|
+
只删内部调试残留(Tool Result 标记、python repr 片段、tool_call_id),
|
|
16
|
+
不动正常的 XML/HTML 标签(用户可能需要 <tag> 输出)。
|
|
17
|
+
"""
|
|
18
|
+
text = re.sub(r"\[Tool Result:.*?\]", "", text, flags=re.DOTALL)
|
|
19
|
+
text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
|
|
20
|
+
text = re.sub(r"name='[^']*'\s*tool_call_id='[^']*'", "", text)
|
|
21
|
+
return text.strip()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def extract_stream_delta(chunk: dict) -> tuple[str, dict | None, bool]:
|
|
25
|
+
"""从 RemoteRunner.stream 的 chunk 提取 (delta 文本, usage, 是否终止信号)。
|
|
26
|
+
|
|
27
|
+
- text/thinking 等普通 chunk:取 delta,非终止。
|
|
28
|
+
- final(chat 路径结束,output=完整文本)/ responses_output(responses 路径
|
|
29
|
+
response.completed,output=list):只取 usage,不把 output 当 delta——
|
|
30
|
+
否则末尾会把完整文本或 list 再 append 一遍(重复/乱码)。
|
|
31
|
+
"""
|
|
32
|
+
chunk_type = str(chunk.get("type") or "text")
|
|
33
|
+
if chunk_type in {"final", "responses_output"}:
|
|
34
|
+
usage = chunk.get("usage")
|
|
35
|
+
usage = dict(usage) if isinstance(usage, dict) else None
|
|
36
|
+
# terminal chunk 可能带 metadata.last_usage(上下文用量),合并进 usage
|
|
37
|
+
# 让 TUI 能显示 context 占比,不丢这条 metadata。
|
|
38
|
+
metadata = chunk.get("metadata")
|
|
39
|
+
if isinstance(metadata, dict) and isinstance(metadata.get("last_usage"), dict):
|
|
40
|
+
if usage is None:
|
|
41
|
+
usage = {}
|
|
42
|
+
usage.setdefault("last_usage", dict(metadata["last_usage"]))
|
|
43
|
+
return "", (usage or None), True
|
|
44
|
+
delta = chunk.get("delta") or ""
|
|
45
|
+
if not isinstance(delta, str):
|
|
46
|
+
delta = str(delta or "")
|
|
47
|
+
return delta, None, False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def format_usage(usage: Optional[dict[str, Any]]) -> str:
|
|
51
|
+
"""把 usage dict 格式成紧凑文本:↑输入 ↓输出 ⌀总计。
|
|
52
|
+
|
|
53
|
+
兼容 input_tokens/output_tokens(chat 路径)与 prompt_tokens/completion_tokens
|
|
54
|
+
(responses 路径)两种字段名。
|
|
55
|
+
"""
|
|
56
|
+
if not isinstance(usage, dict) or not usage:
|
|
57
|
+
return ""
|
|
58
|
+
inp = usage.get("input_tokens") or usage.get("prompt_tokens")
|
|
59
|
+
out = usage.get("output_tokens") or usage.get("completion_tokens")
|
|
60
|
+
total = usage.get("total_tokens")
|
|
61
|
+
parts = []
|
|
62
|
+
if inp:
|
|
63
|
+
parts.append(f"↑{inp}")
|
|
64
|
+
if out:
|
|
65
|
+
parts.append(f"↓{out}")
|
|
66
|
+
if total:
|
|
67
|
+
parts.append(f"⌀{total}")
|
|
68
|
+
return " ".join(parts)
|
ksadk/version.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{x as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createArchitectureServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Dn as e,En as t}from"./MermaidBlock-DCR8WZ17.js";var n=(n,r)=>e.lang.round(t.parse(n)[r]);export{n as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{kn as e}from"./MermaidBlock-DCR8WZ17.js";var t=class{constructor(e){this.init=e,this.records=this.init()}static{e(this,`ImperativeState`)}reset(){this.records=this.init()}};export{t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-FMBD7UC4-PrapyfMu.js";import"./chunk-ND2GUHAM-CboKhTZ3.js";import"./chunk-55IACEB6-DOairxzw.js";import"./chunk-2J33WTMH-DLVdkeyQ.js";import{i as e,n as t,r as n,t as r}from"./chunk-727SXJPM-hCO487gs.js";import{kn as i}from"./MermaidBlock-DCR8WZ17.js";var a={parser:t,get db(){return new r},renderer:n,styles:e,init:i(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-FMBD7UC4-PrapyfMu.js";import"./chunk-ND2GUHAM-CboKhTZ3.js";import"./chunk-55IACEB6-DOairxzw.js";import"./chunk-2J33WTMH-DLVdkeyQ.js";import{i as e,n as t,r as n,t as r}from"./chunk-727SXJPM-hCO487gs.js";import{kn as i}from"./MermaidBlock-DCR8WZ17.js";var a={parser:t,get db(){return new r},renderer:n,styles:e,init:i(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{y as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createEventModelingServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createGitGraphServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{h as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createInfoServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{p as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createPacketServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createPieServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{l as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createRadarServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./chunk-55IACEB6-DOairxzw.js";import"./chunk-2J33WTMH-DLVdkeyQ.js";import{i as e,n as t,r as n,t as r}from"./chunk-AQP2D5EJ-XU8vDOuG.js";import{kn as i}from"./MermaidBlock-DCR8WZ17.js";var a={parser:t,get db(){return new r(2)},renderer:n,styles:e,init:i(e=>{e.state||={},e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createTreeViewServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createTreemapServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./mermaid-parser.core-D2eMVOgo.js";export{e as createWardleyServices};
|
ksadk/tui/app.py
DELETED
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
KsADK Agent TUI - 简洁交互界面
|
|
3
|
-
|
|
4
|
-
基本功能:
|
|
5
|
-
- 对话交互
|
|
6
|
-
- 思考过程显示(通过 --show-thinking 参数控制)
|
|
7
|
-
- 工具调用显示
|
|
8
|
-
- 退出:输入 exit/quit 或 Ctrl+C/Ctrl+D
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from __future__ import annotations
|
|
12
|
-
|
|
13
|
-
import asyncio
|
|
14
|
-
import os
|
|
15
|
-
import uuid
|
|
16
|
-
import re
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
19
|
-
|
|
20
|
-
from textual import on
|
|
21
|
-
from textual.app import App, ComposeResult
|
|
22
|
-
from textual.binding import Binding
|
|
23
|
-
from textual.containers import Container, Vertical, VerticalScroll
|
|
24
|
-
from textual.screen import ModalScreen
|
|
25
|
-
from textual.widgets import Static
|
|
26
|
-
from textual.events import MouseUp
|
|
27
|
-
|
|
28
|
-
from rich.panel import Panel
|
|
29
|
-
|
|
30
|
-
from ksadk.tui.clipboard import copy_selection_to_clipboard
|
|
31
|
-
from ksadk.tui.widgets.user import UserMessage
|
|
32
|
-
from ksadk.tui.widgets.assistant import AssistantMessage
|
|
33
|
-
from ksadk.tui.widgets.thinking import ThinkingMessage
|
|
34
|
-
from ksadk.tui.widgets.system import SystemMessage
|
|
35
|
-
from ksadk.tui.widgets.chat_input import ChatInput
|
|
36
|
-
|
|
37
|
-
if TYPE_CHECKING:
|
|
38
|
-
from ksadk.runners.base_runner import BaseRunner
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _clean_response(text: str) -> str:
|
|
42
|
-
"""清理 LLM 响应中的内部调试信息"""
|
|
43
|
-
text = re.sub(r'\[Tool Result:.*?\]', '', text, flags=re.DOTALL)
|
|
44
|
-
text = re.sub(r'<tool_call>.*?</tool_call>', '', text, flags=re.DOTALL)
|
|
45
|
-
text = re.sub(r"name='[^']*'\s*tool_call_id='[^']*'", '', text)
|
|
46
|
-
return text.strip()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class ApprovalScreen(ModalScreen[bool]):
|
|
50
|
-
"""敏感操作确认弹窗"""
|
|
51
|
-
|
|
52
|
-
BINDINGS = [
|
|
53
|
-
Binding("y", "approve", "确认"),
|
|
54
|
-
Binding("n", "reject", "取消"),
|
|
55
|
-
Binding("escape", "reject", "取消"),
|
|
56
|
-
]
|
|
57
|
-
|
|
58
|
-
def __init__(self, tool_name: str, args: Dict[str, Any], **kwargs):
|
|
59
|
-
super().__init__(**kwargs)
|
|
60
|
-
self.tool_name = tool_name
|
|
61
|
-
self.tool_args = args
|
|
62
|
-
|
|
63
|
-
def compose(self) -> ComposeResult:
|
|
64
|
-
args_str = "\n".join(f" {k}: {v}" for k, v in self.tool_args.items()) if self.tool_args else " (无参数)"
|
|
65
|
-
yield Container(
|
|
66
|
-
Static(
|
|
67
|
-
Panel(
|
|
68
|
-
f"[bold yellow]⚠️ 需要您确认敏感操作[/]\n\n"
|
|
69
|
-
f"[bold]操作:[/] {self.tool_name}\n"
|
|
70
|
-
f"[bold]参数:[/]\n{args_str}\n\n"
|
|
71
|
-
f"[green]y[/] 确认 [red]n[/] 取消",
|
|
72
|
-
title="🔒 确认",
|
|
73
|
-
border_style="yellow",
|
|
74
|
-
),
|
|
75
|
-
),
|
|
76
|
-
id="approval-dialog",
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
def action_approve(self) -> None:
|
|
80
|
-
self.dismiss(True)
|
|
81
|
-
|
|
82
|
-
def action_reject(self) -> None:
|
|
83
|
-
self.dismiss(False)
|
|
84
|
-
|
|
85
|
-
CSS = """
|
|
86
|
-
#approval-dialog {
|
|
87
|
-
align: center middle;
|
|
88
|
-
width: 60;
|
|
89
|
-
height: auto;
|
|
90
|
-
}
|
|
91
|
-
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
class AgentTUI(App):
|
|
95
|
-
"""KsADK Agent TUI - 简洁交互界面"""
|
|
96
|
-
|
|
97
|
-
CSS = """
|
|
98
|
-
Screen {
|
|
99
|
-
background: $background;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
#main-scroll {
|
|
103
|
-
height: 100%;
|
|
104
|
-
padding: 0 1;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
#title-bar {
|
|
108
|
-
height: 1;
|
|
109
|
-
color: $primary;
|
|
110
|
-
padding: 0;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
#welcome-area {
|
|
114
|
-
height: auto;
|
|
115
|
-
padding: 2 0;
|
|
116
|
-
content-align: center middle;
|
|
117
|
-
text-align: center;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
#chat-log {
|
|
121
|
-
height: auto;
|
|
122
|
-
background: transparent;
|
|
123
|
-
padding: 0;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
#input-area {
|
|
127
|
-
height: auto;
|
|
128
|
-
padding: 1 0;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
#hint {
|
|
132
|
-
height: 1;
|
|
133
|
-
color: $text-muted;
|
|
134
|
-
padding: 0 0 0 2;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.started #welcome-area {
|
|
138
|
-
display: none;
|
|
139
|
-
}
|
|
140
|
-
"""
|
|
141
|
-
|
|
142
|
-
BINDINGS = [
|
|
143
|
-
Binding("escape", "interrupt", "中断", show=False, priority=True),
|
|
144
|
-
Binding("ctrl+c", "quit_or_interrupt", "退出/中断", show=False),
|
|
145
|
-
Binding("ctrl+d", "quit_app", "退出", show=False, priority=True),
|
|
146
|
-
Binding("ctrl+q", "quit_app", "退出", show=False),
|
|
147
|
-
]
|
|
148
|
-
|
|
149
|
-
TITLE = "KsADK"
|
|
150
|
-
|
|
151
|
-
def __init__(
|
|
152
|
-
self,
|
|
153
|
-
runner: "BaseRunner",
|
|
154
|
-
show_thinking: bool = False,
|
|
155
|
-
project_dir: str = ".",
|
|
156
|
-
**kwargs,
|
|
157
|
-
):
|
|
158
|
-
super().__init__(**kwargs)
|
|
159
|
-
self.runner = runner
|
|
160
|
-
self.show_thinking = show_thinking
|
|
161
|
-
self.project_dir = Path(project_dir).resolve()
|
|
162
|
-
|
|
163
|
-
self.session_id = getattr(runner, "session_id", None) or str(uuid.uuid4())[:8]
|
|
164
|
-
if getattr(self.runner, "session_id", None) is None:
|
|
165
|
-
self.runner.session_id = self.session_id
|
|
166
|
-
self.history: List[Dict[str, str]] = []
|
|
167
|
-
self._is_streaming = False
|
|
168
|
-
self._started = False
|
|
169
|
-
self.model_name = os.getenv("MODEL_NAME", "unknown")
|
|
170
|
-
|
|
171
|
-
try:
|
|
172
|
-
from ksadk import __version__
|
|
173
|
-
self.version = __version__
|
|
174
|
-
except ImportError:
|
|
175
|
-
self.version = "0.2.0"
|
|
176
|
-
|
|
177
|
-
def compose(self) -> ComposeResult:
|
|
178
|
-
"""构建 UI 布局"""
|
|
179
|
-
with VerticalScroll(id="main-scroll"):
|
|
180
|
-
yield Static(f"─ KsADK v{self.version} ─", id="title-bar")
|
|
181
|
-
|
|
182
|
-
yield Container(
|
|
183
|
-
Static(
|
|
184
|
-
f"[bold]Welcome![/]\n"
|
|
185
|
-
f"🤖\n"
|
|
186
|
-
f"[dim]{self.model_name} · Interactive Mode[/]\n"
|
|
187
|
-
f"[dim]{self._short_path()}[/]",
|
|
188
|
-
id="welcome-content",
|
|
189
|
-
),
|
|
190
|
-
id="welcome-area",
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
with Vertical(id="chat-log"):
|
|
194
|
-
pass
|
|
195
|
-
|
|
196
|
-
yield Container(
|
|
197
|
-
ChatInput(cwd=self.project_dir, id="chat-input-widget"),
|
|
198
|
-
Static("Ctrl+C 退出", id="hint"),
|
|
199
|
-
id="input-area",
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
def _short_path(self) -> str:
|
|
203
|
-
home = Path.home()
|
|
204
|
-
try:
|
|
205
|
-
return "~/" + str(self.project_dir.relative_to(home))
|
|
206
|
-
except ValueError:
|
|
207
|
-
return str(self.project_dir)
|
|
208
|
-
|
|
209
|
-
def on_mount(self) -> None:
|
|
210
|
-
self.query_one("#chat-input-widget", ChatInput).focus_input()
|
|
211
|
-
|
|
212
|
-
@on(ChatInput.Submitted)
|
|
213
|
-
async def handle_input(self, event: ChatInput.Submitted) -> None:
|
|
214
|
-
"""处理用户输入"""
|
|
215
|
-
user_input = event.value.strip()
|
|
216
|
-
|
|
217
|
-
if not user_input:
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
# 检查退出
|
|
221
|
-
if user_input.lower() in ("exit", "quit", "退出"):
|
|
222
|
-
self.exit()
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
# 隐藏欢迎区域
|
|
226
|
-
if not self._started:
|
|
227
|
-
self._started = True
|
|
228
|
-
self.add_class("started")
|
|
229
|
-
|
|
230
|
-
asyncio.create_task(self._stream_response(user_input))
|
|
231
|
-
|
|
232
|
-
async def _stream_response(self, user_input: str) -> None:
|
|
233
|
-
"""流式获取 Agent 响应"""
|
|
234
|
-
chat_log = self.query_one("#chat-log", Vertical)
|
|
235
|
-
main_scroll = self.query_one("#main-scroll", VerticalScroll)
|
|
236
|
-
|
|
237
|
-
self._is_streaming = True
|
|
238
|
-
|
|
239
|
-
input_data = {
|
|
240
|
-
"input": user_input,
|
|
241
|
-
"session_id": self.session_id,
|
|
242
|
-
"history": self.history,
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
thinking_msg: Optional[ThinkingMessage] = None
|
|
246
|
-
assistant_msg: Optional[AssistantMessage] = None
|
|
247
|
-
full_response_text = ""
|
|
248
|
-
|
|
249
|
-
try:
|
|
250
|
-
await chat_log.mount(UserMessage(user_input))
|
|
251
|
-
main_scroll.scroll_end(animate=False)
|
|
252
|
-
|
|
253
|
-
async for chunk in self.runner.stream(input_data):
|
|
254
|
-
if not self._is_streaming:
|
|
255
|
-
if thinking_msg:
|
|
256
|
-
thinking_msg.stop()
|
|
257
|
-
if assistant_msg:
|
|
258
|
-
await assistant_msg.stop_stream()
|
|
259
|
-
await chat_log.mount(SystemMessage("已中断", level="warning"))
|
|
260
|
-
break
|
|
261
|
-
|
|
262
|
-
chunk_type = chunk.get("type", "text")
|
|
263
|
-
|
|
264
|
-
# 思考过程
|
|
265
|
-
if chunk_type == "thinking":
|
|
266
|
-
delta = chunk.get("delta", "")
|
|
267
|
-
if delta and self.show_thinking:
|
|
268
|
-
if not thinking_msg:
|
|
269
|
-
thinking_msg = ThinkingMessage()
|
|
270
|
-
await chat_log.mount(thinking_msg)
|
|
271
|
-
main_scroll.scroll_end(animate=False)
|
|
272
|
-
await thinking_msg.append_content(delta)
|
|
273
|
-
continue
|
|
274
|
-
|
|
275
|
-
if thinking_msg and thinking_msg.is_active:
|
|
276
|
-
thinking_msg.stop()
|
|
277
|
-
|
|
278
|
-
# 中断/确认
|
|
279
|
-
if chunk_type == "interrupt":
|
|
280
|
-
await self._handle_interrupt(chunk)
|
|
281
|
-
return
|
|
282
|
-
|
|
283
|
-
# 工具调用 - 跳过不显示
|
|
284
|
-
if chunk_type == "tool_call":
|
|
285
|
-
continue
|
|
286
|
-
|
|
287
|
-
# 文本响应
|
|
288
|
-
delta = chunk.get("output", "") or chunk.get("delta", "")
|
|
289
|
-
if delta:
|
|
290
|
-
if not assistant_msg:
|
|
291
|
-
assistant_msg = AssistantMessage()
|
|
292
|
-
await chat_log.mount(assistant_msg)
|
|
293
|
-
main_scroll.scroll_end(animate=False)
|
|
294
|
-
await assistant_msg.append_content(delta)
|
|
295
|
-
full_response_text += delta
|
|
296
|
-
|
|
297
|
-
if thinking_msg:
|
|
298
|
-
thinking_msg.stop()
|
|
299
|
-
if assistant_msg:
|
|
300
|
-
await assistant_msg.stop_stream()
|
|
301
|
-
|
|
302
|
-
cleaned_response = _clean_response(full_response_text)
|
|
303
|
-
if cleaned_response:
|
|
304
|
-
self.history.append({"role": "user", "content": user_input})
|
|
305
|
-
self.history.append({"role": "model", "content": cleaned_response})
|
|
306
|
-
elif self._is_streaming and not full_response_text:
|
|
307
|
-
result = await self.runner.invoke(input_data)
|
|
308
|
-
response_text = result.get("output", "")
|
|
309
|
-
cleaned_response = _clean_response(response_text)
|
|
310
|
-
|
|
311
|
-
if cleaned_response:
|
|
312
|
-
assistant_msg = AssistantMessage()
|
|
313
|
-
await chat_log.mount(assistant_msg)
|
|
314
|
-
await assistant_msg.append_content(cleaned_response)
|
|
315
|
-
await assistant_msg.stop_stream()
|
|
316
|
-
self.history.append({"role": "user", "content": user_input})
|
|
317
|
-
self.history.append({"role": "model", "content": cleaned_response})
|
|
318
|
-
else:
|
|
319
|
-
await chat_log.mount(SystemMessage("(无响应)", level="warning"))
|
|
320
|
-
|
|
321
|
-
except asyncio.CancelledError:
|
|
322
|
-
await chat_log.mount(SystemMessage("已取消", level="warning"))
|
|
323
|
-
except Exception as e:
|
|
324
|
-
await chat_log.mount(SystemMessage(f"错误: {e}", level="error"))
|
|
325
|
-
finally:
|
|
326
|
-
self._is_streaming = False
|
|
327
|
-
main_scroll.scroll_end(animate=False)
|
|
328
|
-
|
|
329
|
-
async def _handle_interrupt(self, interrupt_chunk: Dict[str, Any]) -> None:
|
|
330
|
-
"""处理敏感操作确认"""
|
|
331
|
-
interrupt_info = interrupt_chunk.get("interrupt_info", {})
|
|
332
|
-
|
|
333
|
-
if isinstance(interrupt_info, dict):
|
|
334
|
-
tool_name = interrupt_info.get("tool", "未知操作")
|
|
335
|
-
args = interrupt_info.get("args", {})
|
|
336
|
-
elif isinstance(interrupt_info, list) and interrupt_info:
|
|
337
|
-
first = interrupt_info[0]
|
|
338
|
-
tool_name = str(getattr(first, "value", first))
|
|
339
|
-
args = {}
|
|
340
|
-
else:
|
|
341
|
-
tool_name = str(interrupt_info)
|
|
342
|
-
args = {}
|
|
343
|
-
|
|
344
|
-
approved = await self.push_screen_wait(ApprovalScreen(tool_name, args))
|
|
345
|
-
chat_log = self.query_one("#chat-log", Vertical)
|
|
346
|
-
|
|
347
|
-
if approved:
|
|
348
|
-
await chat_log.mount(SystemMessage("✓ 已确认,继续执行...", level="success"))
|
|
349
|
-
|
|
350
|
-
resume_data = {
|
|
351
|
-
"input": "确认",
|
|
352
|
-
"session_id": interrupt_chunk.get("session_id", self.session_id),
|
|
353
|
-
"history": self.history,
|
|
354
|
-
"resume": True,
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
assistant_msg = AssistantMessage()
|
|
358
|
-
await chat_log.mount(assistant_msg)
|
|
359
|
-
|
|
360
|
-
try:
|
|
361
|
-
async for chunk in self.runner.stream(resume_data):
|
|
362
|
-
delta = chunk.get("output", "") or chunk.get("delta", "")
|
|
363
|
-
if delta:
|
|
364
|
-
await assistant_msg.append_content(delta)
|
|
365
|
-
await assistant_msg.stop_stream()
|
|
366
|
-
if assistant_msg.content:
|
|
367
|
-
self.history.append({"role": "model", "content": assistant_msg.content})
|
|
368
|
-
except Exception as e:
|
|
369
|
-
await chat_log.mount(SystemMessage(f"错误: {e}", level="error"))
|
|
370
|
-
else:
|
|
371
|
-
await chat_log.mount(SystemMessage("✗ 已取消操作", level="warning"))
|
|
372
|
-
|
|
373
|
-
def action_interrupt(self) -> None:
|
|
374
|
-
self._is_streaming = False
|
|
375
|
-
|
|
376
|
-
def action_quit_or_interrupt(self) -> None:
|
|
377
|
-
if self._is_streaming:
|
|
378
|
-
self.action_interrupt()
|
|
379
|
-
else:
|
|
380
|
-
self.exit()
|
|
381
|
-
|
|
382
|
-
def action_quit_app(self) -> None:
|
|
383
|
-
self.exit()
|
|
384
|
-
|
|
385
|
-
def on_mouse_up(self, event: MouseUp) -> None:
|
|
386
|
-
copy_selection_to_clipboard(self)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
def run_tui(
|
|
390
|
-
runner: "BaseRunner",
|
|
391
|
-
show_thinking: bool = False,
|
|
392
|
-
project_dir: str = ".",
|
|
393
|
-
) -> None:
|
|
394
|
-
"""启动 TUI 应用"""
|
|
395
|
-
app = AgentTUI(
|
|
396
|
-
runner=runner,
|
|
397
|
-
show_thinking=show_thinking,
|
|
398
|
-
project_dir=project_dir,
|
|
399
|
-
)
|
|
400
|
-
app.run()
|
ksadk/tui/clipboard.py
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"""剪贴板工具 - 支持鼠标选择复制"""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import base64
|
|
6
|
-
import os
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from textual.app import App
|
|
11
|
-
|
|
12
|
-
_PREVIEW_MAX_LENGTH = 40
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _copy_osc52(text: str) -> None:
|
|
16
|
-
"""使用 OSC 52 转义序列复制(支持 SSH/tmux)"""
|
|
17
|
-
encoded = base64.b64encode(text.encode("utf-8")).decode("ascii")
|
|
18
|
-
osc52_seq = f"\033]52;c;{encoded}\a"
|
|
19
|
-
if os.environ.get("TMUX"):
|
|
20
|
-
osc52_seq = f"\033Ptmux;\033{osc52_seq}\033\\"
|
|
21
|
-
|
|
22
|
-
try:
|
|
23
|
-
with open("/dev/tty", "w") as tty:
|
|
24
|
-
tty.write(osc52_seq)
|
|
25
|
-
tty.flush()
|
|
26
|
-
except Exception:
|
|
27
|
-
raise RuntimeError("OSC52 复制失败")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _shorten_preview(texts: list[str]) -> str:
|
|
31
|
-
"""缩短文本预览"""
|
|
32
|
-
dense_text = "⏎".join(texts).replace("\n", "⏎")
|
|
33
|
-
if len(dense_text) > _PREVIEW_MAX_LENGTH:
|
|
34
|
-
return f"{dense_text[: _PREVIEW_MAX_LENGTH - 1]}…"
|
|
35
|
-
return dense_text
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _clipboard_copy_methods(app: App):
|
|
39
|
-
copy_methods = []
|
|
40
|
-
if os.name != "nt":
|
|
41
|
-
copy_methods.append(_copy_osc52)
|
|
42
|
-
|
|
43
|
-
try:
|
|
44
|
-
import pyperclip
|
|
45
|
-
copy_methods.append(pyperclip.copy)
|
|
46
|
-
except ImportError:
|
|
47
|
-
pass
|
|
48
|
-
|
|
49
|
-
copy_methods.append(app.copy_to_clipboard)
|
|
50
|
-
return copy_methods
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def copy_selection_to_clipboard(app: App) -> None:
|
|
54
|
-
"""复制选中文本到剪贴板
|
|
55
|
-
|
|
56
|
-
遍历所有 widgets 获取选中文本并复制到系统剪贴板
|
|
57
|
-
"""
|
|
58
|
-
selected_texts = []
|
|
59
|
-
|
|
60
|
-
for widget in app.query("*"):
|
|
61
|
-
if not hasattr(widget, "text_selection") or not widget.text_selection:
|
|
62
|
-
continue
|
|
63
|
-
|
|
64
|
-
selection = widget.text_selection
|
|
65
|
-
|
|
66
|
-
try:
|
|
67
|
-
result = widget.get_selection(selection)
|
|
68
|
-
except Exception:
|
|
69
|
-
continue
|
|
70
|
-
|
|
71
|
-
if not result:
|
|
72
|
-
continue
|
|
73
|
-
|
|
74
|
-
selected_text, _ = result
|
|
75
|
-
if selected_text.strip():
|
|
76
|
-
selected_texts.append(selected_text)
|
|
77
|
-
|
|
78
|
-
if not selected_texts:
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
combined_text = "\n".join(selected_texts)
|
|
82
|
-
|
|
83
|
-
for copy_fn in _clipboard_copy_methods(app):
|
|
84
|
-
try:
|
|
85
|
-
copy_fn(combined_text)
|
|
86
|
-
app.notify(
|
|
87
|
-
f'"{_shorten_preview(selected_texts)}" 已复制',
|
|
88
|
-
severity="information",
|
|
89
|
-
timeout=2,
|
|
90
|
-
)
|
|
91
|
-
return
|
|
92
|
-
except Exception:
|
|
93
|
-
continue
|
|
94
|
-
|
|
95
|
-
# 所有方式都失败
|
|
96
|
-
app.notify(
|
|
97
|
-
"复制失败 - 剪贴板不可用",
|
|
98
|
-
severity="warning",
|
|
99
|
-
timeout=3,
|
|
100
|
-
)
|
ksadk/tui/widgets/__init__.py
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
TUI Widgets - 消息组件
|
|
3
|
-
|
|
4
|
-
提供各类消息组件,用于 TUI 渲染。
|
|
5
|
-
参考 deepagents-cli 的消息组件设计。
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from .base import MessageWidget
|
|
9
|
-
from .user import UserMessage
|
|
10
|
-
from .assistant import AssistantMessage
|
|
11
|
-
from .thinking import ThinkingMessage
|
|
12
|
-
from .tool_call import ToolCallMessage
|
|
13
|
-
from .system import SystemMessage
|
|
14
|
-
|
|
15
|
-
__all__ = [
|
|
16
|
-
"MessageWidget",
|
|
17
|
-
"UserMessage",
|
|
18
|
-
"AssistantMessage",
|
|
19
|
-
"ThinkingMessage",
|
|
20
|
-
"ToolCallMessage",
|
|
21
|
-
"SystemMessage",
|
|
22
|
-
]
|