langchain-agentx-python 1.2.7__py3-none-any.whl → 1.2.8__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.
@@ -11,7 +11,7 @@ from langchain_agentx import create_loop_agent
11
11
  ```
12
12
  """
13
13
 
14
- __version__ = "1.2.7"
14
+ __version__ = "1.2.8"
15
15
 
16
16
  from .loop import ( # noqa: F401
17
17
  create_loop_agent,
@@ -32,7 +32,7 @@ from langchain_core.messages import AIMessage, AnyMessage, HumanMessage, ToolMes
32
32
  from langgraph._internal._runnable import RunnableCallable
33
33
  from langgraph.graph.state import StateGraph
34
34
  from langgraph.prebuilt.tool_node import ToolNode
35
- from ..hook.async_hook_runner import AsyncHookRunner
35
+ from ..hook.async_hook_runner import AsyncHookRunner, get_process_async_hook_runner
36
36
  from ..hook.hook_projection import hook_result_to_event_dict
37
37
  from ..hook.types import HookContext, HookEvent
38
38
 
@@ -96,7 +96,7 @@ def _execute_hook_common(
96
96
  return None
97
97
  hook_runner = state.get("_hook_runner")
98
98
  if not isinstance(hook_runner, AsyncHookRunner):
99
- hook_runner = AsyncHookRunner()
99
+ hook_runner = get_process_async_hook_runner()
100
100
  ctx_kwargs: dict[str, Any] = {
101
101
  "event": event,
102
102
  "state": state,
@@ -12,7 +12,11 @@ loop.hook — Hook 子系统(P1/P2)。
12
12
  Phase 1:新增 HookProgressPayloadBuilder 用于 progress 展示元数据构建。
13
13
  """
14
14
 
15
- from .async_hook_runner import AsyncHookRunner
15
+ from .async_hook_runner import (
16
+ AsyncHookRunner,
17
+ get_process_async_hook_runner,
18
+ shutdown_process_async_hook_runner,
19
+ )
16
20
  from .config import HookCandidate, HookCandidateDecision, HookSpec, HooksConfigSnapshot
17
21
  from .engine import HookEngine
18
22
  from .executors import AgentExecutor, CommandExecutor, HttpExecutor, PromptExecutor
@@ -40,6 +44,8 @@ from .types import (
40
44
  __all__ = [
41
45
  "AggregatedHookResult",
42
46
  "AsyncHookRunner",
47
+ "get_process_async_hook_runner",
48
+ "shutdown_process_async_hook_runner",
43
49
  "ATTACHMENT_HOOK_BLOCKING_ERROR",
44
50
  "ATTACHMENT_HOOK_CANCELLED",
45
51
  "ATTACHMENT_HOOK_NON_BLOCKING_ERROR",
@@ -8,12 +8,14 @@ async_hook_runner.py — Hook 同步入口的异步执行协作者。
8
8
  HookGraphWiring 与 graph_edges 通过本协作者调用 HookEngine.execute()。
9
9
 
10
10
  当前裁剪范围:
11
- P1 仅实现线程桥接执行;不引入 nest_asyncio,不修改外部事件循环。
11
+ P1 仅实现线程桥接执行;不引入 nest_asyncio,不修改外部 event loop。
12
+ 进程级单例 ThreadPoolExecutor,atexit 显式 shutdown,禁止 fallback 裸 new。
12
13
  """
13
14
 
14
15
  from __future__ import annotations
15
16
 
16
17
  import asyncio
18
+ import atexit
17
19
  from concurrent.futures import ThreadPoolExecutor
18
20
  from threading import Lock
19
21
 
@@ -45,7 +47,10 @@ class AsyncHookRunner:
45
47
  def _get_or_create_executor(self) -> ThreadPoolExecutor:
46
48
  with self._executor_lock:
47
49
  if self._executor is None:
48
- self._executor = ThreadPoolExecutor(max_workers=1)
50
+ self._executor = ThreadPoolExecutor(
51
+ max_workers=1,
52
+ thread_name_prefix="async-hook-runner",
53
+ )
49
54
  return self._executor
50
55
 
51
56
  def close(self) -> None:
@@ -59,4 +64,33 @@ class AsyncHookRunner:
59
64
  return asyncio.run(hook_engine.execute(ctx))
60
65
 
61
66
 
62
- __all__ = ["AsyncHookRunner"]
67
+ _process_runner: AsyncHookRunner | None = None
68
+ _process_runner_lock = Lock()
69
+
70
+
71
+ def get_process_async_hook_runner() -> AsyncHookRunner:
72
+ """进程级 Hook 桥接 runner(单例,避免 graph_edges fallback 泄漏线程池)。"""
73
+ global _process_runner
74
+ with _process_runner_lock:
75
+ if _process_runner is None:
76
+ _process_runner = AsyncHookRunner()
77
+ return _process_runner
78
+
79
+
80
+ def shutdown_process_async_hook_runner() -> None:
81
+ """关闭进程级 runner 线程池(测试 / 进程退出)。"""
82
+ global _process_runner
83
+ with _process_runner_lock:
84
+ if _process_runner is not None:
85
+ _process_runner.close()
86
+ _process_runner = None
87
+
88
+
89
+ atexit.register(shutdown_process_async_hook_runner)
90
+
91
+
92
+ __all__ = [
93
+ "AsyncHookRunner",
94
+ "get_process_async_hook_runner",
95
+ "shutdown_process_async_hook_runner",
96
+ ]
@@ -17,7 +17,7 @@ from dataclasses import dataclass, field
17
17
  from typing import Any
18
18
  from uuid import uuid4
19
19
 
20
- from .async_hook_runner import AsyncHookRunner
20
+ from .async_hook_runner import AsyncHookRunner, get_process_async_hook_runner
21
21
  from .engine import HookEngine
22
22
  from .hook_projection import derive_hook_outcome, hook_result_to_event_dict
23
23
  from .types import AggregatedHookResult, HookContext, HookEvent
@@ -44,7 +44,7 @@ class HookGraphWiring:
44
44
  workspace_cfg: Any | None = None
45
45
  session_id: str | None = None
46
46
  container_type: str = "interactive"
47
- hook_runner: AsyncHookRunner = field(default_factory=AsyncHookRunner)
47
+ hook_runner: AsyncHookRunner = field(default_factory=get_process_async_hook_runner)
48
48
 
49
49
  def loop_start_node(self, state: dict[str, Any], runtime: Any) -> dict[str, Any]:
50
50
  run_id = state.get("_run_id")
@@ -29,6 +29,7 @@ task_runtime/tasklist/store.py — 任务存储核心
29
29
  from __future__ import annotations
30
30
 
31
31
  import asyncio
32
+ import atexit
32
33
  import json
33
34
  from collections.abc import Coroutine
34
35
  from concurrent.futures import ThreadPoolExecutor
@@ -514,6 +515,14 @@ _T = TypeVar("_T")
514
515
  _ASYNC_RUNNER = ThreadPoolExecutor(max_workers=1, thread_name_prefix="taskstore-sync")
515
516
 
516
517
 
518
+ def shutdown_taskstore_async_runner(*, wait: bool = True) -> None:
519
+ """关闭 TaskStoreSync 桥接线程池(测试 / 进程退出)。"""
520
+ _ASYNC_RUNNER.shutdown(wait=wait)
521
+
522
+
523
+ atexit.register(shutdown_taskstore_async_runner)
524
+
525
+
517
526
  def _run_async(coro_factory: Callable[[], Coroutine[Any, Any, _T]]) -> _T:
518
527
  """在同步上下文中执行协程;若已有 running loop 则在线程内 asyncio.run。"""
519
528
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langchain-agentx-python
3
- Version: 1.2.7
3
+ Version: 1.2.8
4
4
  Summary: LangChain/LangGraph-based agent utilities for CodeBaseX.
5
5
  Author-email: GoodMood2008 <GoodMood2008@users.noreply.github.com>
6
6
  License: Apache License
@@ -1,4 +1,4 @@
1
- langchain_agentx/__init__.py,sha256=Eo8mhs--d9e_b3hUzJW5IlhNE-9Jeqp2Ho5fkUKQZgc,1678
1
+ langchain_agentx/__init__.py,sha256=b-u3BUFUQ27sY5qq7MGKGyTccmPP4VsDNwXrDWhXR24,1678
2
2
  langchain_agentx/command/__init__.py,sha256=Ej260S5uFQcmEtGZQG_NH7BYjHoStrpBLtGUsBTxyZk,631
3
3
  langchain_agentx/command/allowed_tools.py,sha256=TVA0VM8rm98H-QbLK_GRiX5RhEAZFxKawliMi4kk96A,3359
4
4
  langchain_agentx/command/context.py,sha256=DIGOGPBw5UGDBm0WNNJlKx1h_9rCRmcdROv4DT61Qug,731
@@ -66,14 +66,14 @@ langchain_agentx/loop/exit/withheld_error.py,sha256=LfX3j7Rgd9VoMzlz7GOs9TPuRe7R
66
66
  langchain_agentx/loop/graph/__init__.py,sha256=9pUUqn7G87n3lV45iYIU24j0wcTHyCD--SSdtv3urh4,136
67
67
  langchain_agentx/loop/graph/builtin_loop_control.py,sha256=jagBvSo8fj6Yjl95rkp5A0W8RgyVf4P-EXMmGweURl8,7184
68
68
  langchain_agentx/loop/graph/factory.py,sha256=Pjc36mds2PYEo8rNSUawfUN8jiO0U6BO4AIDpCvNP3Q,91663
69
- langchain_agentx/loop/graph/graph_edges.py,sha256=YisRMKFh3-vEJmrC6JF2RGlJgltf6TtdvApv5SoywSs,43356
69
+ langchain_agentx/loop/graph/graph_edges.py,sha256=A82WBrKfNCbx55xNz0If-oaPs1tr2R0inTnQFJSUu4E,43401
70
70
  langchain_agentx/loop/graph/reactive_compact_node.py,sha256=AN31reohf25zRiqdPTmFCIQ1N6VwKn1oIkRkRuGAp4k,8725
71
71
  langchain_agentx/loop/graph/runtime_tools_node.py,sha256=5iBPvHl2hOdfnRZrwtnoD4J_SzEuQKC6VQUJyPuthgA,5089
72
- langchain_agentx/loop/hook/__init__.py,sha256=itzo-r53bwbMgqXFwt7A9spBZMhTZrwzZJOet2N-C0c,2004
73
- langchain_agentx/loop/hook/async_hook_runner.py,sha256=UIrdpgAcUumyxs7ASeJh6ehK2NSo-FDQVj9ICpuPook,2146
72
+ langchain_agentx/loop/hook/__init__.py,sha256=rOjc1cqWBxrcp651w_SQX50dWY7o28L2c-r8iVs-SWU,2167
73
+ langchain_agentx/loop/hook/async_hook_runner.py,sha256=SNyuYnQoowZzBTBHQb6o5L9wTG2Rl7qryeMtqFyEsxI,3195
74
74
  langchain_agentx/loop/hook/config.py,sha256=f8XtGGHtyjuRvZNhkU8ohbhyqOHD7mHZn_w0rVXMX_o,9157
75
75
  langchain_agentx/loop/hook/engine.py,sha256=s18KUN1QY0eC5T01mG8yO2lNxLg-GDa8ZhIXFjo1BSk,14865
76
- langchain_agentx/loop/hook/graph_wiring.py,sha256=5boUnrr4v1GkrU6pZTkY1M7BlqbEN-hr8WnxQ2nFB_g,8012
76
+ langchain_agentx/loop/hook/graph_wiring.py,sha256=Bh40IsQzoYT_2ThdwFQB2uFiy8S6DZQ6NN8LBslcJJs,8057
77
77
  langchain_agentx/loop/hook/hook_projection.py,sha256=B2PgU2YPx6Mnpdpcac6g_8SlkmR1bllNNPIn8jp2hyU,5966
78
78
  langchain_agentx/loop/hook/progress_payload.py,sha256=bbNUEguw_cYbI3l0RJZ4VAtfgaRhD0jqwk3eE5AzgCE,4532
79
79
  langchain_agentx/loop/hook/registry.py,sha256=X8k9NEzyBnasQyAWdJZmbLtX_OlV_KqUkFY4roYPLKo,11958
@@ -313,7 +313,7 @@ langchain_agentx/task_runtime/tasklist/notifier.py,sha256=ylFrvD220TJTpvxGzkSt3r
313
313
  langchain_agentx/task_runtime/tasklist/path_resolver.py,sha256=mCo4I-dZiHpNVHHm6rT5ByFs9bfsfmtspejFKMo_Xq8,2604
314
314
  langchain_agentx/task_runtime/tasklist/paths.py,sha256=pvrKiUK4kUa_mCWQz6_e4T2U6jXTOevBQRJmSfCquto,2284
315
315
  langchain_agentx/task_runtime/tasklist/reader.py,sha256=PIc_GJ2uhp2dskX5Sb_jJxz_miZOkiHy-7nQrXd80E8,2295
316
- langchain_agentx/task_runtime/tasklist/store.py,sha256=gCv9nf7u2cbdjTd_U1NknLaL3mUp0IU_n9RFHxS95_8,19816
316
+ langchain_agentx/task_runtime/tasklist/store.py,sha256=RShUySLEJlGbv1KY2zXTRVm57YCbtB6pXd09T8YDcyQ,20065
317
317
  langchain_agentx/task_runtime/tasks/__init__.py,sha256=G_a_TVE5c3thIiETiOtKov5yMDAkob0dqzfwDsVsfPk,2041
318
318
  langchain_agentx/task_runtime/tasks/ai_analysis/__init__.py,sha256=f_1U_gULXWWdrE9zCXNGfC2M-627X8p0K1Yj9CB_SHc,455
319
319
  langchain_agentx/task_runtime/tasks/ai_analysis/base.py,sha256=bSIBCBlQyg_2sygKrZenUOmz4sLM7lpKrxEroMTf-9I,2702
@@ -667,8 +667,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
667
667
  langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
668
668
  langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
669
669
  langchain_agentx/workspace/view.py,sha256=Ip02CZNI-ZjF5k0OYT3q7RUR_auMEw83VJ6rQsaBcYU,4588
670
- langchain_agentx_python-1.2.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
671
- langchain_agentx_python-1.2.7.dist-info/METADATA,sha256=i36FJyJItNCQwqXc8FDOn3RppwJvvb90dM0ICEN3ihI,24846
672
- langchain_agentx_python-1.2.7.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
673
- langchain_agentx_python-1.2.7.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
674
- langchain_agentx_python-1.2.7.dist-info/RECORD,,
670
+ langchain_agentx_python-1.2.8.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
671
+ langchain_agentx_python-1.2.8.dist-info/METADATA,sha256=GEO2zoN_6c-aydS3qR6yS6zRRIJB79FR_IX6YFePh3g,24846
672
+ langchain_agentx_python-1.2.8.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
673
+ langchain_agentx_python-1.2.8.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
674
+ langchain_agentx_python-1.2.8.dist-info/RECORD,,