langchain-agentx-python 2.2.2__py3-none-any.whl → 2.2.4__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.
- langchain_agentx/__init__.py +1 -1
- langchain_agentx/tools/grep/backend.py +38 -16
- langchain_agentx/tools/grep/rg_subprocess_controller.py +39 -13
- langchain_agentx/tools/grep/ripgrep_stdout_cap.py +93 -0
- {langchain_agentx_python-2.2.2.dist-info → langchain_agentx_python-2.2.4.dist-info}/METADATA +1 -1
- {langchain_agentx_python-2.2.2.dist-info → langchain_agentx_python-2.2.4.dist-info}/RECORD +9 -8
- {langchain_agentx_python-2.2.2.dist-info → langchain_agentx_python-2.2.4.dist-info}/LICENSE +0 -0
- {langchain_agentx_python-2.2.2.dist-info → langchain_agentx_python-2.2.4.dist-info}/WHEEL +0 -0
- {langchain_agentx_python-2.2.2.dist-info → langchain_agentx_python-2.2.4.dist-info}/top_level.txt +0 -0
langchain_agentx/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@ tools/grep/backend.py — RipgrepBackend(ripgrep 子进程封装)
|
|
|
6
6
|
|
|
7
7
|
在整体链路中的位置:
|
|
8
8
|
GrepRuntimeTool.invoke() -> RipgrepBackend.search() -> run_ripgrep_lines(可选 ctx.cancel_event)
|
|
9
|
-
-> RgSubprocessController
|
|
9
|
+
-> RgSubprocessController(有界 stdout)或 TextSubprocessRunner(测试注入)
|
|
10
10
|
|
|
11
11
|
对应 CC:src/utils/ripgrep.ts + GrepTool.ts call() 中参数拼装。
|
|
12
12
|
"""
|
|
@@ -16,7 +16,6 @@ from __future__ import annotations
|
|
|
16
16
|
import os
|
|
17
17
|
import subprocess
|
|
18
18
|
import threading
|
|
19
|
-
from typing import Any
|
|
20
19
|
|
|
21
20
|
from langchain_agentx.tool_runtime.errors import ToolRuntimeError
|
|
22
21
|
from langchain_agentx.utils.rg_executable import default_rg_executable
|
|
@@ -26,6 +25,20 @@ from langchain_agentx.utils.subprocess_text import (
|
|
|
26
25
|
)
|
|
27
26
|
|
|
28
27
|
from .models import GrepOutputMode
|
|
28
|
+
from .ripgrep_stdout_cap import RipgrepStdoutCap
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def resolve_ripgrep_subprocess_cwd(search_path: str) -> str:
|
|
32
|
+
"""subprocess cwd 必须是目录;schema 允许 path 为文件时退到父目录。
|
|
33
|
+
|
|
34
|
+
Windows 上相对 glob 需要 cwd≈搜索根目录;但不能把文件路径直接当 cwd
|
|
35
|
+
(会 NotADirectoryError / Errno 20)。rg 命令行目标仍用原 search_path。
|
|
36
|
+
"""
|
|
37
|
+
real = os.path.realpath(search_path)
|
|
38
|
+
if os.path.isfile(real):
|
|
39
|
+
parent = os.path.dirname(real)
|
|
40
|
+
return parent if parent else os.sep
|
|
41
|
+
return real
|
|
29
42
|
|
|
30
43
|
|
|
31
44
|
class GrepTimeoutError(ToolRuntimeError):
|
|
@@ -45,6 +58,11 @@ class GrepCancelledError(ToolRuntimeError):
|
|
|
45
58
|
super().__init__(message, code="GREP_CANCELLED")
|
|
46
59
|
|
|
47
60
|
|
|
61
|
+
def _uses_injected_subprocess_run(text_runner: TextSubprocessRunner) -> bool:
|
|
62
|
+
"""测试注入自定义 ``subprocess_run`` 时走 text_runner;生产默认走有界 controller。"""
|
|
63
|
+
return getattr(text_runner, "_subprocess_run", None) is not subprocess.run
|
|
64
|
+
|
|
65
|
+
|
|
48
66
|
def run_ripgrep_lines(
|
|
49
67
|
rg_path: str,
|
|
50
68
|
args: list[str],
|
|
@@ -57,19 +75,25 @@ def run_ripgrep_lines(
|
|
|
57
75
|
) -> list[str]:
|
|
58
76
|
"""
|
|
59
77
|
执行 [rg_path, *args, search_path],解析 stdout 为行列表。
|
|
60
|
-
对齐 CC ripgrep.ts:returncode 1 -> [];EAGAIN 时以 -j 1
|
|
78
|
+
对齐 CC ripgrep.ts:returncode 1 -> [];EAGAIN 时以 -j 1 重试一次;
|
|
79
|
+
stdout 硬顶 ``MAX_STDOUT_BUFFER_SIZE``(20MB)。
|
|
61
80
|
供 RipgrepBackend 与 Glob 的 rg --files 列文件共用。
|
|
62
81
|
|
|
63
|
-
SDK 补充:``subprocess cwd
|
|
64
|
-
|
|
82
|
+
SDK 补充:``subprocess cwd`` 对齐搜索根目录(目录本身,或文件的父目录)。
|
|
83
|
+
CC 隐式依赖进程 cwd≈searchDir;编排布局 ``state_root≠repo`` 时须显式设 cwd,
|
|
84
|
+
否则 Windows 上目录前缀 glob(如 ``alpha/*``)误空。
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
|
|
86
|
+
生产路径使用 ``RgSubprocessController`` 边收边截;仅当测试注入自定义
|
|
87
|
+
``text_runner.subprocess_run`` 时走 ``text_runner.run``(事后硬顶)。
|
|
68
88
|
"""
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
89
|
+
search_target = os.path.realpath(search_path)
|
|
90
|
+
search_cwd = resolve_ripgrep_subprocess_cwd(search_path)
|
|
91
|
+
cmd = [rg_path, *args, search_target]
|
|
92
|
+
stdout_truncated = False
|
|
93
|
+
|
|
94
|
+
use_text_runner = cancel_event is None and _uses_injected_subprocess_run(text_runner)
|
|
95
|
+
if not use_text_runner:
|
|
96
|
+
if cancel_event is not None and cancel_event.is_set():
|
|
73
97
|
raise GrepCancelledError("ripgrep cancelled before start")
|
|
74
98
|
from langchain_agentx.tools.grep.rg_subprocess_controller import RgSubprocessController
|
|
75
99
|
|
|
@@ -85,6 +109,7 @@ def run_ripgrep_lines(
|
|
|
85
109
|
raise
|
|
86
110
|
except GrepCancelledError:
|
|
87
111
|
raise
|
|
112
|
+
stdout_truncated = bool(getattr(result, "stdout_truncated", False))
|
|
88
113
|
else:
|
|
89
114
|
try:
|
|
90
115
|
result = text_runner.run(
|
|
@@ -119,10 +144,7 @@ def run_ripgrep_lines(
|
|
|
119
144
|
raise GrepExecutionError(f"ripgrep failed: {stderr or f'exit code {result.returncode}'}")
|
|
120
145
|
|
|
121
146
|
out = result.stdout or ""
|
|
122
|
-
|
|
123
|
-
return []
|
|
124
|
-
lines = [ln.rstrip("\r") for ln in out.rstrip("\n").split("\n")]
|
|
125
|
-
return [ln for ln in lines if ln]
|
|
147
|
+
return RipgrepStdoutCap.lines_from_text(out, truncated=stdout_truncated)
|
|
126
148
|
|
|
127
149
|
|
|
128
150
|
class RipgrepBackend:
|
|
@@ -321,7 +343,7 @@ class RipgrepBackend:
|
|
|
321
343
|
plugin_glob_exclusions=plugin_glob_exclusions,
|
|
322
344
|
)
|
|
323
345
|
cmd = [self._rg_path, *args, os.path.realpath(search_path)]
|
|
324
|
-
search_cwd =
|
|
346
|
+
search_cwd = resolve_ripgrep_subprocess_cwd(search_path)
|
|
325
347
|
if cancel_event is not None and cancel_event.is_set():
|
|
326
348
|
raise GrepCancelledError("ripgrep cancelled before start")
|
|
327
349
|
try:
|
|
@@ -3,10 +3,11 @@ tools/grep/rg_subprocess_controller.py — ripgrep 子进程 timeout + 协作取
|
|
|
3
3
|
|
|
4
4
|
职责:
|
|
5
5
|
在无法使用单次 ``subprocess.run(..., timeout=)`` 响应协作取消时,用 Popen + 后台
|
|
6
|
-
|
|
6
|
+
有界 stdout/stderr 读取与主线程轮询 ``cancel_event``,对齐 CC ``ripGrep(..., abortSignal)``
|
|
7
|
+
与 ``MAX_BUFFER_SIZE`` 边收边截。
|
|
7
8
|
|
|
8
9
|
链路位置:
|
|
9
|
-
``grep.backend.run_ripgrep_lines``
|
|
10
|
+
``grep.backend.run_ripgrep_lines`` 在生产路径(或传入 ``cancel_event``)时委托本类;
|
|
10
11
|
``RipgrepBackend.search_stream_limited`` 在读取 stdout 时轮询同一事件。
|
|
11
12
|
|
|
12
13
|
当前裁剪范围:
|
|
@@ -24,13 +25,17 @@ from langchain_agentx.tools.grep.backend import (
|
|
|
24
25
|
GrepExecutionError,
|
|
25
26
|
GrepTimeoutError,
|
|
26
27
|
)
|
|
28
|
+
from langchain_agentx.tools.grep.ripgrep_stdout_cap import RipgrepStdoutCap
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class RgSubprocessController:
|
|
30
|
-
"""封装可中断的阻塞式子进程等待(timeout + ``threading.Event
|
|
32
|
+
"""封装可中断的阻塞式子进程等待(timeout + ``threading.Event`` + stdout 硬顶)。"""
|
|
31
33
|
|
|
32
34
|
_poll_interval_s = 0.05
|
|
33
35
|
|
|
36
|
+
def __init__(self, *, stdout_cap_factory: type[RipgrepStdoutCap] = RipgrepStdoutCap) -> None:
|
|
37
|
+
self._stdout_cap_factory = stdout_cap_factory
|
|
38
|
+
|
|
34
39
|
def run_argv_wait_completed(
|
|
35
40
|
self,
|
|
36
41
|
argv: list[str],
|
|
@@ -42,8 +47,11 @@ class RgSubprocessController:
|
|
|
42
47
|
"""
|
|
43
48
|
启动 ``argv``,在 ``timeout`` 秒内等待结束;若 ``cancel_event`` 被 set 则 terminate/kill。
|
|
44
49
|
|
|
50
|
+
stdout/stderr 边收边截(``RipgrepStdoutCap``),避免无界 ``communicate`` 打爆内存。
|
|
51
|
+
|
|
45
52
|
Returns:
|
|
46
53
|
``CompletedProcess``(text stdout/stderr),供上层解析 returncode。
|
|
54
|
+
额外属性 ``stdout_truncated`` / ``stderr_truncated``(bool)。
|
|
47
55
|
"""
|
|
48
56
|
if cancel_event is not None and cancel_event.is_set():
|
|
49
57
|
raise GrepCancelledError("ripgrep cancelled before start")
|
|
@@ -64,19 +72,33 @@ class RgSubprocessController:
|
|
|
64
72
|
"'rg.exe' is on PATH; on Unix ensure 'rg' is on PATH."
|
|
65
73
|
) from e
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
stdout_cap = self._stdout_cap_factory()
|
|
76
|
+
stderr_cap = self._stdout_cap_factory()
|
|
69
77
|
thread_exc: list[BaseException | None] = [None]
|
|
70
78
|
|
|
71
|
-
def
|
|
79
|
+
def _readers_worker() -> None:
|
|
72
80
|
try:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
assert proc.stdout is not None
|
|
82
|
+
assert proc.stderr is not None
|
|
83
|
+
out_t = threading.Thread(
|
|
84
|
+
target=stdout_cap.drain_text_stream,
|
|
85
|
+
args=(proc.stdout,),
|
|
86
|
+
daemon=True,
|
|
87
|
+
)
|
|
88
|
+
err_t = threading.Thread(
|
|
89
|
+
target=stderr_cap.drain_text_stream,
|
|
90
|
+
args=(proc.stderr,),
|
|
91
|
+
daemon=True,
|
|
92
|
+
)
|
|
93
|
+
out_t.start()
|
|
94
|
+
err_t.start()
|
|
95
|
+
out_t.join()
|
|
96
|
+
err_t.join()
|
|
97
|
+
proc.wait()
|
|
76
98
|
except BaseException as ex: # noqa: BLE001 — 必须兜住线程内异常
|
|
77
99
|
thread_exc[0] = ex
|
|
78
100
|
|
|
79
|
-
worker = threading.Thread(target=
|
|
101
|
+
worker = threading.Thread(target=_readers_worker, daemon=True)
|
|
80
102
|
worker.start()
|
|
81
103
|
deadline = time.monotonic() + timeout
|
|
82
104
|
|
|
@@ -98,12 +120,16 @@ class RgSubprocessController:
|
|
|
98
120
|
if thread_exc[0] is not None:
|
|
99
121
|
raise thread_exc[0]
|
|
100
122
|
|
|
101
|
-
|
|
123
|
+
result = subprocess.CompletedProcess(
|
|
102
124
|
args=argv,
|
|
103
125
|
returncode=proc.returncode if proc.returncode is not None else -1,
|
|
104
|
-
stdout=
|
|
105
|
-
stderr=
|
|
126
|
+
stdout=stdout_cap.text(),
|
|
127
|
+
stderr=stderr_cap.text(),
|
|
106
128
|
)
|
|
129
|
+
# 供 run_ripgrep_lines 决定是否丢弃残缺末行
|
|
130
|
+
setattr(result, "stdout_truncated", stdout_cap.truncated)
|
|
131
|
+
setattr(result, "stderr_truncated", stderr_cap.truncated)
|
|
132
|
+
return result
|
|
107
133
|
finally:
|
|
108
134
|
if proc.poll() is None:
|
|
109
135
|
try:
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/grep/ripgrep_stdout_cap.py — ripgrep stdout/stderr 字节硬顶
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
对齐 CC ``src/utils/ripgrep.ts`` 的 ``MAX_BUFFER_SIZE = 20_000_000``:
|
|
6
|
+
边收边截,超限停止 append;解析行时若已截断则丢弃可能残缺的末行。
|
|
7
|
+
|
|
8
|
+
链路位置:
|
|
9
|
+
``run_ripgrep_lines`` / ``RgSubprocessController`` 共用;Grep 与 Glob 列文件同路径受益。
|
|
10
|
+
|
|
11
|
+
当前裁剪:
|
|
12
|
+
按 Unicode 字符长度计量(对齐 CC JS string ``.length``);不在此杀进程。
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# 对齐 CC ripgrep.ts:large monorepos can have 200k+ files
|
|
22
|
+
MAX_STDOUT_BUFFER_SIZE = 20_000_000
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RipgrepStdoutCap:
|
|
26
|
+
"""可复用的有界 stdout/stderr 收集器(feed 后 to_lines / text)。"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, max_size: int = MAX_STDOUT_BUFFER_SIZE) -> None:
|
|
29
|
+
if max_size <= 0:
|
|
30
|
+
raise ValueError("max_size must be positive")
|
|
31
|
+
self._max_size = max_size
|
|
32
|
+
self._parts: list[str] = []
|
|
33
|
+
self._size = 0
|
|
34
|
+
self.truncated = False
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def max_size(self) -> int:
|
|
38
|
+
return self._max_size
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def size(self) -> int:
|
|
42
|
+
return self._size
|
|
43
|
+
|
|
44
|
+
def feed(self, chunk: str) -> None:
|
|
45
|
+
"""追加 chunk;已截断时忽略内容(调用方仍应继续读以排空管道)。"""
|
|
46
|
+
if self.truncated or not chunk:
|
|
47
|
+
return
|
|
48
|
+
remaining = self._max_size - self._size
|
|
49
|
+
if len(chunk) <= remaining:
|
|
50
|
+
self._parts.append(chunk)
|
|
51
|
+
self._size += len(chunk)
|
|
52
|
+
return
|
|
53
|
+
if remaining > 0:
|
|
54
|
+
self._parts.append(chunk[:remaining])
|
|
55
|
+
self._size = self._max_size
|
|
56
|
+
self.truncated = True
|
|
57
|
+
|
|
58
|
+
def text(self) -> str:
|
|
59
|
+
return "".join(self._parts)
|
|
60
|
+
|
|
61
|
+
def to_lines(self) -> list[str]:
|
|
62
|
+
"""拆成非空行;截断时丢弃末行(对齐 CC buffer overflow / timeout 处理)。"""
|
|
63
|
+
return self.lines_from_text(self.text(), truncated=self.truncated)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def lines_from_text(out: str, *, truncated: bool = False) -> list[str]:
|
|
67
|
+
"""对已物化字符串做硬顶 + 分行(text_runner / mock 路径)。"""
|
|
68
|
+
if not out:
|
|
69
|
+
return []
|
|
70
|
+
was_truncated = truncated
|
|
71
|
+
if len(out) > MAX_STDOUT_BUFFER_SIZE:
|
|
72
|
+
out = out[:MAX_STDOUT_BUFFER_SIZE]
|
|
73
|
+
was_truncated = True
|
|
74
|
+
if not out.strip():
|
|
75
|
+
return []
|
|
76
|
+
lines = [ln.rstrip("\r") for ln in out.rstrip("\n").split("\n")]
|
|
77
|
+
lines = [ln for ln in lines if ln]
|
|
78
|
+
if was_truncated and lines:
|
|
79
|
+
lines = lines[:-1]
|
|
80
|
+
if was_truncated:
|
|
81
|
+
logger.warning(
|
|
82
|
+
"ripgrep stdout truncated at %s chars (CC MAX_BUFFER_SIZE alignment)",
|
|
83
|
+
MAX_STDOUT_BUFFER_SIZE,
|
|
84
|
+
)
|
|
85
|
+
return lines
|
|
86
|
+
|
|
87
|
+
def drain_text_stream(self, stream) -> None:
|
|
88
|
+
"""从文本流按块读取并 feed,超限后继续排空但不入库。"""
|
|
89
|
+
while True:
|
|
90
|
+
chunk = stream.read(64 * 1024)
|
|
91
|
+
if not chunk:
|
|
92
|
+
break
|
|
93
|
+
self.feed(chunk)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
langchain_agentx/__init__.py,sha256=
|
|
1
|
+
langchain_agentx/__init__.py,sha256=rYu7Sa7AIXQVmlKCoRc4uqxp7Lb37UIESLvYcrsemdA,1614
|
|
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
|
|
@@ -581,10 +581,11 @@ langchain_agentx/tools/glob/rg_list_backend.py,sha256=gTyFQE0eYHl-7LkSdah76S8bQn
|
|
|
581
581
|
langchain_agentx/tools/glob/rg_pattern.py,sha256=YOV-1gdxC52vzdAwgz27l7mzPJvVuHqpqeWhiEnWQJs,1377
|
|
582
582
|
langchain_agentx/tools/glob/tool.py,sha256=m746Vl_aI9eFmrXYECkg16zMln9YPVxqjtijPlc5ALA,14868
|
|
583
583
|
langchain_agentx/tools/grep/__init__.py,sha256=AA9qa42Ap9tw6lPQkNgKzYmr4hu44eP93qVdyP-eFNU,162
|
|
584
|
-
langchain_agentx/tools/grep/backend.py,sha256=
|
|
584
|
+
langchain_agentx/tools/grep/backend.py,sha256=12BHyDZccr7Hzvfumj3zGpXrSRNV_LiCbYYyG2Y3BCg,15062
|
|
585
585
|
langchain_agentx/tools/grep/models.py,sha256=6fTSux0COK06weQ7PWYqCGBQx60OaNtqC5q7xKtf5i0,3860
|
|
586
586
|
langchain_agentx/tools/grep/prompt.py,sha256=Euo1NErphk5WHxaKXwJH-1v7joblfwEFWHIYNthKkzo,1336
|
|
587
|
-
langchain_agentx/tools/grep/rg_subprocess_controller.py,sha256=
|
|
587
|
+
langchain_agentx/tools/grep/rg_subprocess_controller.py,sha256=A4RGCiZaIo8lrohqftzrOcDmU8VVDlSic1Pqy5ZF5Js,5315
|
|
588
|
+
langchain_agentx/tools/grep/ripgrep_stdout_cap.py,sha256=b7krKaCXYMjkecRwhpB1a8gaJ96BW0oyvxhrlDMQTtA,3149
|
|
588
589
|
langchain_agentx/tools/grep/tool.py,sha256=S_nGFqFgGHx9UhnXJdNvkdoHCJxLRZBDoPDd-Pxms0g,20513
|
|
589
590
|
langchain_agentx/tools/plan_mode/__init__.py,sha256=wO5FX24xEB0Dx9h6bysBt1ql3wn0rjPYsg70mbl4S_0,402
|
|
590
591
|
langchain_agentx/tools/plan_mode/constants.py,sha256=vBjcx9JTSOhU9jfCJ4RKnCxVQPn0HzHapouKxRf2Zns,293
|
|
@@ -760,8 +761,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
|
|
|
760
761
|
langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
|
|
761
762
|
langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
|
|
762
763
|
langchain_agentx/workspace/view.py,sha256=PGasqTaqhlD03SXXazHuw4RHfV681AIdlsYqL70pEjc,4774
|
|
763
|
-
langchain_agentx_python-2.2.
|
|
764
|
-
langchain_agentx_python-2.2.
|
|
765
|
-
langchain_agentx_python-2.2.
|
|
766
|
-
langchain_agentx_python-2.2.
|
|
767
|
-
langchain_agentx_python-2.2.
|
|
764
|
+
langchain_agentx_python-2.2.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
765
|
+
langchain_agentx_python-2.2.4.dist-info/METADATA,sha256=X5bLG_gFcxVwOPDJ6PWzNl-HYGukdx7N2B-TNv6ECsc,24250
|
|
766
|
+
langchain_agentx_python-2.2.4.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
|
|
767
|
+
langchain_agentx_python-2.2.4.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
|
|
768
|
+
langchain_agentx_python-2.2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{langchain_agentx_python-2.2.2.dist-info → langchain_agentx_python-2.2.4.dist-info}/top_level.txt
RENAMED
|
File without changes
|