agentengine-sdk-python 0.6.2__py3-none-any.whl → 0.6.9__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.
Files changed (490) hide show
  1. agentengine_sdk_python-0.6.9.dist-info/METADATA +179 -0
  2. agentengine_sdk_python-0.6.9.dist-info/RECORD +487 -0
  3. {agentengine_sdk_python-0.6.2.dist-info → agentengine_sdk_python-0.6.9.dist-info}/WHEEL +1 -1
  4. agentengine_sdk_python-0.6.9.dist-info/entry_points.txt +3 -0
  5. agentengine_sdk_python-0.6.9.dist-info/licenses/LICENSE +175 -0
  6. agentengine_sdk_python-0.6.9.dist-info/top_level.txt +2 -0
  7. ksadk/__init__.py +13 -0
  8. ksadk/__main__.py +4 -0
  9. ksadk/a2a/__init__.py +15 -0
  10. ksadk/a2a/card_builder.py +64 -0
  11. ksadk/a2a/client.py +309 -0
  12. ksadk/a2a/executor.py +201 -0
  13. ksadk/a2a/server.py +79 -0
  14. ksadk/agents/__init__.py +20 -0
  15. ksadk/agents/base.py +370 -0
  16. ksadk/agents/context.py +38 -0
  17. ksadk/agents/event.py +35 -0
  18. ksadk/agents/loop_agent.py +71 -0
  19. ksadk/agents/parallel_agent.py +109 -0
  20. ksadk/agents/runner_adapter.py +24 -0
  21. ksadk/agents/sequential_agent.py +34 -0
  22. ksadk/api/__init__.py +3 -0
  23. ksadk/api/client.py +2227 -0
  24. ksadk/builders/__init__.py +20 -0
  25. ksadk/builders/base.py +66 -0
  26. ksadk/builders/code_builder.py +1990 -0
  27. ksadk/builders/container_builder.py +786 -0
  28. ksadk/builders/framework_requirements.py +59 -0
  29. ksadk/builders/ks3_uploader.py +389 -0
  30. ksadk/builders/mcp_builder.py +476 -0
  31. ksadk/builders/requirements_utils.py +75 -0
  32. ksadk/cli/__init__.py +531 -0
  33. ksadk/cli/__main__.py +4 -0
  34. ksadk/cli/agent_ref.py +204 -0
  35. ksadk/cli/cmd_a2a.py +167 -0
  36. ksadk/cli/cmd_agent.py +201 -0
  37. ksadk/cli/cmd_build.py +270 -0
  38. ksadk/cli/cmd_completion.py +304 -0
  39. ksadk/cli/cmd_config.py +758 -0
  40. ksadk/cli/cmd_create.py +2277 -0
  41. ksadk/cli/cmd_dashboard.py +900 -0
  42. ksadk/cli/cmd_deploy.py +704 -0
  43. ksadk/cli/cmd_destroy.py +345 -0
  44. ksadk/cli/cmd_files.py +1227 -0
  45. ksadk/cli/cmd_hermes.py +1259 -0
  46. ksadk/cli/cmd_invoke.py +1336 -0
  47. ksadk/cli/cmd_launch.py +564 -0
  48. ksadk/cli/cmd_mcp.py +1174 -0
  49. ksadk/cli/cmd_model.py +302 -0
  50. ksadk/cli/cmd_openclaw.py +3918 -0
  51. ksadk/cli/cmd_run.py +232 -0
  52. ksadk/cli/cmd_status.py +588 -0
  53. ksadk/cli/cmd_version.py +522 -0
  54. ksadk/cli/cmd_web.py +288 -0
  55. ksadk/cli/deploy_utils.py +87 -0
  56. ksadk/cli/dry_run.py +214 -0
  57. ksadk/cli/env_options.py +82 -0
  58. ksadk/cli/error_utils.py +525 -0
  59. ksadk/cli/global_options.py +31 -0
  60. ksadk/cli/local_runtime.py +97 -0
  61. ksadk/cli/model_catalog.py +136 -0
  62. ksadk/cli/network_options.py +410 -0
  63. ksadk/cli/resource_common.py +621 -0
  64. ksadk/cli/storage.py +84 -0
  65. ksadk/cli/ui.py +431 -0
  66. ksadk/cli/workflow_common.py +552 -0
  67. ksadk/common/__init__.py +14 -0
  68. ksadk/common/aicp_env.py +128 -0
  69. ksadk/common/auth.py +190 -0
  70. ksadk/common/constants.py +84 -0
  71. ksadk/common/llm_utils.py +61 -0
  72. ksadk/configs/__init__.py +61 -0
  73. ksadk/configs/env_registry.py +342 -0
  74. ksadk/configs/global_config.py +200 -0
  75. ksadk/configs/settings.py +706 -0
  76. ksadk/conversations/__init__.py +95 -0
  77. ksadk/conversations/attachment_storage.py +379 -0
  78. ksadk/conversations/attachments.py +774 -0
  79. ksadk/conversations/compaction_pipeline.py +387 -0
  80. ksadk/conversations/compaction_prompt.py +128 -0
  81. ksadk/conversations/context.py +303 -0
  82. ksadk/conversations/model_context.py +360 -0
  83. ksadk/conversations/model_options.py +136 -0
  84. ksadk/conversations/normalize.py +446 -0
  85. ksadk/conversations/reasoning_markup.py +91 -0
  86. ksadk/conversations/run_kinds.py +61 -0
  87. ksadk/conversations/run_status.py +53 -0
  88. ksadk/conversations/runtime.py +5208 -0
  89. ksadk/conversations/semantic_summary.py +359 -0
  90. ksadk/conversations/session_title.py +254 -0
  91. ksadk/deployment/__init__.py +83 -0
  92. ksadk/deployment/agent_access.py +226 -0
  93. ksadk/deployment/base.py +229 -0
  94. ksadk/deployment/manager.py +332 -0
  95. ksadk/deployment/providers/__init__.py +10 -0
  96. ksadk/deployment/providers/serverless.py +1105 -0
  97. ksadk/deployment/registry.py +120 -0
  98. ksadk/deployment/state.py +78 -0
  99. ksadk/deployment/ui_config.py +21 -0
  100. ksadk/detection/__init__.py +7 -0
  101. ksadk/detection/detector.py +431 -0
  102. ksadk/detection/mcp_detector.py +250 -0
  103. ksadk/hermes_terminal.py +467 -0
  104. ksadk/identity/__init__.py +17 -0
  105. ksadk/identity/resolver.py +385 -0
  106. ksadk/knowledge_base/__init__.py +62 -0
  107. ksadk/knowledge_base/adk_tool.py +47 -0
  108. ksadk/knowledge_base/client.py +297 -0
  109. ksadk/knowledge_base/langchain_tool.py +55 -0
  110. ksadk/knowledge_base/service.py +66 -0
  111. ksadk/knowledge_base/tool.py +50 -0
  112. ksadk/markdown.py +169 -0
  113. ksadk/mcp_runtime/__init__.py +231 -0
  114. ksadk/memory/__init__.py +57 -0
  115. ksadk/memory/adk/__init__.py +51 -0
  116. ksadk/memory/adk/backends/__init__.py +5 -0
  117. ksadk/memory/adk/backends/base_ltm_backend.py +52 -0
  118. ksadk/memory/adk/backends/http_ltm_backend.py +183 -0
  119. ksadk/memory/adk/backends/inmemory_ltm_backend.py +96 -0
  120. ksadk/memory/adk/backends/sdk_ltm_backend.py +456 -0
  121. ksadk/memory/adk/long_term_memory.py +273 -0
  122. ksadk/memory/adk/short_term_memory.py +289 -0
  123. ksadk/memory/adk_tool.py +31 -0
  124. ksadk/memory/backends/__init__.py +6 -0
  125. ksadk/memory/backends/base.py +103 -0
  126. ksadk/memory/backends/memory.py +112 -0
  127. ksadk/memory/backends/redis.py +146 -0
  128. ksadk/memory/langchain_tool.py +39 -0
  129. ksadk/memory/ltm_backend_factory.py +24 -0
  130. ksadk/memory/manager.py +215 -0
  131. ksadk/memory/service.py +172 -0
  132. ksadk/memory/tool.py +96 -0
  133. ksadk/model_policy.py +223 -0
  134. ksadk/openclaw_gateway.py +378 -0
  135. ksadk/runners/__init__.py +8 -0
  136. ksadk/runners/adk_runner.py +1135 -0
  137. ksadk/runners/base_runner.py +305 -0
  138. ksadk/runners/deepagents_runner.py +13 -0
  139. ksadk/runners/factory.py +73 -0
  140. ksadk/runners/langchain_runner.py +539 -0
  141. ksadk/runners/langgraph_runner.py +852 -0
  142. ksadk/runners/patch_langchain.py +187 -0
  143. ksadk/runners/remote_runner.py +667 -0
  144. ksadk/runners/usage_accumulator.py +38 -0
  145. ksadk/runners/utils/__init__.py +19 -0
  146. ksadk/runners/utils/langfuse.py +230 -0
  147. ksadk/runners/utils/loader.py +62 -0
  148. ksadk/runtime_context.py +166 -0
  149. ksadk/runtime_state.py +25 -0
  150. ksadk/sandbox/__init__.py +28 -0
  151. ksadk/sandbox/backends/__init__.py +9 -0
  152. ksadk/sandbox/backends/e2b.py +154 -0
  153. ksadk/sandbox/backends/local_process.py +131 -0
  154. ksadk/sandbox/base.py +101 -0
  155. ksadk/sandbox/factory.py +53 -0
  156. ksadk/sandbox/registry.py +187 -0
  157. ksadk/server/__init__.py +7 -0
  158. ksadk/server/api_models.py +73 -0
  159. ksadk/server/app.py +3789 -0
  160. ksadk/server/static/assets/ArtifactsPanel-C0meuOdi.js +1 -0
  161. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +9 -0
  162. ksadk/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  163. ksadk/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  164. ksadk/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  165. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  166. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  167. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  168. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  169. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  170. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  171. ksadk/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  172. ksadk/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  173. ksadk/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  174. ksadk/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  175. ksadk/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  176. ksadk/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  177. ksadk/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  178. ksadk/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  179. ksadk/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  180. ksadk/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  181. ksadk/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  182. ksadk/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  183. ksadk/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  184. ksadk/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  185. ksadk/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  186. ksadk/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  187. ksadk/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  188. ksadk/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  189. ksadk/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  190. ksadk/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  191. ksadk/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  192. ksadk/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  193. ksadk/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  194. ksadk/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  195. ksadk/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  196. ksadk/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  197. ksadk/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  198. ksadk/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  199. ksadk/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  200. ksadk/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  201. ksadk/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  202. ksadk/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  203. ksadk/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  204. ksadk/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  205. ksadk/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  206. ksadk/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  207. ksadk/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  208. ksadk/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  209. ksadk/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  210. ksadk/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  211. ksadk/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  212. ksadk/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  213. ksadk/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  214. ksadk/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  215. ksadk/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  216. ksadk/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  217. ksadk/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  218. ksadk/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  219. ksadk/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  220. ksadk/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  221. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +8 -0
  222. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +1 -0
  223. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +303 -0
  224. ksadk/server/static/assets/NativeTerminalPanel-CHOd7Rch.css +1 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +1 -0
  226. ksadk/server/static/assets/addon-fit-DthTIhi3.js +1 -0
  227. ksadk/server/static/assets/apl-CnwPGSsG.js +1 -0
  228. ksadk/server/static/assets/arc-bj2rZnP5.js +1 -0
  229. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +1 -0
  230. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-2u1ltDIj.js +36 -0
  231. ksadk/server/static/assets/asciiarmor-qTkVPQu6.js +1 -0
  232. ksadk/server/static/assets/asn1-Dr8qZg38.js +1 -0
  233. ksadk/server/static/assets/asterisk-Bja0s6e1.js +1 -0
  234. ksadk/server/static/assets/blockDiagram-GPEHLZMM-D6fvdK20.js +132 -0
  235. ksadk/server/static/assets/brainfuck-D5EjA2JK.js +1 -0
  236. ksadk/server/static/assets/c4Diagram-AAUBKEIU-BK82L5n5.js +10 -0
  237. ksadk/server/static/assets/channel-BSSOwFIb.js +1 -0
  238. ksadk/server/static/assets/chunk-2J33WTMH-DLVdkeyQ.js +1 -0
  239. ksadk/server/static/assets/chunk-4BX2VUAB-BbwsYcbW.js +1 -0
  240. ksadk/server/static/assets/chunk-55IACEB6-DOairxzw.js +1 -0
  241. ksadk/server/static/assets/chunk-727SXJPM-hCO487gs.js +206 -0
  242. ksadk/server/static/assets/chunk-AQP2D5EJ-XU8vDOuG.js +231 -0
  243. ksadk/server/static/assets/chunk-FMBD7UC4-PrapyfMu.js +15 -0
  244. ksadk/server/static/assets/chunk-ND2GUHAM-CboKhTZ3.js +1 -0
  245. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +1 -0
  246. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +1 -0
  247. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +1 -0
  248. ksadk/server/static/assets/clike-DHH8Ad3s.js +1 -0
  249. ksadk/server/static/assets/clojure-BflJGmDX.js +1 -0
  250. ksadk/server/static/assets/cmake-CyVbuzPu.js +1 -0
  251. ksadk/server/static/assets/cobol-B_OZ4V-R.js +1 -0
  252. ksadk/server/static/assets/coffeescript-B2pKbFk8.js +1 -0
  253. ksadk/server/static/assets/commonlisp-CcllspGY.js +1 -0
  254. ksadk/server/static/assets/cose-bilkent-S5V4N54A-_N8bJ-EU.js +1 -0
  255. ksadk/server/static/assets/crystal-M5-qzICs.js +1 -0
  256. ksadk/server/static/assets/css-CYpP4FRV.js +1 -0
  257. ksadk/server/static/assets/cypher-p9eYesGn.js +1 -0
  258. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +321 -0
  259. ksadk/server/static/assets/d-BhMjBjQP.js +1 -0
  260. ksadk/server/static/assets/dagre-BM42HDAG-FoDlo5J2.js +4 -0
  261. ksadk/server/static/assets/dagre-Bx709z4p.js +1 -0
  262. ksadk/server/static/assets/defaultLocale-C8Fc0cco.js +1 -0
  263. ksadk/server/static/assets/diagram-2AECGRRQ-BBp6ebBw.js +43 -0
  264. ksadk/server/static/assets/diagram-5GNKFQAL-CqOvG1PR.js +10 -0
  265. ksadk/server/static/assets/diagram-KO2AKTUF-O9T3t6ba.js +3 -0
  266. ksadk/server/static/assets/diagram-LMA3HP47-CGG8ekBO.js +24 -0
  267. ksadk/server/static/assets/diagram-OG6HWLK6-4BmH1EEg.js +24 -0
  268. ksadk/server/static/assets/diff-ChtP43wD.js +1 -0
  269. ksadk/server/static/assets/dist-6kSZvCZh.js +1 -0
  270. ksadk/server/static/assets/dist-B67uF2uK.js +1 -0
  271. ksadk/server/static/assets/dist-BMKM66B4.js +1 -0
  272. ksadk/server/static/assets/dist-BVzcWa6D.js +1 -0
  273. ksadk/server/static/assets/dist-Bk1l05Ni.js +1 -0
  274. ksadk/server/static/assets/dist-BkJjNih3.js +1 -0
  275. ksadk/server/static/assets/dist-BmuTWcm7.js +9 -0
  276. ksadk/server/static/assets/dist-C2DKRFVK.js +1 -0
  277. ksadk/server/static/assets/dist-CWiTDN_V.js +6 -0
  278. ksadk/server/static/assets/dist-CZ6C3x8B.js +23 -0
  279. ksadk/server/static/assets/dist-CiZHB6ZW.js +1 -0
  280. ksadk/server/static/assets/dist-Cp1_jKwf.js +1 -0
  281. ksadk/server/static/assets/dist-Cwhk4lwI.js +2 -0
  282. ksadk/server/static/assets/dist-D5JGcxBx.js +11 -0
  283. ksadk/server/static/assets/dist-D99wf6_X.js +1 -0
  284. ksadk/server/static/assets/dist-DCpZrjaK.js +1 -0
  285. ksadk/server/static/assets/dist-DI-oVAXw.js +1 -0
  286. ksadk/server/static/assets/dist-DLMjL01k.js +1 -0
  287. ksadk/server/static/assets/dist-DLeM1OCK.js +1 -0
  288. ksadk/server/static/assets/dist-DM0rEQPk.js +1 -0
  289. ksadk/server/static/assets/dist-DbaH3p5h.js +1 -0
  290. ksadk/server/static/assets/dist-S4klkK5W.js +1 -0
  291. ksadk/server/static/assets/dockerfile-BLkNEvjs.js +1 -0
  292. ksadk/server/static/assets/dtd-DTF72YNO.js +1 -0
  293. ksadk/server/static/assets/dylan-C6jEEEk-.js +1 -0
  294. ksadk/server/static/assets/ebnf-D4c0_ac3.js +1 -0
  295. ksadk/server/static/assets/ecl-BSZaIHnp.js +1 -0
  296. ksadk/server/static/assets/eiffel-BEjRio4Q.js +1 -0
  297. ksadk/server/static/assets/elm-Cshzl8qu.js +1 -0
  298. ksadk/server/static/assets/erDiagram-TEJ5UH35-oJhaci7S.js +85 -0
  299. ksadk/server/static/assets/erlang-DaB2Rkuy.js +1 -0
  300. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +1 -0
  301. ksadk/server/static/assets/factor-D7LVTn2l.js +1 -0
  302. ksadk/server/static/assets/fcl-ClOhbFR7.js +1 -0
  303. ksadk/server/static/assets/flowDiagram-I6XJVG4X-DtTmUan-.js +162 -0
  304. ksadk/server/static/assets/forth-Cezjo90N.js +1 -0
  305. ksadk/server/static/assets/fortran-Bt6PBEDR.js +1 -0
  306. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-BSsb_eij.js +292 -0
  307. ksadk/server/static/assets/gas-BHEdbvp9.js +1 -0
  308. ksadk/server/static/assets/gherkin-oBAE_ms0.js +1 -0
  309. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +1 -0
  310. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-BR5s6Pws.js +106 -0
  311. ksadk/server/static/assets/graphlib-B8gBHxth.js +1 -0
  312. ksadk/server/static/assets/groovy-BC3IOsC8.js +1 -0
  313. ksadk/server/static/assets/haskell-BRsoo5mP.js +1 -0
  314. ksadk/server/static/assets/haxe-DIz0ZZqd.js +1 -0
  315. ksadk/server/static/assets/http-CLVgA2GD.js +1 -0
  316. ksadk/server/static/assets/idl-BUZw3wgd.js +1 -0
  317. ksadk/server/static/assets/index-BH7-pC0B.js +165 -0
  318. ksadk/server/static/assets/index-DAURbCsP.css +1 -0
  319. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +1 -0
  320. ksadk/server/static/assets/infoDiagram-5YYISTIA-DYPc5cvh.js +2 -0
  321. ksadk/server/static/assets/init-D6jRqBbL.js +1 -0
  322. ksadk/server/static/assets/ishikawaDiagram-YF4QCWOH-MoBdALDg.js +70 -0
  323. ksadk/server/static/assets/javascript-9Tg8ixDm.js +1 -0
  324. ksadk/server/static/assets/journeyDiagram-JHISSGLW-DgLiQT8l.js +139 -0
  325. ksadk/server/static/assets/julia-CQfgDRfP.js +1 -0
  326. ksadk/server/static/assets/kanban-definition-UN3LZRKU-CsFx112F.js +89 -0
  327. ksadk/server/static/assets/katex-DRsiwpM2.js +265 -0
  328. ksadk/server/static/assets/linear-TNRQ5Orv.js +1 -0
  329. ksadk/server/static/assets/livescript-BXxG0Yva.js +1 -0
  330. ksadk/server/static/assets/lua-CiA1ziua.js +1 -0
  331. ksadk/server/static/assets/mathematica-u8YMmbU0.js +1 -0
  332. ksadk/server/static/assets/mbox-BEMVcqjs.js +1 -0
  333. ksadk/server/static/assets/mermaid-parser.core-D2eMVOgo.js +161 -0
  334. ksadk/server/static/assets/mindmap-definition-RKZ34NQL-BtByYFhL.js +96 -0
  335. ksadk/server/static/assets/mirc-C9zpzAZU.js +1 -0
  336. ksadk/server/static/assets/mllike-B45xgp2S.js +1 -0
  337. ksadk/server/static/assets/modelica-CqHI_q-D.js +1 -0
  338. ksadk/server/static/assets/mscgen-qgSLujhx.js +1 -0
  339. ksadk/server/static/assets/mumps-CW_TBmxz.js +1 -0
  340. ksadk/server/static/assets/nginx-BMaDbqW3.js +1 -0
  341. ksadk/server/static/assets/nsis-AQ_alPln.js +1 -0
  342. ksadk/server/static/assets/ntriples-d28e9m0E.js +1 -0
  343. ksadk/server/static/assets/octave-D2Q8cUp1.js +1 -0
  344. ksadk/server/static/assets/ordinal-hYBb2elL.js +1 -0
  345. ksadk/server/static/assets/oz-BwTct_5T.js +1 -0
  346. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +1 -0
  347. ksadk/server/static/assets/pascal-Dp_KdFgX.js +1 -0
  348. ksadk/server/static/assets/perl-CdEHsPfU.js +1 -0
  349. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +1 -0
  350. ksadk/server/static/assets/pieDiagram-4H26LBE5-DPnuCB8c.js +30 -0
  351. ksadk/server/static/assets/pig-AF4Hwhju.js +1 -0
  352. ksadk/server/static/assets/powershell-CzG71I4L.js +1 -0
  353. ksadk/server/static/assets/properties-C357ku5U.js +1 -0
  354. ksadk/server/static/assets/protobuf-D1ij_kNL.js +1 -0
  355. ksadk/server/static/assets/pug-Bugr-47h.js +1 -0
  356. ksadk/server/static/assets/puppet-B4hC2X4m.js +1 -0
  357. ksadk/server/static/assets/python-BqYw9LYJ.js +1 -0
  358. ksadk/server/static/assets/q-B9NhS7L_.js +1 -0
  359. ksadk/server/static/assets/quadrantDiagram-W4KKPZXB-miFpCPv4.js +7 -0
  360. ksadk/server/static/assets/r-DADKenSm.js +1 -0
  361. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +1 -0
  362. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-mjB_yYkW.js +84 -0
  363. ksadk/server/static/assets/rpm-gpDK5sbt.js +1 -0
  364. ksadk/server/static/assets/ruby-DsYpTuWg.js +1 -0
  365. ksadk/server/static/assets/sankeyDiagram-5OEKKPKP-D7hSlZZZ.js +40 -0
  366. ksadk/server/static/assets/sas-C2OyD2Y6.js +1 -0
  367. ksadk/server/static/assets/scheme-CyDdOh7e.js +1 -0
  368. ksadk/server/static/assets/sequenceDiagram-3UESZ5HK-D9Cx6hC3.js +162 -0
  369. ksadk/server/static/assets/shell-Du5Qg9im.js +1 -0
  370. ksadk/server/static/assets/sieve-BAxa3IjF.js +1 -0
  371. ksadk/server/static/assets/simple-mode-DRpGK0lJ.js +1 -0
  372. ksadk/server/static/assets/smalltalk-BLp5-jwC.js +1 -0
  373. ksadk/server/static/assets/solr-BBopId9w.js +1 -0
  374. ksadk/server/static/assets/sparql-Dz-mlCAC.js +1 -0
  375. ksadk/server/static/assets/spreadsheet-CTIncYxj.js +1 -0
  376. ksadk/server/static/assets/sql-DcCJ9jop.js +1 -0
  377. ksadk/server/static/assets/stateDiagram-AJRCARHV-D_zrg47o.js +1 -0
  378. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +1 -0
  379. ksadk/server/static/assets/stex-sDrdk2BW.js +1 -0
  380. ksadk/server/static/assets/stylus-DLLZ8dgm.js +1 -0
  381. ksadk/server/static/assets/swift-DKB6_1j6.js +1 -0
  382. ksadk/server/static/assets/tcl-BGfruO1u.js +1 -0
  383. ksadk/server/static/assets/textile-DWBqthVk.js +1 -0
  384. ksadk/server/static/assets/tiddlywiki-VlUiK86J.js +1 -0
  385. ksadk/server/static/assets/tiki-CxVlf5KZ.js +1 -0
  386. ksadk/server/static/assets/timeline-definition-PNZ67QCA-CwSec0Wa.js +120 -0
  387. ksadk/server/static/assets/toml-LvoKBwCs.js +1 -0
  388. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +1 -0
  389. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +1 -0
  390. ksadk/server/static/assets/troff-C2dAgh7P.js +1 -0
  391. ksadk/server/static/assets/ttcn-BePYh-hG.js +1 -0
  392. ksadk/server/static/assets/ttcn-cfg-CY0aot7Q.js +1 -0
  393. ksadk/server/static/assets/turtle-BDLWE2cD.js +1 -0
  394. ksadk/server/static/assets/vb-BthdM_4N.js +1 -0
  395. ksadk/server/static/assets/vbscript-D9f6rSsU.js +1 -0
  396. ksadk/server/static/assets/velocity-CqftRsjg.js +1 -0
  397. ksadk/server/static/assets/vennDiagram-CIIHVFJN-agCVobiF.js +34 -0
  398. ksadk/server/static/assets/verilog-D-x6ne0Z.js +1 -0
  399. ksadk/server/static/assets/vhdl-CMrOq2q1.js +1 -0
  400. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +1 -0
  401. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-TmrlhWNt.js +78 -0
  402. ksadk/server/static/assets/webidl-D5jiJvOU.js +1 -0
  403. ksadk/server/static/assets/xquery-fj_R-kMw.js +1 -0
  404. ksadk/server/static/assets/xterm-DooSxjI5.js +36 -0
  405. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-DV71V-ah.js +7 -0
  406. ksadk/server/static/assets/yacas-CXXgtw0p.js +1 -0
  407. ksadk/server/static/assets/z80-CL1naaMV.js +1 -0
  408. ksadk/server/static/favicon.svg +1 -0
  409. ksadk/server/static/icons.svg +24 -0
  410. ksadk/server/static/index.html +14 -0
  411. ksadk/server/terminal_sessions.py +551 -0
  412. ksadk/sessions/__init__.py +283 -0
  413. ksadk/sessions/base.py +354 -0
  414. ksadk/sessions/continuity.py +313 -0
  415. ksadk/sessions/errors.py +7 -0
  416. ksadk/sessions/in_memory.py +273 -0
  417. ksadk/sessions/local_service.py +949 -0
  418. ksadk/sessions/postgres_service.py +838 -0
  419. ksadk/sessions/sqlite_service.py +13 -0
  420. ksadk/skills/__init__.py +9 -0
  421. ksadk/skills/loader.py +46 -0
  422. ksadk/skills/models.py +120 -0
  423. ksadk/skills/package_store.py +104 -0
  424. ksadk/skills/runtime/__init__.py +17 -0
  425. ksadk/skills/runtime/agent.py +183 -0
  426. ksadk/skills/runtime/artifacts.py +39 -0
  427. ksadk/skills/runtime/backends/__init__.py +1 -0
  428. ksadk/skills/runtime/backends/disabled.py +8 -0
  429. ksadk/skills/runtime/backends/e2b.py +168 -0
  430. ksadk/skills/runtime/backends/local.py +95 -0
  431. ksadk/skills/runtime/base.py +103 -0
  432. ksadk/skills/runtime/executor.py +219 -0
  433. ksadk/skills/runtime/factory.py +37 -0
  434. ksadk/skills/runtime/loader.py +131 -0
  435. ksadk/skills/runtime/registry.py +150 -0
  436. ksadk/skills/runtime/request.py +67 -0
  437. ksadk/skills/service_client.py +239 -0
  438. ksadk/skills/service_env.py +58 -0
  439. ksadk/skills/tool_defs.py +221 -0
  440. ksadk/terminal_client.py +69 -0
  441. ksadk/terminal_exec_policy.py +229 -0
  442. ksadk/tools/__init__.py +17 -0
  443. ksadk/tools/gateway.py +230 -0
  444. ksadk/tools/result_budget.py +154 -0
  445. ksadk/toolsets/__init__.py +852 -0
  446. ksadk/toolsets/_langchain.py +11 -0
  447. ksadk/toolsets/platform.py +93 -0
  448. ksadk/toolsets/sandbox.py +310 -0
  449. ksadk/toolsets/skills.py +217 -0
  450. ksadk/toolsets/web.py +266 -0
  451. ksadk/toolsets/workspace.py +878 -0
  452. ksadk/toolsets/workspace_state.py +44 -0
  453. ksadk/tracing/__init__.py +37 -0
  454. ksadk/tracing/exporters/__init__.py +1 -0
  455. ksadk/tracing/exporters/inmemory_exporter.py +116 -0
  456. ksadk/tracing/exporters/langfuse_exporter.py +445 -0
  457. ksadk/tracing/setup.py +829 -0
  458. ksadk/tracing/span_utils.py +114 -0
  459. ksadk/tui/__init__.py +12 -0
  460. ksadk/tui/app.py +400 -0
  461. ksadk/tui/clipboard.py +100 -0
  462. ksadk/tui/widgets/__init__.py +22 -0
  463. ksadk/tui/widgets/assistant.py +89 -0
  464. ksadk/tui/widgets/base.py +54 -0
  465. ksadk/tui/widgets/chat_input.py +246 -0
  466. ksadk/tui/widgets/history.py +156 -0
  467. ksadk/tui/widgets/system.py +87 -0
  468. ksadk/tui/widgets/thinking.py +110 -0
  469. ksadk/tui/widgets/tool_call.py +161 -0
  470. ksadk/tui/widgets/user.py +37 -0
  471. ksadk/ui_config.py +168 -0
  472. ksadk/version.py +4 -0
  473. ksadk_runtime_common/__init__.py +20 -0
  474. ksadk_runtime_common/memory_backend/__init__.py +29 -0
  475. ksadk_runtime_common/memory_backend/manifest.py +79 -0
  476. ksadk_runtime_common/memory_backend/providers/__init__.py +6 -0
  477. ksadk_runtime_common/memory_backend/providers/lancedb.py +151 -0
  478. ksadk_runtime_common/memory_backend/providers/mem0.py +74 -0
  479. ksadk_runtime_common/memory_backend/registry.py +56 -0
  480. ksadk_runtime_common/memory_backend/render.py +71 -0
  481. ksadk_runtime_common/schemas/memory_backend_manifest.schema.json +148 -0
  482. ksadk_runtime_common/workspace_files/__init__.py +29 -0
  483. ksadk_runtime_common/workspace_files/bootstrap.py +56 -0
  484. ksadk_runtime_common/workspace_files/constants.py +9 -0
  485. ksadk_runtime_common/workspace_files/path_utils.py +71 -0
  486. ksadk_runtime_common/workspace_files/preview.py +96 -0
  487. ksadk_runtime_common/workspace_files/router.py +254 -0
  488. agentengine_sdk_python-0.6.2.dist-info/METADATA +0 -46
  489. agentengine_sdk_python-0.6.2.dist-info/RECORD +0 -4
  490. agentengine_sdk_python-0.6.2.dist-info/top_level.txt +0 -1
@@ -0,0 +1,852 @@
1
+ """
2
+ LangGraphRunner - LangGraph 框架运行时
3
+
4
+ 直接透传 LangGraph 原生能力,最小化封装
5
+ """
6
+
7
+ import os
8
+ import uuid
9
+ import re
10
+ from typing import Any, AsyncIterator, Dict, Mapping
11
+ import base64
12
+ from pathlib import Path
13
+
14
+ from ksadk.runners.base_runner import BaseRunner
15
+ from ksadk.sessions.continuity import LangGraphSessionAdapter
16
+ from ksadk.runners.utils import get_langfuse_callbacks, get_langfuse_metadata, load_agent_module
17
+ from langgraph.types import Command
18
+ from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
19
+ from ksadk.conversations.reasoning_markup import ReasoningMarkupParser, strip_reasoning_markup
20
+
21
+
22
+ class LangGraphRunner(BaseRunner):
23
+ """LangGraph 框架运行时
24
+
25
+ 透传原生 LangGraph 功能,支持任意 State 格式
26
+ """
27
+
28
+ def load_agent(self) -> None:
29
+ self._load_agent(force_reload=False)
30
+
31
+ def _load_agent(self, *, force_reload: bool) -> None:
32
+ """加载 LangGraph 编译后的图"""
33
+ self._agent, self._module = load_agent_module(
34
+ self.project_dir,
35
+ self.detection_result.entry_point,
36
+ self.detection_result.agent_variable,
37
+ force_reload=force_reload,
38
+ )
39
+ self._loaded_model_name = self.normalize_requested_model(
40
+ os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
41
+ )
42
+
43
+ if not hasattr(self._agent, "invoke"):
44
+ raise TypeError("加载的对象不是有效的 LangGraph CompiledGraph")
45
+
46
+ def prepare_for_request(self, model: str | None) -> None:
47
+ normalized = self.sync_process_model_env(model)
48
+ if normalized is None or self._agent is None:
49
+ return
50
+ if normalized == getattr(self, "_loaded_model_name", None):
51
+ return
52
+ self._load_agent(force_reload=True)
53
+
54
+ def get_session_adapter(self):
55
+ return LangGraphSessionAdapter()
56
+
57
+ def describe_checkpoint_capability(self) -> dict[str, Any]:
58
+ agent = getattr(self, "_agent", None)
59
+ has_checkpointer = bool(
60
+ getattr(agent, "checkpointer", None) or getattr(agent, "_checkpointer", None)
61
+ )
62
+ if not has_checkpointer:
63
+ return {
64
+ "Supported": False,
65
+ "Backend": "none",
66
+ "Scope": "unknown",
67
+ "Durable": False,
68
+ "SharedAcrossPods": False,
69
+ "Reason": "LangGraph graph has no configured checkpointer",
70
+ }
71
+
72
+ backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
73
+ if backend == "local":
74
+ backend = "sqlite"
75
+ if not backend:
76
+ backend = "unknown"
77
+ scope = "unknown"
78
+ durable = False
79
+ shared = False
80
+ reason = ""
81
+ if backend == "postgres":
82
+ scope = "shared"
83
+ durable = True
84
+ shared = True
85
+ elif backend == "sqlite":
86
+ scope = "pod_local"
87
+ durable = True
88
+ shared = False
89
+ reason = "SQLite checkpoint is durable for local web debugging but is not shared across pods"
90
+ elif backend in {"memory", "inmemory"}:
91
+ backend = "memory"
92
+ scope = "process_local"
93
+ durable = False
94
+ shared = False
95
+ reason = "In-memory checkpoint cannot be recovered after process restart or across pods"
96
+
97
+ return {
98
+ "Supported": True,
99
+ "Backend": backend,
100
+ "Scope": scope,
101
+ "Durable": durable,
102
+ "SharedAcrossPods": shared,
103
+ "ResumeMode": "time_travel",
104
+ "Reason": reason,
105
+ }
106
+
107
+ def get_runtime_capabilities(self) -> dict[str, Any]:
108
+ capabilities = super().get_runtime_capabilities()
109
+ capabilities["SessionContinuity"] = {
110
+ "Supported": True,
111
+ "Type": "checkpoint"
112
+ if capabilities["Checkpoint"].get("Supported")
113
+ else "semantic_replay",
114
+ "Level": "runtime"
115
+ if capabilities["Checkpoint"].get("Supported")
116
+ else "semantic",
117
+ "Reason": "",
118
+ }
119
+ return capabilities
120
+
121
+ def _get_config(self, session_id: str) -> dict:
122
+ """获取运行配置"""
123
+ config = {"configurable": {"thread_id": session_id}}
124
+
125
+ langfuse_callbacks = get_langfuse_callbacks()
126
+ if langfuse_callbacks:
127
+ config["callbacks"] = langfuse_callbacks
128
+ config["metadata"] = get_langfuse_metadata(session_id)
129
+
130
+ return config
131
+
132
+ @staticmethod
133
+ def _extract_langgraph_checkpoint_ref(payload: Dict[str, Any]) -> dict[str, Any]:
134
+ framework_ref = payload.get("framework_ref") or {}
135
+ if not isinstance(framework_ref, dict):
136
+ return {}
137
+ langgraph_ref = framework_ref.get("langgraph") or {}
138
+ if not isinstance(langgraph_ref, dict):
139
+ return {}
140
+ return dict(langgraph_ref)
141
+
142
+ @classmethod
143
+ def _apply_checkpoint_resume_config(
144
+ cls,
145
+ config: dict[str, Any],
146
+ *,
147
+ session_id: str,
148
+ checkpoint_ref: dict[str, Any],
149
+ ) -> dict[str, Any]:
150
+ checkpoint_id = str(checkpoint_ref.get("checkpoint_id") or "").strip()
151
+ if not checkpoint_id:
152
+ raise ValueError("checkpoint_resume requires framework_ref.langgraph.checkpoint_id")
153
+
154
+ thread_id = str(checkpoint_ref.get("thread_id") or session_id or "").strip()
155
+ if not thread_id:
156
+ raise ValueError("checkpoint_resume requires session_id or framework_ref.langgraph.thread_id")
157
+
158
+ next_config = dict(config)
159
+ configurable = dict(next_config.get("configurable") or {})
160
+ configurable["thread_id"] = thread_id
161
+ configurable["checkpoint_ns"] = str(checkpoint_ref.get("checkpoint_ns") or "")
162
+ configurable["checkpoint_id"] = checkpoint_id
163
+ next_config["configurable"] = configurable
164
+ return next_config
165
+
166
+ @staticmethod
167
+ def _checkpoint_ref_from_state(state: Any) -> dict[str, Any]:
168
+ state_config = None
169
+ if isinstance(state, dict):
170
+ state_config = state.get("config")
171
+ else:
172
+ state_config = getattr(state, "config", None)
173
+ if not isinstance(state_config, dict):
174
+ return {}
175
+ configurable = state_config.get("configurable") or {}
176
+ if not isinstance(configurable, dict):
177
+ return {}
178
+ thread_id = str(configurable.get("thread_id") or "").strip()
179
+ checkpoint_id = str(configurable.get("checkpoint_id") or "").strip()
180
+ if not thread_id or not checkpoint_id:
181
+ return {}
182
+ next_nodes_raw = state.get("next") if isinstance(state, dict) else getattr(state, "next", None)
183
+ next_nodes: list[str] = []
184
+ if isinstance(next_nodes_raw, str):
185
+ if next_nodes_raw.strip():
186
+ next_nodes = [next_nodes_raw.strip()]
187
+ elif isinstance(next_nodes_raw, (list, tuple, set)):
188
+ next_nodes = [
189
+ str(item).strip()
190
+ for item in next_nodes_raw
191
+ if str(item or "").strip()
192
+ ]
193
+ return {
194
+ "langgraph": {
195
+ "thread_id": thread_id,
196
+ **(
197
+ {"checkpoint_ns": str(configurable.get("checkpoint_ns")).strip()}
198
+ if str(configurable.get("checkpoint_ns") or "").strip()
199
+ else {}
200
+ ),
201
+ "checkpoint_id": checkpoint_id,
202
+ **({"next_node": next_nodes[0], "next_nodes": next_nodes} if next_nodes else {}),
203
+ }
204
+ }
205
+
206
+ async def _latest_checkpoint_metadata(self, config: dict[str, Any]) -> dict[str, Any]:
207
+ state = None
208
+ try:
209
+ if callable(getattr(self._agent, "aget_state", None)):
210
+ state = await self._agent.aget_state(config)
211
+ elif callable(getattr(self._agent, "get_state", None)):
212
+ state = self._agent.get_state(config)
213
+ except Exception:
214
+ return {}
215
+ framework_ref = self._checkpoint_ref_from_state(state)
216
+ if not framework_ref:
217
+ return {}
218
+ langgraph_ref = framework_ref.get("langgraph") if isinstance(framework_ref, dict) else {}
219
+ next_node = ""
220
+ if isinstance(langgraph_ref, dict):
221
+ next_node = str(langgraph_ref.get("next_node") or "").strip()
222
+ checkpoint_capability = self.describe_checkpoint_capability()
223
+ is_terminal = not bool(next_node)
224
+ is_resumable = bool(
225
+ checkpoint_capability.get("Supported")
226
+ and next_node
227
+ and checkpoint_capability.get("Scope") != "process_local"
228
+ )
229
+ return {
230
+ "agentengine": {
231
+ "framework": "langgraph",
232
+ "framework_ref": framework_ref,
233
+ "next_node": next_node,
234
+ "is_terminal": is_terminal,
235
+ "is_resumable": is_resumable,
236
+ "resume_status": "resumable" if is_resumable else "disabled",
237
+ "resume_disabled_reason": (
238
+ "该 checkpoint 已是终态;可选择更早恢复点重跑"
239
+ if is_terminal
240
+ else str(checkpoint_capability.get("Reason") or "")
241
+ ),
242
+ "backend": checkpoint_capability.get("Backend"),
243
+ "scope": checkpoint_capability.get("Scope"),
244
+ "durable": bool(checkpoint_capability.get("Durable")),
245
+ }
246
+ }
247
+
248
+ async def _latest_state_usage(self, config: dict[str, Any]) -> dict[str, Any]:
249
+ state = None
250
+ try:
251
+ if callable(getattr(self._agent, "aget_state", None)):
252
+ state = await self._agent.aget_state(config)
253
+ elif callable(getattr(self._agent, "get_state", None)):
254
+ state = self._agent.get_state(config)
255
+ except Exception:
256
+ return {}
257
+ values = getattr(state, "values", None)
258
+ if values is not None:
259
+ return self._extract_usage(values)
260
+ return {}
261
+
262
+ @staticmethod
263
+ def _ambient_context_text(payload: Dict[str, Any]) -> str:
264
+ sections: list[str] = []
265
+ kb_context = payload.get("kb_context") or {}
266
+ kb_text = str(kb_context.get("formatted_text") or "").strip() if isinstance(kb_context, dict) else ""
267
+ if kb_text:
268
+ sections.append(f"Knowledge base context:\n{kb_text}")
269
+
270
+ memory_context = payload.get("memory_context") or {}
271
+ memory_text = (
272
+ str(memory_context.get("formatted_text") or "").strip()
273
+ if isinstance(memory_context, dict)
274
+ else ""
275
+ )
276
+ if memory_text:
277
+ sections.append(f"Long-term memory context:\n{memory_text}")
278
+
279
+ return "\n\n".join(section for section in sections if section)
280
+
281
+ @staticmethod
282
+ def _strip_platform_context_fields(payload: Dict[str, Any]) -> Dict[str, Any]:
283
+ return {
284
+ key: value
285
+ for key, value in payload.items()
286
+ if key not in {"platform_context", "kb_context", "memory_context"}
287
+ }
288
+
289
+ def _has_prepare_state_hook(self) -> bool:
290
+ module = getattr(self, "_module", None)
291
+ return callable(getattr(module, "ksadk_prepare_state", None))
292
+
293
+ def _prepare_state_with_hook(
294
+ self,
295
+ payload: Dict[str, Any],
296
+ session_id: str,
297
+ history: list,
298
+ *,
299
+ is_resume: bool = False,
300
+ ) -> Dict[str, Any]:
301
+ module = getattr(self, "_module", None)
302
+ prepare_state = getattr(module, "ksadk_prepare_state", None)
303
+ if not callable(prepare_state):
304
+ return self._to_state(payload, history)
305
+
306
+ normalized_payload = self._strip_platform_context_fields(payload)
307
+ session_context = {
308
+ "session_id": session_id,
309
+ "history": list(history),
310
+ "is_resume": bool(is_resume),
311
+ "platform_context": payload.get("platform_context"),
312
+ "kb_context": payload.get("kb_context"),
313
+ "memory_context": payload.get("memory_context"),
314
+ }
315
+ prepared = prepare_state(dict(normalized_payload), session_context)
316
+ if not isinstance(prepared, dict):
317
+ raise TypeError("ksadk_prepare_state(payload, session_context) must return a dict")
318
+ return prepared
319
+
320
+ def _to_state(self, payload: Dict[str, Any], history: list) -> Dict[str, Any]:
321
+ """将简化输入转换为 state,并保留除 input 外的附加字段。"""
322
+ normalized_payload = self._strip_platform_context_fields(payload)
323
+ ambient_text = self._ambient_context_text(payload)
324
+ instructions = str(normalized_payload.pop("instructions", "") or "").strip()
325
+ system_sections = [section for section in (instructions, ambient_text) if section]
326
+ system_text = "\n\n".join(system_sections)
327
+
328
+ if "input" in normalized_payload and "messages" not in normalized_payload:
329
+ from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
330
+
331
+ messages = []
332
+ if system_text:
333
+ messages.append(SystemMessage(content=system_text))
334
+ for msg in history:
335
+ role = msg.get("role")
336
+ content = msg.get("content", "")
337
+ if role == "user":
338
+ messages.append(HumanMessage(content=content))
339
+ elif role in ("assistant", "model"):
340
+ messages.append(AIMessage(content=content))
341
+
342
+ user_input = normalized_payload["input"] or "[empty message]"
343
+ attachments = list(normalized_payload.get("attachments") or [])
344
+ model_metadata = normalized_payload.get("model_metadata")
345
+ user_content = self._build_langgraph_human_content(
346
+ user_input,
347
+ attachments,
348
+ model_metadata=model_metadata if isinstance(model_metadata, dict) else None,
349
+ )
350
+ if not self._history_tail_matches_user_content(history, user_content):
351
+ messages.append(HumanMessage(content=user_content))
352
+ state = {k: v for k, v in normalized_payload.items() if k != "input"}
353
+ state["messages"] = messages
354
+ return state
355
+
356
+ if "messages" in normalized_payload:
357
+ state = dict(normalized_payload)
358
+ if system_text and isinstance(state.get("messages"), list):
359
+ from langchain_core.messages import SystemMessage
360
+
361
+ state["messages"] = [SystemMessage(content=system_text), *state["messages"]]
362
+ return state
363
+
364
+ return normalized_payload
365
+
366
+ @classmethod
367
+ def _history_tail_matches_user_content(cls, history: list, user_content: Any) -> bool:
368
+ if not history:
369
+ return False
370
+ tail = history[-1]
371
+ if not isinstance(tail, dict) or tail.get("role") != "user":
372
+ return False
373
+ tail_text = cls._normalizable_text_content(tail.get("content"))
374
+ user_text = cls._normalizable_text_content(user_content)
375
+ return tail_text is not None and tail_text == user_text
376
+
377
+ @staticmethod
378
+ def _normalizable_text_content(content: Any) -> str | None:
379
+ if isinstance(content, str):
380
+ return content.strip()
381
+ if isinstance(content, list):
382
+ text_parts: list[str] = []
383
+ for item in content:
384
+ if not isinstance(item, dict) or item.get("type") != "text":
385
+ return None
386
+ text_parts.append(str(item.get("text") or ""))
387
+ return "\n".join(text_parts).strip()
388
+ return None
389
+
390
+ @staticmethod
391
+ def _build_langgraph_human_content(
392
+ user_input: str,
393
+ attachments: list[dict[str, Any]],
394
+ *,
395
+ model_metadata: dict[str, Any] | None,
396
+ ) -> Any:
397
+ del model_metadata
398
+ image_blocks: list[dict[str, Any]] = []
399
+
400
+ for attachment in attachments or []:
401
+ if not isinstance(attachment, dict):
402
+ continue
403
+
404
+ mime_type = str(attachment.get("mime_type") or "application/octet-stream")
405
+ display_name = str(attachment.get("display_name") or "")
406
+ if classify_attachment_kind(mime_type, display_name) != "image":
407
+ continue
408
+
409
+ data_b64 = str(attachment.get("data") or "").strip()
410
+ transport = str(attachment.get("transport") or "")
411
+ file_uri = str(attachment.get("file_uri") or "").strip()
412
+
413
+ if transport == "inline" and data_b64:
414
+ image_blocks.append(
415
+ {
416
+ "type": "image_url",
417
+ "image_url": {
418
+ "url": f"data:{mime_type};base64,{data_b64}",
419
+ },
420
+ }
421
+ )
422
+ continue
423
+
424
+ if file_uri.startswith(("http://", "https://")):
425
+ image_blocks.append(
426
+ {
427
+ "type": "image_url",
428
+ "image_url": {
429
+ "url": file_uri,
430
+ },
431
+ }
432
+ )
433
+ continue
434
+
435
+ file_uri = attachment.get("file_uri")
436
+ if not file_uri:
437
+ continue
438
+
439
+ raw = read_attachment_uri_bytes(file_uri)
440
+ if not raw:
441
+ continue
442
+
443
+ image_blocks.append(
444
+ {
445
+ "type": "image_url",
446
+ "image_url": {
447
+ "url": f"data:{mime_type};base64,{base64.b64encode(raw).decode('ascii')}",
448
+ },
449
+ }
450
+ )
451
+
452
+ if not image_blocks:
453
+ return user_input
454
+
455
+ content: list[dict[str, Any]] = []
456
+ if user_input:
457
+ content.append({"type": "text", "text": user_input})
458
+ content.extend(image_blocks)
459
+ return content
460
+
461
+ async def _invoke_graph(
462
+ self,
463
+ payload: Any,
464
+ *,
465
+ config: dict[str, Any],
466
+ context: dict[str, Any] | None,
467
+ ) -> Any:
468
+ if hasattr(self._agent, "ainvoke"):
469
+ kwargs = self._build_optional_call_kwargs(
470
+ self._agent.ainvoke,
471
+ config=config,
472
+ context=context,
473
+ )
474
+ return await self._agent.ainvoke(payload, **kwargs)
475
+ kwargs = self._build_optional_call_kwargs(
476
+ self._agent.invoke,
477
+ config=config,
478
+ context=context,
479
+ )
480
+ return self._agent.invoke(payload, **kwargs)
481
+
482
+ async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
483
+ """调用 LangGraph 图
484
+
485
+ 支持两种输入格式:
486
+ 1. 简化格式: {"input": "hello"} - 自动转换为 messages
487
+ 2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
488
+ """
489
+ payload = dict(input_data)
490
+ session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
491
+ is_resume = payload.pop("resume", False)
492
+ is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
493
+ checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
494
+ history = payload.pop("history", [])
495
+ native_context = self.build_native_context(payload.get("platform_context"))
496
+ normalized_payload = self._strip_platform_context_fields(payload)
497
+
498
+ config = self._get_config(session_id)
499
+ if is_checkpoint_resume:
500
+ config = self._apply_checkpoint_resume_config(
501
+ config,
502
+ session_id=session_id,
503
+ checkpoint_ref=checkpoint_ref,
504
+ )
505
+
506
+ # 判断输入格式 / resume
507
+ if is_checkpoint_resume:
508
+ state = None
509
+ elif self._has_prepare_state_hook():
510
+ state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
511
+ elif is_resume:
512
+ if "input" in normalized_payload and len(normalized_payload) == 1:
513
+ state = normalized_payload["input"]
514
+ else:
515
+ state = normalized_payload
516
+ else:
517
+ state = self._to_state(payload, history)
518
+
519
+ try:
520
+ if is_checkpoint_resume:
521
+ result = await self._invoke_graph(
522
+ None,
523
+ config=config,
524
+ context=native_context,
525
+ )
526
+ elif is_resume:
527
+ result = await self._invoke_graph(
528
+ Command(resume=state),
529
+ config=config,
530
+ context=native_context,
531
+ )
532
+ else:
533
+ result = await self._invoke_graph(
534
+ state,
535
+ config=config,
536
+ context=native_context,
537
+ )
538
+
539
+ output = {"output": self._extract_output(result), "raw": result}
540
+ usage = self._extract_usage(result)
541
+ if usage:
542
+ output["usage"] = usage
543
+ last_usage = self._extract_last_usage(result)
544
+ if last_usage:
545
+ output.setdefault("metadata", {})["last_usage"] = last_usage
546
+ metadata = await self._latest_checkpoint_metadata(config)
547
+ if metadata:
548
+ output["metadata"] = {**(output.get("metadata") or {}), **metadata}
549
+ return output
550
+
551
+ except Exception as e:
552
+ if "Interrupt" in type(e).__name__:
553
+ interrupt_info = self._get_interrupt_info(self._agent.get_state(config))
554
+ return {
555
+ "type": "interrupt",
556
+ "interrupt_info": interrupt_info,
557
+ "session_id": session_id,
558
+ "output": interrupt_info.get("message", "需要用户确认") if isinstance(interrupt_info, dict) else "需要用户确认",
559
+ }
560
+ raise
561
+
562
+ def _extract_output(self, result: Any) -> str:
563
+ """从结果中提取输出文本"""
564
+ if isinstance(result, dict):
565
+ # 自定义 output 字段是业务显式出参,优先于内部 messages state。
566
+ if "output" in result:
567
+ return result["output"]
568
+ # LangGraph 示例常用 answer 作为最终业务回答字段。
569
+ if "answer" in result:
570
+ return result["answer"]
571
+ # 标准 messages 格式
572
+ if "messages" in result:
573
+ messages = result["messages"]
574
+ if messages:
575
+ last = messages[-1]
576
+ return last.get("content", str(last)) if isinstance(last, dict) else getattr(last, "content", str(last))
577
+ return str(result) if result else ""
578
+
579
+ def _get_interrupt_info(self, state) -> dict:
580
+ """从 state 中获取 interrupt 信息"""
581
+ if hasattr(state, "tasks") and state.tasks:
582
+ for task in state.tasks:
583
+ if hasattr(task, "interrupts") and task.interrupts:
584
+ for intr in task.interrupts:
585
+ if hasattr(intr, "value"):
586
+ return intr.value
587
+ return {}
588
+
589
+ async def _stream_checkpoint_resume_updates(
590
+ self,
591
+ *,
592
+ config: dict[str, Any],
593
+ context: dict[str, Any] | None,
594
+ ) -> AsyncIterator[Dict[str, Any]]:
595
+ if not callable(getattr(self._agent, "astream", None)):
596
+ return
597
+
598
+ kwargs = self._build_optional_call_kwargs(
599
+ self._agent.astream,
600
+ config=config,
601
+ context=context,
602
+ )
603
+ if self._callable_accepts_keyword(self._agent.astream, "stream_mode"):
604
+ kwargs["stream_mode"] = "updates"
605
+
606
+ latest_output: Any = None
607
+ emitted_update = False
608
+ async for update in self._agent.astream(None, **kwargs):
609
+ emitted_update = True
610
+ latest_output = update
611
+ if isinstance(update, Mapping):
612
+ for node_name, node_output in update.items():
613
+ yield {
614
+ "type": "graph_update",
615
+ "node": str(node_name),
616
+ "output": node_output,
617
+ }
618
+ else:
619
+ yield {"type": "graph_update", "output": update}
620
+
621
+ state_usage = await self._latest_state_usage(config)
622
+ state_output = ""
623
+ try:
624
+ state = None
625
+ if callable(getattr(self._agent, "aget_state", None)):
626
+ state = await self._agent.aget_state(config)
627
+ elif callable(getattr(self._agent, "get_state", None)):
628
+ state = self._agent.get_state(config)
629
+ values = getattr(state, "values", None)
630
+ if values is not None:
631
+ state_output = self._extract_output(values)
632
+ except Exception:
633
+ state_output = ""
634
+ final_output = state_output or self._extract_output(latest_output)
635
+ yield {
636
+ "type": "final",
637
+ "output": final_output,
638
+ **({"usage": state_usage} if state_usage else {}),
639
+ **({"resume_noop": True} if not emitted_update else {}),
640
+ }
641
+
642
+ metadata = await self._latest_checkpoint_metadata(config)
643
+ if metadata:
644
+ yield {"type": "checkpoint", "metadata": metadata}
645
+
646
+ async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
647
+ """流式调用 LangGraph 图"""
648
+ payload = dict(input_data)
649
+ session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
650
+ history = payload.pop("history", [])
651
+ is_resume = payload.pop("resume", False)
652
+ is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
653
+ checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
654
+ native_context = self.build_native_context(payload.get("platform_context"))
655
+ normalized_payload = self._strip_platform_context_fields(payload)
656
+
657
+ invoke_payload = dict(payload)
658
+ invoke_payload["session_id"] = session_id
659
+ if history:
660
+ invoke_payload["history"] = history
661
+ if is_resume:
662
+ invoke_payload["resume"] = True
663
+ if is_checkpoint_resume:
664
+ invoke_payload["checkpoint_resume"] = True
665
+
666
+ config = self._get_config(session_id)
667
+ if is_checkpoint_resume:
668
+ config = self._apply_checkpoint_resume_config(
669
+ config,
670
+ session_id=session_id,
671
+ checkpoint_ref=checkpoint_ref,
672
+ )
673
+
674
+ if is_checkpoint_resume:
675
+ state = None
676
+ elif self._has_prepare_state_hook():
677
+ state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
678
+ elif is_resume:
679
+ if "input" in normalized_payload and len(normalized_payload) == 1:
680
+ state = normalized_payload["input"]
681
+ else:
682
+ state = normalized_payload
683
+ else:
684
+ state = self._to_state(payload, history)
685
+
686
+ accumulated_text = ""
687
+ accumulated_reasoning = ""
688
+ inline_reasoning_parser = ReasoningMarkupParser()
689
+ emitted_non_text_event = False
690
+ final_output_text = ""
691
+ final_output_usage: dict[str, Any] = {}
692
+
693
+ if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
694
+ try:
695
+ async for chunk in self._stream_checkpoint_resume_updates(
696
+ config=config,
697
+ context=native_context,
698
+ ):
699
+ yield chunk
700
+ return
701
+ except Exception as e:
702
+ yield {
703
+ "type": "error",
704
+ "message": str(e) or "LangGraph checkpoint resume failed",
705
+ "checkpoint_id": str(checkpoint_ref.get("checkpoint_id") or ""),
706
+ "exception_type": type(e).__name__,
707
+ }
708
+ return
709
+
710
+ if not hasattr(self._agent, "astream_events"):
711
+ result = await self.invoke(invoke_payload)
712
+ final_chunk = {"output": result.get("output", ""), "type": "final"}
713
+ usage = self._extract_usage(result)
714
+ if usage:
715
+ final_chunk["usage"] = usage
716
+ last_usage = self._extract_last_usage(result)
717
+ if last_usage:
718
+ final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
719
+ yield final_chunk
720
+ return
721
+
722
+ try:
723
+ stream_input = None if is_checkpoint_resume else (Command(resume=state) if is_resume else state)
724
+ stream_kwargs = {"version": "v2", "config": config}
725
+ if native_context and self._callable_accepts_keyword(self._agent.astream_events, "context"):
726
+ stream_kwargs["context"] = native_context
727
+ async for event in self._agent.astream_events(stream_input, **stream_kwargs):
728
+ event_kind = event.get("event", "")
729
+
730
+ if event_kind == "on_chat_model_stream":
731
+ chunk = event.get("data", {}).get("chunk")
732
+ if not chunk:
733
+ continue
734
+
735
+ # 推理内容
736
+ reasoning = getattr(chunk, "reasoning_content", None)
737
+ if not reasoning and hasattr(chunk, "additional_kwargs"):
738
+ reasoning = chunk.additional_kwargs.get("reasoning_content")
739
+
740
+ if reasoning:
741
+ accumulated_reasoning += reasoning
742
+ yield {"delta": reasoning, "type": "thinking"}
743
+
744
+ # 常规内容
745
+ if hasattr(chunk, "content") and chunk.content:
746
+ content = self._filter_tool_tags(chunk.content)
747
+ if isinstance(content, str):
748
+ if accumulated_reasoning and content.startswith(accumulated_reasoning):
749
+ content = content[len(accumulated_reasoning):]
750
+ elif reasoning and content.startswith(reasoning):
751
+ content = content[len(reasoning):]
752
+ if content:
753
+ for part in inline_reasoning_parser.feed(content):
754
+ if not part.text or not part.text.strip():
755
+ continue
756
+ if part.kind == "thinking":
757
+ accumulated_reasoning += part.text
758
+ yield {"delta": part.text, "type": "thinking"}
759
+ else:
760
+ accumulated_text += part.text
761
+ yield {"delta": part.text, "type": "text"}
762
+
763
+ elif event_kind == "on_tool_start":
764
+ emitted_non_text_event = True
765
+ yield {
766
+ "type": "tool_call",
767
+ "tool_name": event.get("name", "unknown"),
768
+ "tool_args": event.get("data", {}).get("input", {}),
769
+ "run_id": event.get("run_id"),
770
+ }
771
+
772
+ elif event_kind == "on_tool_end":
773
+ emitted_non_text_event = True
774
+ tool_output = event.get("data", {}).get("output", "")
775
+ yield {
776
+ "type": "tool_result",
777
+ "tool_name": event.get("name", "unknown"),
778
+ "tool_args": event.get("data", {}).get("input", {}),
779
+ "tool_output": tool_output if isinstance(tool_output, dict) else (str(tool_output) if tool_output else ""),
780
+ "run_id": event.get("run_id"),
781
+ }
782
+
783
+ elif event_kind == "on_chain_end":
784
+ output = event.get("data", {}).get("output", {})
785
+ if isinstance(output, dict) and "__interrupt__" in output:
786
+ emitted_non_text_event = True
787
+ yield {"type": "interrupt", "interrupt_info": output["__interrupt__"], "session_id": session_id}
788
+ return
789
+ extracted_output = self._extract_output(output)
790
+ if extracted_output:
791
+ final_output_text = strip_reasoning_markup(str(extracted_output))
792
+ final_output_usage = self._extract_usage(output)
793
+ final_output_last_usage = self._extract_last_usage(output)
794
+
795
+ except Exception as e:
796
+ if "Interrupt" in type(e).__name__:
797
+ yield {"type": "interrupt", "interrupt_info": self._get_interrupt_info(self._agent.get_state(config)), "session_id": session_id}
798
+ return
799
+ raise
800
+
801
+ for part in inline_reasoning_parser.flush():
802
+ if not part.text or not part.text.strip():
803
+ continue
804
+ if part.kind == "thinking":
805
+ accumulated_reasoning += part.text
806
+ yield {"delta": part.text, "type": "thinking"}
807
+ else:
808
+ accumulated_text += part.text
809
+ yield {"delta": part.text, "type": "text"}
810
+
811
+ if not accumulated_text:
812
+ if final_output_text:
813
+ final_chunk = {"output": final_output_text, "type": "final"}
814
+ if final_output_usage:
815
+ final_chunk["usage"] = final_output_usage
816
+ if final_output_last_usage:
817
+ final_chunk.setdefault("metadata", {})["last_usage"] = final_output_last_usage
818
+ yield final_chunk
819
+ elif not emitted_non_text_event:
820
+ result = await self.invoke(invoke_payload)
821
+ final_chunk = {"output": result.get("output", ""), "type": "final"}
822
+ usage = self._extract_usage(result)
823
+ if usage:
824
+ final_chunk["usage"] = usage
825
+ last_usage = self._extract_last_usage(result)
826
+ if last_usage:
827
+ final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
828
+ yield final_chunk
829
+ metadata = result.get("metadata") if isinstance(result, dict) else None
830
+ if isinstance(metadata, dict) and metadata.get("agentengine"):
831
+ yield {"type": "checkpoint", "metadata": metadata}
832
+ return
833
+ else:
834
+ final_chunk = {"output": accumulated_text, "type": "final"}
835
+ usage = await self._latest_state_usage(config)
836
+ if usage:
837
+ final_chunk["usage"] = usage
838
+ # _latest_state_usage 返回末个 message usage,单次调用场景即 last_usage
839
+ final_chunk.setdefault("metadata", {})["last_usage"] = usage
840
+ yield final_chunk
841
+
842
+ metadata = await self._latest_checkpoint_metadata(config)
843
+ if metadata:
844
+ yield {"type": "checkpoint", "metadata": metadata}
845
+
846
+ def _filter_tool_tags(self, content: str) -> str:
847
+ """过滤 <tool_call> 标签"""
848
+ if not isinstance(content, str):
849
+ return content
850
+ content = re.sub(r'<tool_call>.*?</tool_call>', '', content, flags=re.DOTALL)
851
+ content = re.sub(r'</?(?:tool_call|arg_key|arg_value)>', '', content)
852
+ return content