langchain-agentx-python 0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. langchain_agentx/__init__.py +46 -0
  2. langchain_agentx/command/__init__.py +28 -0
  3. langchain_agentx/command/builtin/__init__.py +25 -0
  4. langchain_agentx/command/builtin/clear.py +33 -0
  5. langchain_agentx/command/builtin/compact.py +33 -0
  6. langchain_agentx/command/builtin/memory.py +37 -0
  7. langchain_agentx/command/builtin/reload_plugins.py +42 -0
  8. langchain_agentx/command/context.py +30 -0
  9. langchain_agentx/command/dispatcher.py +183 -0
  10. langchain_agentx/command/registry.py +110 -0
  11. langchain_agentx/command/result.py +25 -0
  12. langchain_agentx/command/types.py +41 -0
  13. langchain_agentx/config/__init__.py +14 -0
  14. langchain_agentx/loop/__init__.py +47 -0
  15. langchain_agentx/loop/config/__init__.py +20 -0
  16. langchain_agentx/loop/config/agent_config.py +66 -0
  17. langchain_agentx/loop/config/agent_loop_config.py +72 -0
  18. langchain_agentx/loop/config/model_context_resolver.py +105 -0
  19. langchain_agentx/loop/config/runtime_settings.py +50 -0
  20. langchain_agentx/loop/config/token_estimator.py +133 -0
  21. langchain_agentx/loop/context/__init__.py +66 -0
  22. langchain_agentx/loop/context/blocking_guard.py +97 -0
  23. langchain_agentx/loop/context/compaction_service.py +60 -0
  24. langchain_agentx/loop/context/message_utils.py +56 -0
  25. langchain_agentx/loop/context/pipeline.py +127 -0
  26. langchain_agentx/loop/context/settings.py +103 -0
  27. langchain_agentx/loop/context/stages/__init__.py +29 -0
  28. langchain_agentx/loop/context/stages/autocompact.py +140 -0
  29. langchain_agentx/loop/context/stages/base.py +32 -0
  30. langchain_agentx/loop/context/stages/collapse.py +76 -0
  31. langchain_agentx/loop/context/stages/microcompact.py +76 -0
  32. langchain_agentx/loop/context/stages/noop.py +33 -0
  33. langchain_agentx/loop/context/stages/snip.py +71 -0
  34. langchain_agentx/loop/context/stages/tool_result_budget.py +69 -0
  35. langchain_agentx/loop/context/types.py +79 -0
  36. langchain_agentx/loop/exit/__init__.py +1 -0
  37. langchain_agentx/loop/exit/exit_logic.py +320 -0
  38. langchain_agentx/loop/exit/reason_codes.py +39 -0
  39. langchain_agentx/loop/graph/__init__.py +5 -0
  40. langchain_agentx/loop/graph/builtin_loop_control.py +197 -0
  41. langchain_agentx/loop/graph/factory.py +1409 -0
  42. langchain_agentx/loop/graph/graph_edges.py +820 -0
  43. langchain_agentx/loop/hook/__init__.py +48 -0
  44. langchain_agentx/loop/hook/async_hook_runner.py +62 -0
  45. langchain_agentx/loop/hook/config.py +280 -0
  46. langchain_agentx/loop/hook/engine.py +321 -0
  47. langchain_agentx/loop/hook/executors/__init__.py +9 -0
  48. langchain_agentx/loop/hook/executors/agent.py +107 -0
  49. langchain_agentx/loop/hook/executors/command.py +230 -0
  50. langchain_agentx/loop/hook/executors/http.py +114 -0
  51. langchain_agentx/loop/hook/executors/prompt.py +92 -0
  52. langchain_agentx/loop/hook/graph_wiring.py +134 -0
  53. langchain_agentx/loop/hook/registry.py +262 -0
  54. langchain_agentx/loop/hook/trust.py +43 -0
  55. langchain_agentx/loop/hook/types.py +110 -0
  56. langchain_agentx/loop/injection/__init__.py +13 -0
  57. langchain_agentx/loop/injection/dedup.py +74 -0
  58. langchain_agentx/loop/loop_abort.py +36 -0
  59. langchain_agentx/loop/model/__init__.py +1 -0
  60. langchain_agentx/loop/model/model_node.py +648 -0
  61. langchain_agentx/loop/model/model_nodes.py +661 -0
  62. langchain_agentx/loop/model/orphan_tool_results.py +38 -0
  63. langchain_agentx/loop/model/retrier.py +307 -0
  64. langchain_agentx/loop/model/retry_bridge.py +58 -0
  65. langchain_agentx/loop/model/retry_events.py +35 -0
  66. langchain_agentx/loop/model/retry_policy.py +56 -0
  67. langchain_agentx/loop/model/schema_and_format.py +153 -0
  68. langchain_agentx/loop/model/tool_and_model_binding.py +227 -0
  69. langchain_agentx/loop/model/tool_call_degradation_corrector.py +443 -0
  70. langchain_agentx/loop/model/tool_transcript_guard.py +225 -0
  71. langchain_agentx/loop/prompt/__init__.py +95 -0
  72. langchain_agentx/loop/prompt/builder.py +61 -0
  73. langchain_agentx/loop/prompt/builtin.py +218 -0
  74. langchain_agentx/loop/prompt/compact.py +408 -0
  75. langchain_agentx/loop/prompt/sections.py +120 -0
  76. langchain_agentx/loop/runtime/__init__.py +19 -0
  77. langchain_agentx/loop/runtime/context.py +34 -0
  78. langchain_agentx/loop/runtime/context_factory.py +107 -0
  79. langchain_agentx/loop/runtime/subagent_execution_paths.py +68 -0
  80. langchain_agentx/loop/subagent/__init__.py +53 -0
  81. langchain_agentx/loop/subagent/async_runner.py +215 -0
  82. langchain_agentx/loop/subagent/context.py +209 -0
  83. langchain_agentx/loop/subagent/fork_worktree_notice.py +25 -0
  84. langchain_agentx/loop/subagent/graph.py +72 -0
  85. langchain_agentx/loop/subagent/orchestrator.py +391 -0
  86. langchain_agentx/loop/subagent/progress.py +30 -0
  87. langchain_agentx/loop/subagent/prompt.py +52 -0
  88. langchain_agentx/loop/subagent/runner.py +504 -0
  89. langchain_agentx/loop/subagent/transcript.py +172 -0
  90. langchain_agentx/memory/__init__.py +2 -0
  91. langchain_agentx/memory/instruction/__init__.py +12 -0
  92. langchain_agentx/memory/instruction/loader.py +325 -0
  93. langchain_agentx/memory/instruction/resolver.py +24 -0
  94. langchain_agentx/memory/instruction/runtime.py +83 -0
  95. langchain_agentx/memory/instruction/sections.py +83 -0
  96. langchain_agentx/memory/instruction/types.py +59 -0
  97. langchain_agentx/memory/memdir/__init__.py +77 -0
  98. langchain_agentx/memory/memdir/age.py +36 -0
  99. langchain_agentx/memory/memdir/agent_memory.py +380 -0
  100. langchain_agentx/memory/memdir/extractor.py +309 -0
  101. langchain_agentx/memory/memdir/loader.py +187 -0
  102. langchain_agentx/memory/memdir/paths.py +63 -0
  103. langchain_agentx/memory/memdir/recall.py +45 -0
  104. langchain_agentx/memory/memdir/runtime.py +43 -0
  105. langchain_agentx/memory/memdir/scan.py +135 -0
  106. langchain_agentx/memory/memdir/types.py +104 -0
  107. langchain_agentx/memory/session/__init__.py +76 -0
  108. langchain_agentx/memory/session/compact_bridge.py +208 -0
  109. langchain_agentx/memory/session/prompts.py +172 -0
  110. langchain_agentx/memory/session/session_memory.py +282 -0
  111. langchain_agentx/observability/__init__.py +67 -0
  112. langchain_agentx/observability/evaluation/__init__.py +17 -0
  113. langchain_agentx/observability/evaluation/checkers/__init__.py +18 -0
  114. langchain_agentx/observability/evaluation/checkers/base.py +34 -0
  115. langchain_agentx/observability/evaluation/checkers/compaction.py +38 -0
  116. langchain_agentx/observability/evaluation/checkers/degradation.py +50 -0
  117. langchain_agentx/observability/evaluation/checkers/exit_quality.py +42 -0
  118. langchain_agentx/observability/evaluation/checkers/session_memory.py +45 -0
  119. langchain_agentx/observability/evaluation/checkers/tool_behavior.py +53 -0
  120. langchain_agentx/observability/evaluation/retention_scheduler.py +67 -0
  121. langchain_agentx/observability/evaluation/service.py +102 -0
  122. langchain_agentx/observability/evaluation/state.py +32 -0
  123. langchain_agentx/observability/evaluation/store.py +258 -0
  124. langchain_agentx/observability/events/__init__.py +15 -0
  125. langchain_agentx/observability/events/langchain_agentx_event_adapter.py +832 -0
  126. langchain_agentx/observability/logging/__init__.py +15 -0
  127. langchain_agentx/observability/logging/debug_burst.py +95 -0
  128. langchain_agentx/observability/logging/logging_config.py +178 -0
  129. langchain_agentx/observability/logging/logging_contract.py +65 -0
  130. langchain_agentx/observability/replay/__init__.py +35 -0
  131. langchain_agentx/observability/replay/cli.py +91 -0
  132. langchain_agentx/observability/replay/service.py +83 -0
  133. langchain_agentx/observability/replay/store.py +278 -0
  134. langchain_agentx/observability/replay/ui.py +47 -0
  135. langchain_agentx/observability/trace/__init__.py +25 -0
  136. langchain_agentx/observability/trace/collector.py +560 -0
  137. langchain_agentx/observability/trace/event_emitter.py +183 -0
  138. langchain_agentx/observability/trace/hook_event_emitter.py +49 -0
  139. langchain_agentx/observability/trace/models.py +144 -0
  140. langchain_agentx/observability/trace/sqlite_store.py +873 -0
  141. langchain_agentx/observability/trace/trace_callback.py +295 -0
  142. langchain_agentx/observability/trace/trace_lifecycle_collector.py +114 -0
  143. langchain_agentx/plugin/__init__.py +26 -0
  144. langchain_agentx/plugin/builtin.py +53 -0
  145. langchain_agentx/plugin/config.py +113 -0
  146. langchain_agentx/plugin/loader.py +386 -0
  147. langchain_agentx/plugin/manifest.py +154 -0
  148. langchain_agentx/plugin/registries.py +211 -0
  149. langchain_agentx/plugin/types.py +142 -0
  150. langchain_agentx/provider/__init__.py +27 -0
  151. langchain_agentx/provider/anthropic.py +121 -0
  152. langchain_agentx/provider/compatible_chat_openai.py +86 -0
  153. langchain_agentx/provider/env.py +45 -0
  154. langchain_agentx/provider/model_profile.py +156 -0
  155. langchain_agentx/provider/openai.py +89 -0
  156. langchain_agentx/session/__init__.py +17 -0
  157. langchain_agentx/session/agent_session.py +320 -0
  158. langchain_agentx/session/conversation_factory.py +87 -0
  159. langchain_agentx/session/conversation_recovery.py +156 -0
  160. langchain_agentx/session/conversation_session.py +198 -0
  161. langchain_agentx/session/factory.py +143 -0
  162. langchain_agentx/session/protocol.py +25 -0
  163. langchain_agentx/task_runtime/__init__.py +113 -0
  164. langchain_agentx/task_runtime/core/__init__.py +51 -0
  165. langchain_agentx/task_runtime/core/ids.py +33 -0
  166. langchain_agentx/task_runtime/core/interfaces.py +115 -0
  167. langchain_agentx/task_runtime/core/notification_priority.py +19 -0
  168. langchain_agentx/task_runtime/core/types.py +136 -0
  169. langchain_agentx/task_runtime/integrations/__init__.py +33 -0
  170. langchain_agentx/task_runtime/integrations/loop_adapter.py +91 -0
  171. langchain_agentx/task_runtime/integrations/loop_integration.py +61 -0
  172. langchain_agentx/task_runtime/integrations/prefetch_providers.py +108 -0
  173. langchain_agentx/task_runtime/integrations/provider_factory.py +103 -0
  174. langchain_agentx/task_runtime/integrations/queued_command_provider.py +184 -0
  175. langchain_agentx/task_runtime/integrations/sqlite_queued_command_provider.py +338 -0
  176. langchain_agentx/task_runtime/integrations/tool_use_summary_provider.py +254 -0
  177. langchain_agentx/task_runtime/orchestrator/__init__.py +5 -0
  178. langchain_agentx/task_runtime/orchestrator/runtime.py +386 -0
  179. langchain_agentx/task_runtime/output/__init__.py +5 -0
  180. langchain_agentx/task_runtime/output/sink.py +64 -0
  181. langchain_agentx/task_runtime/policy/__init__.py +11 -0
  182. langchain_agentx/task_runtime/policy/withhold_visibility.py +32 -0
  183. langchain_agentx/task_runtime/queue/__init__.py +5 -0
  184. langchain_agentx/task_runtime/queue/in_memory.py +55 -0
  185. langchain_agentx/task_runtime/skill_prefetch/__init__.py +4 -0
  186. langchain_agentx/task_runtime/skill_prefetch/attachments.py +46 -0
  187. langchain_agentx/task_runtime/skill_prefetch/models.py +37 -0
  188. langchain_agentx/task_runtime/skill_prefetch/provider.py +344 -0
  189. langchain_agentx/task_runtime/store/__init__.py +6 -0
  190. langchain_agentx/task_runtime/store/in_memory.py +81 -0
  191. langchain_agentx/task_runtime/store/sqlite_store.py +281 -0
  192. langchain_agentx/task_runtime/tasks/__init__.py +76 -0
  193. langchain_agentx/task_runtime/tasks/ai_analysis/__init__.py +15 -0
  194. langchain_agentx/task_runtime/tasks/ai_analysis/base.py +41 -0
  195. langchain_agentx/task_runtime/tasks/ai_analysis/evaluation.py +67 -0
  196. langchain_agentx/task_runtime/tasks/ai_analysis/registry.py +36 -0
  197. langchain_agentx/task_runtime/tasks/ai_analysis/scheduler.py +70 -0
  198. langchain_agentx/task_runtime/tasks/base/__init__.py +6 -0
  199. langchain_agentx/task_runtime/tasks/base/contracts.py +24 -0
  200. langchain_agentx/task_runtime/tasks/custom/__init__.py +7 -0
  201. langchain_agentx/task_runtime/tasks/custom/executor.py +60 -0
  202. langchain_agentx/task_runtime/tasks/custom/notification.py +7 -0
  203. langchain_agentx/task_runtime/tasks/custom/semantics.py +13 -0
  204. langchain_agentx/task_runtime/tasks/custom/spec.py +33 -0
  205. langchain_agentx/task_runtime/tasks/dream_task/__init__.py +15 -0
  206. langchain_agentx/task_runtime/tasks/dream_task/executor.py +61 -0
  207. langchain_agentx/task_runtime/tasks/dream_task/notification.py +19 -0
  208. langchain_agentx/task_runtime/tasks/dream_task/semantics.py +13 -0
  209. langchain_agentx/task_runtime/tasks/dream_task/spec.py +35 -0
  210. langchain_agentx/task_runtime/tasks/dream_task/state.py +17 -0
  211. langchain_agentx/task_runtime/tasks/in_process_teammate/__init__.py +12 -0
  212. langchain_agentx/task_runtime/tasks/in_process_teammate/executor.py +36 -0
  213. langchain_agentx/task_runtime/tasks/in_process_teammate/notification.py +25 -0
  214. langchain_agentx/task_runtime/tasks/in_process_teammate/semantics.py +13 -0
  215. langchain_agentx/task_runtime/tasks/in_process_teammate/spec.py +63 -0
  216. langchain_agentx/task_runtime/tasks/local_agent/__init__.py +14 -0
  217. langchain_agentx/task_runtime/tasks/local_agent/executor.py +33 -0
  218. langchain_agentx/task_runtime/tasks/local_agent/notification.py +21 -0
  219. langchain_agentx/task_runtime/tasks/local_agent/runner.py +43 -0
  220. langchain_agentx/task_runtime/tasks/local_agent/semantics.py +13 -0
  221. langchain_agentx/task_runtime/tasks/local_agent/spec.py +31 -0
  222. langchain_agentx/task_runtime/tasks/local_bash/__init__.py +13 -0
  223. langchain_agentx/task_runtime/tasks/local_bash/executor.py +95 -0
  224. langchain_agentx/task_runtime/tasks/local_bash/notification.py +22 -0
  225. langchain_agentx/task_runtime/tasks/local_bash/semantics.py +13 -0
  226. langchain_agentx/task_runtime/tasks/local_bash/spec.py +55 -0
  227. langchain_agentx/task_runtime/tasks/remote_agent/__init__.py +19 -0
  228. langchain_agentx/task_runtime/tasks/remote_agent/backend.py +76 -0
  229. langchain_agentx/task_runtime/tasks/remote_agent/executor.py +37 -0
  230. langchain_agentx/task_runtime/tasks/remote_agent/notification.py +22 -0
  231. langchain_agentx/task_runtime/tasks/remote_agent/semantics.py +13 -0
  232. langchain_agentx/task_runtime/tasks/remote_agent/spec.py +34 -0
  233. langchain_agentx/task_runtime/tasks/trace_cleanup/__init__.py +19 -0
  234. langchain_agentx/task_runtime/tasks/trace_cleanup/bootstrap.py +95 -0
  235. langchain_agentx/task_runtime/tasks/trace_cleanup/executor.py +66 -0
  236. langchain_agentx/task_runtime/tasks/trace_cleanup/scheduler.py +169 -0
  237. langchain_agentx/tool_runtime/__init__.py +90 -0
  238. langchain_agentx/tool_runtime/adapter.py +365 -0
  239. langchain_agentx/tool_runtime/base.py +319 -0
  240. langchain_agentx/tool_runtime/errors.py +190 -0
  241. langchain_agentx/tool_runtime/identical_call_cache.py +110 -0
  242. langchain_agentx/tool_runtime/loader.py +195 -0
  243. langchain_agentx/tool_runtime/models.py +260 -0
  244. langchain_agentx/tool_runtime/permission_context.py +78 -0
  245. langchain_agentx/tool_runtime/pipeline.py +621 -0
  246. langchain_agentx/tool_runtime/policy.py +447 -0
  247. langchain_agentx/tool_runtime/registry.py +81 -0
  248. langchain_agentx/tool_runtime/resolvers/__init__.py +27 -0
  249. langchain_agentx/tool_runtime/resolvers/agent_session.py +125 -0
  250. langchain_agentx/tool_runtime/resolvers/background.py +32 -0
  251. langchain_agentx/tool_runtime/resolvers/base.py +20 -0
  252. langchain_agentx/tool_runtime/resolvers/conversation.py +22 -0
  253. langchain_agentx/tool_runtime/resolvers/workflow.py +73 -0
  254. langchain_agentx/tool_runtime/session_store.py +132 -0
  255. langchain_agentx/tool_runtime/smoke_test_runtime.py +294 -0
  256. langchain_agentx/tool_runtime/state_bridge.py +164 -0
  257. langchain_agentx/tools/__init__.py +26 -0
  258. langchain_agentx/tools/agent/__init__.py +9 -0
  259. langchain_agentx/tools/agent/backend.py +53 -0
  260. langchain_agentx/tools/agent/built_in/__init__.py +19 -0
  261. langchain_agentx/tools/agent/built_in/agentx_guide.py +65 -0
  262. langchain_agentx/tools/agent/built_in/explore.py +80 -0
  263. langchain_agentx/tools/agent/built_in/general.py +57 -0
  264. langchain_agentx/tools/agent/built_in/plan.py +89 -0
  265. langchain_agentx/tools/agent/built_in/statusline_setup.py +64 -0
  266. langchain_agentx/tools/agent/built_in/verification.py +120 -0
  267. langchain_agentx/tools/agent/builtin_subagent_loader.py +89 -0
  268. langchain_agentx/tools/agent/cwd_resolution.py +119 -0
  269. langchain_agentx/tools/agent/limits.py +26 -0
  270. langchain_agentx/tools/agent/loader.py +270 -0
  271. langchain_agentx/tools/agent/models.py +85 -0
  272. langchain_agentx/tools/agent/prompt.py +120 -0
  273. langchain_agentx/tools/agent/registry/__init__.py +18 -0
  274. langchain_agentx/tools/agent/registry/config.py +29 -0
  275. langchain_agentx/tools/agent/registry/registry.py +47 -0
  276. langchain_agentx/tools/agent/scope.py +137 -0
  277. langchain_agentx/tools/agent/tool.py +256 -0
  278. langchain_agentx/tools/bash/__init__.py +9 -0
  279. langchain_agentx/tools/bash/ast_security.py +571 -0
  280. langchain_agentx/tools/bash/backend.py +1447 -0
  281. langchain_agentx/tools/bash/bash_hardening.py +734 -0
  282. langchain_agentx/tools/bash/bash_runtime_contract.py +41 -0
  283. langchain_agentx/tools/bash/cwd_reporter.py +95 -0
  284. langchain_agentx/tools/bash/limits.py +71 -0
  285. langchain_agentx/tools/bash/mode_validation.py +282 -0
  286. langchain_agentx/tools/bash/models.py +131 -0
  287. langchain_agentx/tools/bash/observability.py +148 -0
  288. langchain_agentx/tools/bash/output_utils.py +200 -0
  289. langchain_agentx/tools/bash/path_security.py +2429 -0
  290. langchain_agentx/tools/bash/prompt.py +68 -0
  291. langchain_agentx/tools/bash/read_only_validation.py +589 -0
  292. langchain_agentx/tools/bash/result_presenter.py +324 -0
  293. langchain_agentx/tools/bash/sandbox_decision.py +133 -0
  294. langchain_agentx/tools/bash/security.py +311 -0
  295. langchain_agentx/tools/bash/sed_edit_parser.py +243 -0
  296. langchain_agentx/tools/bash/sed_validation.py +163 -0
  297. langchain_agentx/tools/bash/semantics.py +111 -0
  298. langchain_agentx/tools/bash/session_manager.py +205 -0
  299. langchain_agentx/tools/bash/session_runtime.py +290 -0
  300. langchain_agentx/tools/bash/shell_locator.py +191 -0
  301. langchain_agentx/tools/bash/task_runtime.py +91 -0
  302. langchain_agentx/tools/bash/tool.py +939 -0
  303. langchain_agentx/tools/bash/windows_shell_quoting.py +45 -0
  304. langchain_agentx/tools/glob/__init__.py +9 -0
  305. langchain_agentx/tools/glob/models.py +57 -0
  306. langchain_agentx/tools/glob/pagination.py +30 -0
  307. langchain_agentx/tools/glob/prompt.py +24 -0
  308. langchain_agentx/tools/glob/rg_list_backend.py +139 -0
  309. langchain_agentx/tools/glob/rg_pattern.py +44 -0
  310. langchain_agentx/tools/glob/tool.py +327 -0
  311. langchain_agentx/tools/grep/__init__.py +7 -0
  312. langchain_agentx/tools/grep/backend.py +375 -0
  313. langchain_agentx/tools/grep/models.py +127 -0
  314. langchain_agentx/tools/grep/prompt.py +30 -0
  315. langchain_agentx/tools/grep/rg_subprocess_controller.py +114 -0
  316. langchain_agentx/tools/grep/tool.py +475 -0
  317. langchain_agentx/tools/read/__init__.py +9 -0
  318. langchain_agentx/tools/read/backend.py +415 -0
  319. langchain_agentx/tools/read/limits.py +67 -0
  320. langchain_agentx/tools/read/models.py +156 -0
  321. langchain_agentx/tools/read/prompt.py +73 -0
  322. langchain_agentx/tools/read/tool.py +494 -0
  323. langchain_agentx/tools/ripgrep_plugin_exclusions.py +137 -0
  324. langchain_agentx/tools/skill/__init__.py +4 -0
  325. langchain_agentx/tools/skill/argument_substitution.py +80 -0
  326. langchain_agentx/tools/skill/loader.py +196 -0
  327. langchain_agentx/tools/skill/models.py +88 -0
  328. langchain_agentx/tools/skill/policy.py +80 -0
  329. langchain_agentx/tools/skill/prompt.py +35 -0
  330. langchain_agentx/tools/skill/tool.py +222 -0
  331. langchain_agentx/utils/__init__.py +0 -0
  332. langchain_agentx/utils/cwd.py +124 -0
  333. langchain_agentx/utils/host_platform.py +112 -0
  334. langchain_agentx/utils/path_hierarchy.py +48 -0
  335. langchain_agentx/utils/path_user_input.py +66 -0
  336. langchain_agentx/utils/rg_executable.py +18 -0
  337. langchain_agentx/utils/subprocess_text.py +101 -0
  338. langchain_agentx/utils/temp_paths.py +77 -0
  339. langchain_agentx/utils/unc_path.py +25 -0
  340. langchain_agentx/utils/win_reserved_paths.py +51 -0
  341. langchain_agentx/workflow/__init__.py +7 -0
  342. langchain_agentx/workflow/base.py +97 -0
  343. langchain_agentx/workflow/batch.py +55 -0
  344. langchain_agentx/workflow/dag.py +54 -0
  345. langchain_agentx/workspace/__init__.py +13 -0
  346. langchain_agentx/workspace/config.py +140 -0
  347. langchain_agentx/workspace/path_key_normalizer.py +30 -0
  348. langchain_agentx/workspace/resolver.py +74 -0
  349. langchain_agentx/workspace/validators.py +41 -0
  350. langchain_agentx_python-0.1.dist-info/LICENSE +201 -0
  351. langchain_agentx_python-0.1.dist-info/METADATA +513 -0
  352. langchain_agentx_python-0.1.dist-info/RECORD +354 -0
  353. langchain_agentx_python-0.1.dist-info/WHEEL +5 -0
  354. langchain_agentx_python-0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,560 @@
1
+ """
2
+ TraceCollector:统一的 Trace 数据汇聚点。
3
+
4
+ 职责:
5
+ 1. 维护内存中的 span 树(按 session_id 隔离)
6
+ 2. 提供幂等的 session 管理
7
+ 3. 提供查询 API(替代 PerformanceMonitor)
8
+
9
+ 设计要点:
10
+ - 不依赖 ContextVar,所有方法显式传递 session_id
11
+ - start_session() 幂等:已存在则跳过
12
+ - 线程安全:使用 Lock 保护 _sessions 字典
13
+ """
14
+
15
+ import time
16
+ import uuid
17
+ import logging
18
+ from threading import Lock
19
+ from typing import Dict, List, Optional, Any
20
+
21
+ from .models import (
22
+ TraceSession,
23
+ TraceSpan,
24
+ SessionBuffer,
25
+ SpanType,
26
+ SpanStatus,
27
+ )
28
+
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class TraceCollector:
34
+ """Trace 数据收集器"""
35
+
36
+ def __init__(self, store: Optional[Any] = None):
37
+ """
38
+ 初始化 TraceCollector。
39
+
40
+ Args:
41
+ store: SqliteTraceStore 实例(可选,Phase 2 实现)
42
+ """
43
+ self._store = store
44
+ self._sessions: Dict[str, SessionBuffer] = {}
45
+ self._lock = Lock()
46
+
47
+ # ========= 采集 API(幂等) =========
48
+
49
+ def has_session(self, session_id: str) -> bool:
50
+ """
51
+ 检查 session 是否已启动。
52
+
53
+ Args:
54
+ session_id: session 唯一标识
55
+
56
+ Returns:
57
+ True 如果 session 已存在
58
+ """
59
+ with self._lock:
60
+ return session_id in self._sessions
61
+
62
+ def get_session_buffer(self, session_id: str) -> Optional[SessionBuffer]:
63
+ """返回 session 的内存缓冲区,供评估系统读取。"""
64
+ with self._lock:
65
+ return self._sessions.get(session_id)
66
+
67
+ def start_session(
68
+ self,
69
+ session_id: str,
70
+ graph_name: str = "unknown",
71
+ user_query: Optional[str] = None,
72
+ **kwargs
73
+ ) -> None:
74
+ """
75
+ 启动一个 trace session(幂等:已存在则跳过)。
76
+
77
+ Args:
78
+ session_id: session 唯一标识(通常是 run_id)
79
+ graph_name: Agent 名称
80
+ user_query: 用户查询(可选)
81
+ **kwargs: 其他元数据
82
+ """
83
+ with self._lock:
84
+ if session_id in self._sessions:
85
+ return # 已存在,跳过
86
+
87
+ conversation_session_id = str(kwargs.pop("conversation_session_id", "") or session_id)
88
+ parent_session_id = kwargs.pop("parent_session_id", None)
89
+ parent_tool_call_id = kwargs.pop("parent_tool_call_id", None)
90
+ workflow_id = kwargs.pop("workflow_id", None)
91
+ unit_type = str(kwargs.pop("unit_type", "main_loop") or "main_loop")
92
+ container_type = str(kwargs.pop("container_type", "agent_session") or "agent_session")
93
+ origin = str(kwargs.pop("origin", "user") or "user")
94
+ visibility = str(kwargs.pop("visibility", "default") or "default")
95
+ session = TraceSession(
96
+ session_id=session_id,
97
+ conversation_session_id=conversation_session_id,
98
+ parent_session_id=parent_session_id,
99
+ parent_tool_call_id=parent_tool_call_id,
100
+ workflow_id=workflow_id,
101
+ unit_type=unit_type,
102
+ container_type=container_type,
103
+ origin=origin,
104
+ visibility=visibility,
105
+ graph_name=graph_name,
106
+ user_query=user_query,
107
+ start_time=time.time(),
108
+ metadata=kwargs
109
+ )
110
+
111
+ self._sessions[session_id] = SessionBuffer(session=session)
112
+
113
+ def end_session(
114
+ self,
115
+ session_id: str,
116
+ finish_reason: Optional[str] = None,
117
+ **kwargs
118
+ ) -> None:
119
+ """
120
+ 结束一个 session。
121
+
122
+ Args:
123
+ session_id: session 唯一标识
124
+ finish_reason: 退出原因
125
+ **kwargs: 其他元数据
126
+ """
127
+ should_flush = False
128
+ with self._lock:
129
+ if session_id not in self._sessions:
130
+ return
131
+
132
+ buffer = self._sessions[session_id]
133
+ buffer.session.end_time = time.time()
134
+ buffer.session.status = str(kwargs.get("status", "completed") or "completed")
135
+ buffer.session.finish_reason = finish_reason
136
+ if "exit_code" in kwargs:
137
+ buffer.session.exit_code = kwargs.get("exit_code")
138
+ if "terminal_reason" in kwargs:
139
+ buffer.session.terminal_reason = kwargs.get("terminal_reason")
140
+
141
+ # 计算聚合指标
142
+ buffer.session.loop_count = sum(
143
+ 1 for span in buffer.spans if span.span_type == SpanType.LOOP_STEP
144
+ )
145
+ llm_spans = [span for span in buffer.spans if span.span_type == SpanType.LLM_CALL]
146
+ buffer.session.total_input_tokens = sum(
147
+ self._as_int((span.metadata or {}).get("input_tokens")) for span in llm_spans
148
+ )
149
+ buffer.session.total_output_tokens = sum(
150
+ self._as_int((span.metadata or {}).get("output_tokens")) for span in llm_spans
151
+ )
152
+ buffer.session.total_latency_ms = (
153
+ (buffer.session.end_time - buffer.session.start_time) * 1000
154
+ if buffer.session.end_time
155
+ else 0
156
+ )
157
+ should_flush = True
158
+
159
+ # 在会话结束时自动持久化,避免调用方漏调 flush() 导致数据仅停留在内存中。
160
+ if should_flush:
161
+ self.flush(session_id)
162
+
163
+ def emit_event(
164
+ self,
165
+ *,
166
+ session_id: str,
167
+ event_type: str,
168
+ payload: Optional[Dict[str, Any]] = None,
169
+ parent_span_id: Optional[str] = None,
170
+ ) -> str:
171
+ """统一事件采集入口(用于 loop/task 决策类事件)。"""
172
+ payload = payload or {}
173
+ span_type_map = {
174
+ "loop.decision": SpanType.LOOP_DECISION.value,
175
+ "task.reserve": SpanType.TASK_EVENT.value,
176
+ "task.ack": SpanType.TASK_EVENT.value,
177
+ "hook.execution": SpanType.HOOK_EXEC.value,
178
+ }
179
+ span_type = span_type_map.get(event_type, SpanType.TASK_EVENT.value)
180
+ span_id = self.span_start(
181
+ span_type=span_type,
182
+ name=event_type,
183
+ session_id=session_id,
184
+ parent_span_id=parent_span_id,
185
+ metadata=payload,
186
+ )
187
+ self.span_end(span_id=span_id, session_id=session_id)
188
+ return span_id
189
+
190
+ def span_start(
191
+ self,
192
+ span_type: str,
193
+ name: str,
194
+ session_id: str,
195
+ parent_span_id: Optional[str] = None,
196
+ input_data: Optional[Dict[str, Any]] = None,
197
+ metadata: Optional[Dict[str, Any]] = None,
198
+ ) -> str:
199
+ """
200
+ 开始一个 span。
201
+
202
+ Args:
203
+ span_type: span 类型(agent_run / loop_step / llm_call / tool_call)
204
+ name: span 名称
205
+ session_id: 所属 session
206
+ parent_span_id: 父 span ID(可选)
207
+ input_data: 输入数据
208
+ metadata: 元数据
209
+
210
+ Returns:
211
+ span_id: 新创建的 span ID
212
+
213
+ Raises:
214
+ RuntimeError: 如果 session 未启动
215
+ """
216
+ with self._lock:
217
+ if session_id not in self._sessions:
218
+ raise RuntimeError(
219
+ f"trace session is not started: {session_id}. "
220
+ f"Call start_session() first or ensure _ensure_session() is called."
221
+ )
222
+
223
+ buffer = self._sessions[session_id]
224
+ metadata = metadata or {}
225
+ metadata = dict(metadata)
226
+ metadata["seq_no"] = buffer.next_seq_no
227
+ buffer.next_seq_no += 1
228
+
229
+ # 自动父 span 推断:若调用方未显式传 parent_span_id,则挂到“当前最近打开且未关闭”的 span。
230
+ # 依赖 buffer.span_stack 维护的 FILO 语义;显式传参优先,不覆盖调用方意图。
231
+ if parent_span_id is None and buffer.span_stack:
232
+ parent_span_id = buffer.span_stack[-1]
233
+
234
+ span_id = str(uuid.uuid4())
235
+ span = TraceSpan(
236
+ span_id=span_id,
237
+ session_id=session_id,
238
+ parent_span_id=parent_span_id,
239
+ span_type=SpanType(span_type) if isinstance(span_type, str) else span_type,
240
+ name=name,
241
+ start_time=time.time(),
242
+ input_data=input_data,
243
+ metadata=metadata
244
+ )
245
+
246
+ buffer.add_span(span)
247
+ buffer.span_stack.append(span_id)
248
+
249
+ return span_id
250
+
251
+ def span_end(
252
+ self,
253
+ span_id: str,
254
+ session_id: str,
255
+ output_data: Optional[Dict[str, Any]] = None,
256
+ error: Optional[str] = None,
257
+ metadata: Optional[Dict[str, Any]] = None,
258
+ ) -> None:
259
+ """
260
+ 结束一个 span。
261
+
262
+ Args:
263
+ span_id: span 唯一标识
264
+ session_id: 所属 session
265
+ output_data: 输出数据
266
+ error: 错误信息(如果失败)
267
+ metadata: 额外元数据
268
+ """
269
+ span_to_persist: TraceSpan | None = None
270
+ with self._lock:
271
+ if session_id not in self._sessions:
272
+ return
273
+
274
+ buffer = self._sessions[session_id]
275
+ span = buffer.get_span(span_id)
276
+
277
+ if not span:
278
+ return
279
+
280
+ span.end_time = time.time()
281
+ span.latency_ms = (span.end_time - span.start_time) * 1000
282
+ span.status = SpanStatus.FAILED if error else SpanStatus.COMPLETED
283
+ span.output_data = output_data
284
+ span.error = error
285
+
286
+ if metadata:
287
+ span.metadata.update(metadata)
288
+
289
+ # 从栈中移除
290
+ if span_id in buffer.span_stack:
291
+ buffer.span_stack.remove(span_id)
292
+ span_to_persist = span
293
+
294
+ # 双写:span 结束时立即落盘,降低异常中断时数据丢失概率。
295
+ if span_to_persist is not None and self._store is not None:
296
+ self._store.save_spans([span_to_persist])
297
+
298
+ def flush(self, session_id: str) -> None:
299
+ """
300
+ 将 session 聚合信息写入存储并清理内存。
301
+
302
+ Args:
303
+ session_id: session 唯一标识
304
+ """
305
+ with self._lock:
306
+ if session_id not in self._sessions:
307
+ return
308
+
309
+ buffer = self._sessions[session_id]
310
+
311
+ # 写入 SQLite:span 已在 span_end 双写,不再重复落盘。
312
+ if self._store:
313
+ self._store.save_session(buffer.session)
314
+ logger.debug("Flushed session %s to SQLite", session_id)
315
+ else:
316
+ logger.debug("Flushing session %s (no store)", session_id)
317
+ logger.debug(
318
+ "Trace session summary: graph=%s spans=%s loop_count=%s total_latency_ms=%.2f",
319
+ buffer.session.graph_name,
320
+ len(buffer.spans),
321
+ buffer.session.loop_count,
322
+ buffer.session.total_latency_ms,
323
+ )
324
+
325
+ # 清理内存
326
+ del self._sessions[session_id]
327
+
328
+ # ========= 查询 API(Phase 2 实现) =========
329
+
330
+ def get_session(self, session_id: str) -> Optional[TraceSession]:
331
+ """获取 session"""
332
+ # 先查内存
333
+ with self._lock:
334
+ if session_id in self._sessions:
335
+ return self._sessions[session_id].session
336
+
337
+ # 再查 SQLite
338
+ if self._store:
339
+ return self._store.get_session(session_id)
340
+
341
+ return None
342
+
343
+ def query_sessions(self, **filters) -> List[TraceSession]:
344
+ """查询 session 列表"""
345
+ if self._store:
346
+ return self._store.query_sessions(**filters)
347
+ return []
348
+
349
+ def get_span_tree(self, session_id: str) -> List[TraceSpan]:
350
+ """获取 session 的所有 span"""
351
+ # 先查内存
352
+ with self._lock:
353
+ if session_id in self._sessions:
354
+ return self._sessions[session_id].spans.copy()
355
+
356
+ # 再查 SQLite
357
+ if self._store:
358
+ return self._store.get_span_tree(session_id)
359
+
360
+ return []
361
+
362
+ def get_session_metrics(self, session_id: str) -> Dict[str, Any]:
363
+ """返回单个 session 的聚合指标。"""
364
+ spans = self.get_span_tree(session_id)
365
+ session = self.get_session(session_id)
366
+ return self._build_session_metrics(session_id=session_id, session=session, spans=spans)
367
+
368
+ def get_sessions_analysis(self, session_ids: List[str]) -> Dict[str, Dict[str, Any]]:
369
+ """
370
+ 批量计算多个 session 的 metrics/warnings/degradation,避免重复查库。
371
+ 返回字典按传入 session_ids 顺序插入,便于上层直接按输入顺序消费。
372
+ 返回结构:
373
+ {
374
+ session_id: {
375
+ "metrics": ...,
376
+ "warnings": ...,
377
+ "degradation": ...,
378
+ }
379
+ }
380
+ """
381
+ if not session_ids:
382
+ return {}
383
+ sessions: Dict[str, TraceSession] = {}
384
+ if self._store and hasattr(self._store, "get_sessions_by_ids"):
385
+ sessions = self._store.get_sessions_by_ids(session_ids)
386
+ else:
387
+ sessions = {
388
+ session_id: session
389
+ for session_id in session_ids
390
+ for session in [self.get_session(session_id)]
391
+ if session is not None
392
+ }
393
+ spans_map: Dict[str, List[TraceSpan]] = {}
394
+ if self._store and hasattr(self._store, "get_span_trees"):
395
+ spans_map = self._store.get_span_trees(session_ids)
396
+ else:
397
+ spans_map = {sid: self.get_span_tree(sid) for sid in session_ids}
398
+
399
+ result: Dict[str, Dict[str, Any]] = {}
400
+ for sid in session_ids:
401
+ spans = spans_map.get(sid, [])
402
+ session = sessions.get(sid)
403
+ metrics = self._build_session_metrics(session_id=sid, session=session, spans=spans)
404
+ warnings = self._build_performance_warnings(spans=spans)
405
+ degradation = self._build_degradation_stats(spans=spans)
406
+ result[sid] = {
407
+ "metrics": metrics,
408
+ "warnings": warnings,
409
+ "degradation": degradation,
410
+ }
411
+ return result
412
+
413
+ def _build_session_metrics(
414
+ self,
415
+ *,
416
+ session_id: str,
417
+ session: Optional[TraceSession],
418
+ spans: List[TraceSpan],
419
+ ) -> Dict[str, Any]:
420
+ ordered = self._ordered_spans(spans)
421
+ tool_spans = [span for span in ordered if span.span_type == SpanType.TOOL_CALL]
422
+ llm_spans = [span for span in ordered if span.span_type == SpanType.LLM_CALL]
423
+ tool_call_chain = [span.name for span in tool_spans]
424
+ llm_call_chain = [span.name for span in llm_spans]
425
+ complete_call_chain = [f"{span.span_type.value}:{span.name}" for span in ordered]
426
+ total_llm_latency_ms = sum((span.latency_ms or 0.0) for span in llm_spans)
427
+ total_input_tokens = sum(
428
+ self._as_int((span.metadata or {}).get("input_tokens")) for span in llm_spans
429
+ )
430
+ total_output_tokens = sum(
431
+ self._as_int((span.metadata or {}).get("output_tokens")) for span in llm_spans
432
+ )
433
+ return {
434
+ "session_id": session_id,
435
+ "total_tool_calls": len(tool_spans),
436
+ "total_latency_ms": session.total_latency_ms if session else 0.0,
437
+ "tool_call_chain": tool_call_chain,
438
+ "unique_tools": sorted(set(tool_call_chain)),
439
+ "context_tokens": total_input_tokens,
440
+ "total_input_tokens": total_input_tokens,
441
+ "total_output_tokens": total_output_tokens,
442
+ "total_llm_calls": len(llm_spans),
443
+ "llm_call_chain": llm_call_chain,
444
+ "total_llm_latency_ms": total_llm_latency_ms,
445
+ "complete_call_chain": complete_call_chain,
446
+ }
447
+
448
+ def get_performance_warnings(self, session_id: str) -> List[str]:
449
+ """返回单个 session 的性能告警。"""
450
+ spans = self.get_span_tree(session_id)
451
+ return self._build_performance_warnings(spans=spans)
452
+
453
+ def _build_performance_warnings(self, *, spans: List[TraceSpan]) -> List[str]:
454
+ ordered = self._ordered_spans(spans)
455
+ warnings: List[str] = []
456
+ tool_spans = [span for span in ordered if span.span_type == SpanType.TOOL_CALL]
457
+ llm_spans = [span for span in ordered if span.span_type == SpanType.LLM_CALL]
458
+ if len(tool_spans) > 10:
459
+ warnings.append(f"工具调用次数偏高: {len(tool_spans)} (>10)")
460
+ if tool_spans:
461
+ avg_tool_latency_ms = sum((span.latency_ms or 0.0) for span in tool_spans) / len(tool_spans)
462
+ if avg_tool_latency_ms > 2000:
463
+ warnings.append(f"工具平均耗时偏高: {avg_tool_latency_ms:.0f}ms (>2000ms)")
464
+ if llm_spans:
465
+ avg_llm_latency_ms = sum((span.latency_ms or 0.0) for span in llm_spans) / len(llm_spans)
466
+ if avg_llm_latency_ms > 5000:
467
+ warnings.append(f"LLM 平均耗时偏高: {avg_llm_latency_ms:.0f}ms (>5000ms)")
468
+ return warnings
469
+
470
+ def get_degradation_stats(self, session_id: str) -> Dict[str, Any]:
471
+ """返回工具调用退化统计。"""
472
+ spans = self.get_span_tree(session_id)
473
+ return self._build_degradation_stats(spans=spans)
474
+
475
+ def _build_degradation_stats(self, *, spans: List[TraceSpan]) -> Dict[str, Any]:
476
+ llm_spans = [span for span in spans if span.span_type == SpanType.LLM_CALL]
477
+ degradation_hits: List[TraceSpan] = []
478
+ tool_name_distribution: Dict[str, int] = {}
479
+ for span in llm_spans:
480
+ meta = span.metadata or {}
481
+ degradation = meta.get("degradation")
482
+ detected = False
483
+ tool_names: List[str] = []
484
+ if isinstance(degradation, dict):
485
+ detected = bool(degradation.get("detected"))
486
+ raw_names = degradation.get("tool_names")
487
+ if isinstance(raw_names, list):
488
+ tool_names = [str(item) for item in raw_names if str(item)]
489
+ elif isinstance(degradation, bool):
490
+ detected = degradation
491
+ if detected:
492
+ degradation_hits.append(span)
493
+ for tool_name in tool_names:
494
+ tool_name_distribution[tool_name] = tool_name_distribution.get(tool_name, 0) + 1
495
+ total_llm_calls = len(llm_spans)
496
+ degradation_count = len(degradation_hits)
497
+ total_input_on_degradation = sum(
498
+ self._as_int((span.metadata or {}).get("input_tokens")) for span in degradation_hits
499
+ )
500
+ return {
501
+ "total_llm_calls": total_llm_calls,
502
+ "degradation_count": degradation_count,
503
+ "degradation_rate_percent": (
504
+ (degradation_count / total_llm_calls) * 100 if total_llm_calls else 0.0
505
+ ),
506
+ "tool_name_distribution": tool_name_distribution,
507
+ "avg_prompt_length_on_degradation": (
508
+ total_input_on_degradation / degradation_count if degradation_count else 0.0
509
+ ),
510
+ }
511
+
512
+ def generate_report(self, session_id: str, mode: str = "debug") -> str:
513
+ """生成 session 报告文本。"""
514
+ mode_normalized = str(mode).lower()
515
+ if mode_normalized == "production":
516
+ return ""
517
+ metrics = self.get_session_metrics(session_id)
518
+ warnings = self.get_performance_warnings(session_id)
519
+ degradation = self.get_degradation_stats(session_id)
520
+ lines = [
521
+ f"Trace Report [{session_id}]",
522
+ f"- Tool calls: {metrics['total_tool_calls']}",
523
+ f"- LLM calls: {metrics['total_llm_calls']}",
524
+ f"- Total latency: {metrics['total_latency_ms']:.1f}ms",
525
+ f"- LLM latency: {metrics['total_llm_latency_ms']:.1f}ms",
526
+ f"- Tokens: in={metrics['total_input_tokens']} out={metrics['total_output_tokens']}",
527
+ (
528
+ f"- Degradation: {degradation['degradation_count']}/{degradation['total_llm_calls']}"
529
+ f" ({degradation['degradation_rate_percent']:.1f}%)"
530
+ ),
531
+ ]
532
+ if warnings:
533
+ lines.append("- Warnings:")
534
+ lines.extend([f" - {item}" for item in warnings])
535
+ else:
536
+ lines.append("- Warnings: none")
537
+ if mode_normalized == "benchmark":
538
+ lines.append(f"- Tool chain: {', '.join(metrics['tool_call_chain']) or '-'}")
539
+ lines.append(f"- LLM chain: {', '.join(metrics['llm_call_chain']) or '-'}")
540
+ if degradation["tool_name_distribution"]:
541
+ lines.append(f"- Degradation tools: {degradation['tool_name_distribution']}")
542
+ return "\n".join(lines)
543
+
544
+ @staticmethod
545
+ def _as_int(value: Any) -> int:
546
+ try:
547
+ return int(value)
548
+ except (TypeError, ValueError):
549
+ return 0
550
+
551
+ @staticmethod
552
+ def _ordered_spans(spans: List[TraceSpan]) -> List[TraceSpan]:
553
+ def seq_no(span: TraceSpan) -> int:
554
+ raw = (span.metadata or {}).get("seq_no")
555
+ try:
556
+ return int(raw)
557
+ except (TypeError, ValueError):
558
+ return 10**9
559
+
560
+ return sorted(spans, key=lambda span: (seq_no(span), span.start_time or 0.0))