langchain-agentx-python 2.3.2__py3-none-any.whl → 2.3.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/loop/config/__init__.py +2 -0
- langchain_agentx/loop/config/compaction_config.py +38 -0
- langchain_agentx/loop/config/loop_config.py +13 -0
- langchain_agentx/loop/context/pipeline.py +25 -7
- langchain_agentx/loop/context/settings.py +11 -0
- langchain_agentx/loop/context/stages/autocompact.py +51 -9
- langchain_agentx/loop/context/types.py +4 -0
- langchain_agentx/loop/exit/exit_logic.py +2 -0
- langchain_agentx/loop/graph/factory.py +172 -48
- langchain_agentx/observability/events/agent_event_projector.py +11 -0
- langchain_agentx/tool_runtime/loader.py +2 -0
- langchain_agentx/tool_runtime/loop_loader.py +13 -3
- langchain_agentx/tools/skill/catalog.py +1 -1
- langchain_agentx/tools/skill/multi_source_repository.py +2 -0
- langchain_agentx/tools/skill/repository_factory.py +5 -3
- langchain_agentx/tools/skill/source_paths.py +62 -12
- langchain_agentx/workspace/project_memory_layout.py +4 -0
- {langchain_agentx_python-2.3.2.dist-info → langchain_agentx_python-2.3.4.dist-info}/METADATA +1 -1
- {langchain_agentx_python-2.3.2.dist-info → langchain_agentx_python-2.3.4.dist-info}/RECORD +23 -22
- {langchain_agentx_python-2.3.2.dist-info → langchain_agentx_python-2.3.4.dist-info}/LICENSE +0 -0
- {langchain_agentx_python-2.3.2.dist-info → langchain_agentx_python-2.3.4.dist-info}/WHEEL +0 -0
- {langchain_agentx_python-2.3.2.dist-info → langchain_agentx_python-2.3.4.dist-info}/top_level.txt +0 -0
langchain_agentx/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ from .loop_config import (
|
|
|
4
4
|
AgentStateConfig,
|
|
5
5
|
LoopServices,
|
|
6
6
|
PromptConfig,
|
|
7
|
+
SkillDiscoveryConfig,
|
|
7
8
|
TraceConfig,
|
|
8
9
|
SubagentLinkage,
|
|
9
10
|
)
|
|
@@ -25,6 +26,7 @@ from .runtime_settings import (
|
|
|
25
26
|
__all__ = [
|
|
26
27
|
"AgentLoopConfig",
|
|
27
28
|
"AgentStateConfig",
|
|
29
|
+
"SkillDiscoveryConfig",
|
|
28
30
|
"LoopServices",
|
|
29
31
|
"PromptConfig",
|
|
30
32
|
"TraceConfig",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Compaction 控制面 env 解析(对齐 CC DISABLE_COMPACT / DISABLE_AUTO_COMPACT)。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _parse_bool_env(name: str) -> bool | None:
|
|
9
|
+
raw = os.environ.get(name, "").strip().lower()
|
|
10
|
+
if not raw:
|
|
11
|
+
return None
|
|
12
|
+
return raw in ("1", "true", "yes", "on")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def resolve_is_auto_compact_enabled(*, config_is_auto_compact: bool = True) -> bool:
|
|
16
|
+
"""CC:DISABLE_COMPACT 关全部;DISABLE_AUTO_COMPACT 仅关 proactive autocompact。"""
|
|
17
|
+
if _parse_bool_env("DISABLE_COMPACT") is True:
|
|
18
|
+
return False
|
|
19
|
+
if _parse_bool_env("DISABLE_AUTO_COMPACT") is True:
|
|
20
|
+
return False
|
|
21
|
+
return config_is_auto_compact
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def compute_autocompact_trigger_token_threshold(
|
|
25
|
+
*,
|
|
26
|
+
context_window_tokens: int,
|
|
27
|
+
max_output_tokens_reserved: int,
|
|
28
|
+
compact_buffer_tokens: int,
|
|
29
|
+
) -> int:
|
|
30
|
+
"""对齐 CC:effectiveWindow - AUTOCOMPACT_BUFFER_TOKENS。"""
|
|
31
|
+
effective = max(0, int(context_window_tokens) - int(max_output_tokens_reserved))
|
|
32
|
+
return max(0, effective - int(compact_buffer_tokens))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"compute_autocompact_trigger_token_threshold",
|
|
37
|
+
"resolve_is_auto_compact_enabled",
|
|
38
|
+
]
|
|
@@ -52,6 +52,16 @@ class PromptConfig:
|
|
|
52
52
|
thinking_generation_enabled: bool = True # 是否启用 request-side thinking 生成(对齐 CC thinkingConfig)
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class SkillDiscoveryConfig:
|
|
57
|
+
"""Workflow / 双 workspace 节点的 Skill 发现层级(对齐 bug.md ComponentScan)。"""
|
|
58
|
+
|
|
59
|
+
project_skills_dir: str | Path | None = None
|
|
60
|
+
global_skills_dir: str | Path | None = None
|
|
61
|
+
include_managed: bool = False
|
|
62
|
+
include_user_home: bool = False
|
|
63
|
+
|
|
64
|
+
|
|
55
65
|
@dataclass(frozen=True)
|
|
56
66
|
class AgentStateConfig:
|
|
57
67
|
"""Agent 状态配置(Agent 状态目录落点 / 派生目录视图)。
|
|
@@ -64,6 +74,7 @@ class AgentStateConfig:
|
|
|
64
74
|
runtime_cwd: 工具运行时 cwd / 相对路径展开基准(None 时回退到 workspace_root)
|
|
65
75
|
read_roots: 只读根目录
|
|
66
76
|
write_roots: 只写根目录
|
|
77
|
+
skill_discovery: 可选项目级 / 全局 skill 目录(覆盖 legacy 三源)
|
|
67
78
|
|
|
68
79
|
CC 对照:无直接对应,SDK 特化的状态层配置。
|
|
69
80
|
"""
|
|
@@ -74,6 +85,7 @@ class AgentStateConfig:
|
|
|
74
85
|
write_roots: tuple[str, ...] = ()
|
|
75
86
|
workspace_trust_accepted: bool | None = None
|
|
76
87
|
enable_dynamic_root_grant: bool = False
|
|
88
|
+
skill_discovery: SkillDiscoveryConfig | None = None
|
|
77
89
|
|
|
78
90
|
|
|
79
91
|
@dataclass(frozen=True)
|
|
@@ -241,6 +253,7 @@ class AgentLoopConfig:
|
|
|
241
253
|
token_budget_max_continuations: int = 3
|
|
242
254
|
compact_buffer_tokens: int = 13_000
|
|
243
255
|
max_output_tokens_reserved: int = 20_000
|
|
256
|
+
is_auto_compact: bool = True
|
|
244
257
|
|
|
245
258
|
# -----------------------------------------------------------------------
|
|
246
259
|
# 运行模式
|
|
@@ -33,17 +33,33 @@ from .stages.base import CompactionStage
|
|
|
33
33
|
from .types import CompactProgress, ContextCompactionContext
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _apply_autocompact_failure_patch(
|
|
37
|
+
state: dict[str, Any],
|
|
38
|
+
stages_meta: list[dict[str, Any]],
|
|
39
|
+
patch: dict[str, Any],
|
|
40
|
+
) -> None:
|
|
41
|
+
ac = next((m for m in stages_meta if m.get("name") == "autocompact"), None)
|
|
42
|
+
if ac is None:
|
|
43
|
+
return
|
|
44
|
+
extra = ac.get("extra") or {}
|
|
45
|
+
prev = int(state.get("consecutive_autocompact_failures") or 0)
|
|
46
|
+
if extra.get("autocompact_llm_failed"):
|
|
47
|
+
patch["consecutive_autocompact_failures"] = prev + 1
|
|
48
|
+
elif extra.get("autocompact_mode") == "llm":
|
|
49
|
+
patch["consecutive_autocompact_failures"] = 0
|
|
50
|
+
|
|
51
|
+
|
|
36
52
|
class DefaultCompactionStages:
|
|
37
53
|
"""CC 五段默认装配(顺序即契约)。"""
|
|
38
54
|
|
|
39
55
|
@staticmethod
|
|
40
|
-
def build() -> tuple[CompactionStage, ...]:
|
|
56
|
+
def build(settings: CompactionSettings | None = None) -> tuple[CompactionStage, ...]:
|
|
41
57
|
return (
|
|
42
|
-
ToolResultBudgetStage(),
|
|
43
|
-
SnipCompactStage(),
|
|
44
|
-
MicrocompactStage(),
|
|
45
|
-
ContextCollapseStage(),
|
|
46
|
-
AutocompactStage(),
|
|
58
|
+
ToolResultBudgetStage(settings),
|
|
59
|
+
SnipCompactStage(settings),
|
|
60
|
+
MicrocompactStage(settings),
|
|
61
|
+
ContextCollapseStage(settings),
|
|
62
|
+
AutocompactStage(settings),
|
|
47
63
|
)
|
|
48
64
|
|
|
49
65
|
|
|
@@ -65,7 +81,7 @@ class ContextPipeline:
|
|
|
65
81
|
settings: CompactionSettings | None = None,
|
|
66
82
|
) -> ContextPipeline:
|
|
67
83
|
"""使用 `DefaultCompactionStages` 构建管道。"""
|
|
68
|
-
return cls(DefaultCompactionStages.build(), settings=settings)
|
|
84
|
+
return cls(DefaultCompactionStages.build(settings), settings=settings)
|
|
69
85
|
|
|
70
86
|
@property
|
|
71
87
|
def stages(self) -> tuple[CompactionStage, ...]:
|
|
@@ -117,6 +133,7 @@ class ContextPipeline:
|
|
|
117
133
|
"mode": ctx.mode,
|
|
118
134
|
}
|
|
119
135
|
|
|
136
|
+
_apply_autocompact_failure_patch(state, stages_meta, patch)
|
|
120
137
|
if not patch:
|
|
121
138
|
return {}
|
|
122
139
|
return patch
|
|
@@ -198,6 +215,7 @@ class ContextPipeline:
|
|
|
198
215
|
"mode": ctx.mode,
|
|
199
216
|
}
|
|
200
217
|
|
|
218
|
+
_apply_autocompact_failure_patch(state, stages_meta, patch)
|
|
201
219
|
if not patch:
|
|
202
220
|
return {}
|
|
203
221
|
return patch
|
|
@@ -32,6 +32,8 @@ class CompactionSettings:
|
|
|
32
32
|
prune_protected_tools: FrozenSet[str] = field(default_factory=lambda: frozenset({"Skill"}))
|
|
33
33
|
default_max_context_tokens: int = 120_000
|
|
34
34
|
default_compress_trigger_fraction: float = 0.8
|
|
35
|
+
# 非 None 时 AutocompactStage 用绝对触发线(由 AgentLoopConfig compact_buffer 派生)。
|
|
36
|
+
autocompact_trigger_token_threshold: int | None = None
|
|
35
37
|
default_compress_keep_recent_messages: int = 8
|
|
36
38
|
# --- autocompact LLM 提示词(对齐 `compact.ts` + `prompt.ts`)---
|
|
37
39
|
# 摘要插在保留 tail 之前 → 默认 `partial_up_to`;全量折叠无尾窗时用 `full`;与 CC partial pivot「from」一致时用 `partial_from`。
|
|
@@ -62,6 +64,8 @@ class CompactionSettings:
|
|
|
62
64
|
default_factory=QuerySourcePolicy.autocompact_skip_sources
|
|
63
65
|
)
|
|
64
66
|
autocompact_use_llm_when_available: bool = True
|
|
67
|
+
is_auto_compact_enabled: bool = True
|
|
68
|
+
max_consecutive_autocompact_failures: int = 3
|
|
65
69
|
autocompact_llm_excerpt_chars_per_message: int = 20_000
|
|
66
70
|
# --- snip carry → autocompact 阈值放宽(对齐 CC snipTokensFreed 计入阈值)---
|
|
67
71
|
# True:将本流水线已累计的 `snip_tokens_freed_carry` 加到触发阈值上,避免 snip 已释放后仍立刻 autocompact。
|
|
@@ -89,6 +93,12 @@ class CompactionSettings:
|
|
|
89
93
|
active_compaction_settings: CompactionSettings = CompactionSettings()
|
|
90
94
|
|
|
91
95
|
|
|
96
|
+
def autocompact_trigger_threshold_tokens(settings: CompactionSettings) -> int:
|
|
97
|
+
if settings.autocompact_trigger_token_threshold is not None:
|
|
98
|
+
return settings.autocompact_trigger_token_threshold
|
|
99
|
+
return int(settings.default_max_context_tokens * settings.default_compress_trigger_fraction)
|
|
100
|
+
|
|
101
|
+
|
|
92
102
|
def get_active_compaction_settings() -> CompactionSettings:
|
|
93
103
|
"""返回当前生效的策略(默认即模块级 `active_compaction_settings`)。"""
|
|
94
104
|
return active_compaction_settings
|
|
@@ -104,6 +114,7 @@ __all__ = [
|
|
|
104
114
|
"AutocompactLlmPromptKind",
|
|
105
115
|
"CompactionSettings",
|
|
106
116
|
"active_compaction_settings",
|
|
117
|
+
"autocompact_trigger_threshold_tokens",
|
|
107
118
|
"get_active_compaction_settings",
|
|
108
119
|
"set_active_compaction_settings",
|
|
109
120
|
]
|
|
@@ -25,7 +25,11 @@ from langchain_core.messages import HumanMessage, SystemMessage
|
|
|
25
25
|
from ..blocking_guard import CompactionBlockingGuard
|
|
26
26
|
from ..message_utils import stringify_content, total_estimated_tokens_for_messages
|
|
27
27
|
from ...prompt.compact import build_autocompact_llm_instruction_prompt, get_compact_user_summary_message
|
|
28
|
-
from ..settings import
|
|
28
|
+
from ..settings import (
|
|
29
|
+
CompactionSettings,
|
|
30
|
+
autocompact_trigger_threshold_tokens,
|
|
31
|
+
get_active_compaction_settings,
|
|
32
|
+
)
|
|
29
33
|
from ..types import CompactProgress, ContextCompactionContext, StageResult
|
|
30
34
|
from .base import CompactionStage
|
|
31
35
|
|
|
@@ -45,10 +49,25 @@ class AutocompactStage(CompactionStage):
|
|
|
45
49
|
|
|
46
50
|
def run(self, messages: list[Any], ctx: ContextCompactionContext) -> StageResult:
|
|
47
51
|
s = self._effective_settings()
|
|
52
|
+
if not s.is_auto_compact_enabled:
|
|
53
|
+
return StageResult(
|
|
54
|
+
messages=messages,
|
|
55
|
+
tokens_freed=0,
|
|
56
|
+
extra={"autocompact_skipped": "auto_compact_disabled"},
|
|
57
|
+
)
|
|
58
|
+
if ctx.consecutive_autocompact_failures >= s.max_consecutive_autocompact_failures:
|
|
59
|
+
return StageResult(
|
|
60
|
+
messages=messages,
|
|
61
|
+
tokens_freed=0,
|
|
62
|
+
extra={
|
|
63
|
+
"autocompact_skipped": "consecutive_failures",
|
|
64
|
+
"consecutive_autocompact_failures": ctx.consecutive_autocompact_failures,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
48
67
|
if ctx.query_source in s.autocompact_skip_query_sources:
|
|
49
68
|
return StageResult(messages=messages, tokens_freed=0, extra={"autocompact_skipped": "query_source"})
|
|
50
69
|
total = total_estimated_tokens_for_messages(messages, s.num_chars_per_token)
|
|
51
|
-
threshold =
|
|
70
|
+
threshold = autocompact_trigger_threshold_tokens(s)
|
|
52
71
|
slack = ctx.snip_tokens_freed_carry if s.autocompact_snip_carry_threshold_slack else 0
|
|
53
72
|
if total <= threshold + slack:
|
|
54
73
|
return StageResult(messages=messages, tokens_freed=0)
|
|
@@ -66,11 +85,12 @@ class AutocompactStage(CompactionStage):
|
|
|
66
85
|
tail = rest[-k:]
|
|
67
86
|
|
|
68
87
|
block_llm = CompactionBlockingGuard(settings=s).should_block_compact_llm(messages)
|
|
69
|
-
|
|
88
|
+
attempted_llm = (
|
|
70
89
|
s.autocompact_use_llm_when_available
|
|
71
90
|
and ctx.model is not None
|
|
72
91
|
and not block_llm
|
|
73
|
-
)
|
|
92
|
+
)
|
|
93
|
+
if attempted_llm:
|
|
74
94
|
summary = _invoke_summary(ctx.model, s, head)
|
|
75
95
|
if summary:
|
|
76
96
|
# 修复后:不包含 leading,state 不含 SystemMessage
|
|
@@ -104,20 +124,38 @@ class AutocompactStage(CompactionStage):
|
|
|
104
124
|
] + tail
|
|
105
125
|
new_total = total_estimated_tokens_for_messages(folded, s.num_chars_per_token)
|
|
106
126
|
mode = "prune_blocked_llm" if block_llm else "prune"
|
|
127
|
+
extra: dict[str, Any] = {"autocompact_mode": mode, "folded_messages": len(head)}
|
|
128
|
+
if attempted_llm:
|
|
129
|
+
extra["autocompact_llm_failed"] = True
|
|
107
130
|
return StageResult(
|
|
108
131
|
messages=folded,
|
|
109
132
|
tokens_freed=max(0, total - new_total),
|
|
110
|
-
extra=
|
|
133
|
+
extra=extra,
|
|
111
134
|
)
|
|
112
135
|
|
|
113
136
|
async def arun(self, messages: list[Any], ctx: ContextCompactionContext) -> StageResult:
|
|
114
137
|
"""Async 版本:LLM 摘要使用 astream 流式生成,支持进度回调。"""
|
|
115
138
|
s = self._effective_settings()
|
|
139
|
+
if not s.is_auto_compact_enabled:
|
|
140
|
+
return StageResult(
|
|
141
|
+
messages=messages,
|
|
142
|
+
tokens_freed=0,
|
|
143
|
+
extra={"autocompact_skipped": "auto_compact_disabled"},
|
|
144
|
+
)
|
|
145
|
+
if ctx.consecutive_autocompact_failures >= s.max_consecutive_autocompact_failures:
|
|
146
|
+
return StageResult(
|
|
147
|
+
messages=messages,
|
|
148
|
+
tokens_freed=0,
|
|
149
|
+
extra={
|
|
150
|
+
"autocompact_skipped": "consecutive_failures",
|
|
151
|
+
"consecutive_autocompact_failures": ctx.consecutive_autocompact_failures,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
116
154
|
if ctx.query_source in s.autocompact_skip_query_sources:
|
|
117
155
|
return StageResult(messages=messages, tokens_freed=0, extra={"autocompact_skipped": "query_source"})
|
|
118
156
|
|
|
119
157
|
total = total_estimated_tokens_for_messages(messages, s.num_chars_per_token)
|
|
120
|
-
threshold =
|
|
158
|
+
threshold = autocompact_trigger_threshold_tokens(s)
|
|
121
159
|
slack = ctx.snip_tokens_freed_carry if s.autocompact_snip_carry_threshold_slack else 0
|
|
122
160
|
if total <= threshold + slack:
|
|
123
161
|
return StageResult(messages=messages, tokens_freed=0)
|
|
@@ -135,11 +173,12 @@ class AutocompactStage(CompactionStage):
|
|
|
135
173
|
tail = rest[-k:]
|
|
136
174
|
|
|
137
175
|
block_llm = CompactionBlockingGuard(settings=s).should_block_compact_llm(messages)
|
|
138
|
-
|
|
176
|
+
attempted_llm = (
|
|
139
177
|
s.autocompact_use_llm_when_available
|
|
140
178
|
and ctx.model is not None
|
|
141
179
|
and not block_llm
|
|
142
|
-
)
|
|
180
|
+
)
|
|
181
|
+
if attempted_llm:
|
|
143
182
|
summary = await _astream_summary(ctx.model, s, head, ctx.progress_callback)
|
|
144
183
|
if summary:
|
|
145
184
|
folded = [
|
|
@@ -172,10 +211,13 @@ class AutocompactStage(CompactionStage):
|
|
|
172
211
|
] + tail
|
|
173
212
|
new_total = total_estimated_tokens_for_messages(folded, s.num_chars_per_token)
|
|
174
213
|
mode = "prune_blocked_llm" if block_llm else "prune"
|
|
214
|
+
extra: dict[str, Any] = {"autocompact_mode": mode, "folded_messages": len(head)}
|
|
215
|
+
if attempted_llm:
|
|
216
|
+
extra["autocompact_llm_failed"] = True
|
|
175
217
|
return StageResult(
|
|
176
218
|
messages=folded,
|
|
177
219
|
tokens_freed=max(0, total - new_total),
|
|
178
|
-
extra=
|
|
220
|
+
extra=extra,
|
|
179
221
|
)
|
|
180
222
|
|
|
181
223
|
|
|
@@ -38,6 +38,7 @@ class ContextCompactionContext:
|
|
|
38
38
|
model: Any | None = None
|
|
39
39
|
snip_tokens_freed_carry: int = 0
|
|
40
40
|
query_source: str | None = None
|
|
41
|
+
consecutive_autocompact_failures: int = 0
|
|
41
42
|
config: CompactionConfig = field(default_factory=CompactionConfig)
|
|
42
43
|
progress_callback: CompactProgressCallback = None
|
|
43
44
|
mode: str = "proactive"
|
|
@@ -47,11 +48,14 @@ class ContextCompactionContext:
|
|
|
47
48
|
def from_state(
|
|
48
49
|
cls, state: Mapping[str, Any], model: Any | None, mode: str = "proactive"
|
|
49
50
|
) -> ContextCompactionContext:
|
|
51
|
+
raw_failures = state.get("consecutive_autocompact_failures")
|
|
52
|
+
failures = int(raw_failures) if isinstance(raw_failures, int) else 0
|
|
50
53
|
return cls(
|
|
51
54
|
model=model,
|
|
52
55
|
query_source=state.get("query_source")
|
|
53
56
|
if isinstance(state.get("query_source"), str)
|
|
54
57
|
else None,
|
|
58
|
+
consecutive_autocompact_failures=max(0, failures),
|
|
55
59
|
mode=mode,
|
|
56
60
|
)
|
|
57
61
|
|
|
@@ -204,6 +204,7 @@ def create_initial_state(
|
|
|
204
204
|
# 恢复路径守卫状态
|
|
205
205
|
"collapse_retry_available": False,
|
|
206
206
|
"has_attempted_reactive_compact": False,
|
|
207
|
+
"consecutive_autocompact_failures": 0,
|
|
207
208
|
"compaction_pipeline_meta": None,
|
|
208
209
|
"max_output_tokens_override": None,
|
|
209
210
|
"max_output_tokens_recovery_count": 0,
|
|
@@ -248,6 +249,7 @@ class AgentLoopState(TypedDict):
|
|
|
248
249
|
withhold_retry_count: int # withhold 已重试次数
|
|
249
250
|
collapse_retry_available: bool # collapse_drain_retry 相关守卫
|
|
250
251
|
has_attempted_reactive_compact: bool # reactive_compact 重试守卫
|
|
252
|
+
consecutive_autocompact_failures: int # proactive autocompact LLM 连续失败(CC 熔断)
|
|
251
253
|
compaction_pipeline_meta: dict[str, Any] | None # prune_and_compress 观测
|
|
252
254
|
max_output_tokens_override: int | None # max_output_tokens 升档参数
|
|
253
255
|
max_output_tokens_recovery_count: int # max_output_tokens 恢复计数
|
|
@@ -10,20 +10,15 @@ LangChain AgentX Agent Factory
|
|
|
10
10
|
|
|
11
11
|
参考设计文档:.claude/design/create-opencode-style-agent-design.md
|
|
12
12
|
|
|
13
|
-
LangChain AgentX Agent 状态图(文字版,含 loop_controller):
|
|
13
|
+
LangChain AgentX Agent 状态图(文字版,含 loop_controller · proactive compact gate):
|
|
14
14
|
|
|
15
|
-
START
|
|
15
|
+
START → hook_loop_start → prune_and_compress → hook_before_model → model
|
|
16
16
|
↓
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
↓
|
|
23
|
-
loop_controller(集中处理 resolution / terminal / 工具调用)
|
|
24
|
-
├─ resolution.kind=terminal → END(或 task reconcile / recovery 分支)
|
|
25
|
-
├─ 有工具调用 → tools → (task_event_*/prune_and_compress) → model
|
|
26
|
-
└─ 无工具调用 → END
|
|
17
|
+
hook_stop → loop_controller
|
|
18
|
+
├─ terminal → hook_loop_end → END
|
|
19
|
+
├─ tools → (task_event_* →) prune_and_compress → hook_before_model → model
|
|
20
|
+
├─ 再进 model(无 tools)→ prune_and_compress → hook_before_model → model
|
|
21
|
+
└─ reactive_compact → prune_and_compress(或 task 链)→ … → model
|
|
27
22
|
"""
|
|
28
23
|
|
|
29
24
|
from __future__ import annotations
|
|
@@ -39,6 +34,16 @@ from langchain_core.messages import HumanMessage, SystemMessage
|
|
|
39
34
|
from langchain_core.runnables import RunnableConfig
|
|
40
35
|
from langgraph._internal._runnable import RunnableCallable
|
|
41
36
|
from langgraph.constants import END, START
|
|
37
|
+
|
|
38
|
+
_PRE_MODEL_COMPACT_NODE = "prune_and_compress"
|
|
39
|
+
_BEFORE_MODEL_NODE = "hook_before_model"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _pre_model_gate_destination(loop_entry_node: str) -> str:
|
|
43
|
+
"""CC 对齐:直连 hook_before_model 的 loop 续跑须先过 proactive compact gate。"""
|
|
44
|
+
if loop_entry_node == _BEFORE_MODEL_NODE:
|
|
45
|
+
return _PRE_MODEL_COMPACT_NODE
|
|
46
|
+
return loop_entry_node
|
|
42
47
|
from langgraph.graph.state import StateGraph
|
|
43
48
|
import asyncio
|
|
44
49
|
import dataclasses
|
|
@@ -66,8 +71,9 @@ from ..model.schema_and_format import (
|
|
|
66
71
|
)
|
|
67
72
|
from ..model.model_node import ModelNode
|
|
68
73
|
from ..model.model_nodes import make_model_nodes
|
|
69
|
-
from ..context import default_loop_context_compaction
|
|
74
|
+
from ..context import LoopContextCompaction, default_loop_context_compaction
|
|
70
75
|
from ..context.settings import CompactionSettings
|
|
76
|
+
from ..context.types import CompactProgress
|
|
71
77
|
from ..injection.dedup import filter_attachment_dicts_by_task_command_id
|
|
72
78
|
from ..message_contract import to_record
|
|
73
79
|
from ..model.tool_call_degradation_corrector import (
|
|
@@ -102,6 +108,10 @@ from ...memory.memdir.paths import is_memory_enabled
|
|
|
102
108
|
from ..prompt import SystemPromptSection, build_effective_system_prompt
|
|
103
109
|
from ..prompt.session_context import get_default_session_context_sections
|
|
104
110
|
from ..config import ModelContextResolver
|
|
111
|
+
from ..config.compaction_config import (
|
|
112
|
+
compute_autocompact_trigger_token_threshold,
|
|
113
|
+
resolve_is_auto_compact_enabled,
|
|
114
|
+
)
|
|
105
115
|
from ..config.interaction_profile import (
|
|
106
116
|
assemble_services,
|
|
107
117
|
normalize_config,
|
|
@@ -556,7 +566,7 @@ class LoopGraphBuilder:
|
|
|
556
566
|
# 运行时需要的额外值存储在 self._* 字段中
|
|
557
567
|
|
|
558
568
|
self._compaction_settings = self._build_compaction_settings()
|
|
559
|
-
self._loop_context_compaction =
|
|
569
|
+
self._loop_context_compaction = LoopContextCompaction.with_default_pipeline(
|
|
560
570
|
settings=self._compaction_settings
|
|
561
571
|
)
|
|
562
572
|
self._model = model
|
|
@@ -585,7 +595,10 @@ class LoopGraphBuilder:
|
|
|
585
595
|
session_store.hooks_snapshot = self._hook_snapshot
|
|
586
596
|
from langchain_agentx.tools.skill.repository_factory import build_skill_repository
|
|
587
597
|
|
|
588
|
-
session_store.skill_repository = build_skill_repository(
|
|
598
|
+
session_store.skill_repository = build_skill_repository(
|
|
599
|
+
self._workspace_cfg,
|
|
600
|
+
discovery=self._skill_discovery_layout,
|
|
601
|
+
)
|
|
589
602
|
self._trace_callback_handler: TraceCallbackHandler | None = None
|
|
590
603
|
self._trace_tool_callback_handler: TraceCallbackHandler | None = None
|
|
591
604
|
hook_event_emitter = None
|
|
@@ -745,6 +758,7 @@ class LoopGraphBuilder:
|
|
|
745
758
|
workspace_root=self._workspace_root,
|
|
746
759
|
agent_home_segment=self._agent_home_segment,
|
|
747
760
|
session_cwd=self._workspace_cfg.session_cwd or self._workspace_cfg.workspace_root,
|
|
761
|
+
skill_discovery=self._skill_discovery_layout,
|
|
748
762
|
)
|
|
749
763
|
# 用户已在 loader 上注册自定义 AgentRuntimeTool 时,跳过默认 Agent 注册,
|
|
750
764
|
# 由后续合并循环把用户实例搬入 loop_loader.registry。
|
|
@@ -1080,6 +1094,7 @@ class LoopGraphBuilder:
|
|
|
1080
1094
|
self._workspace_cfg = workspace_cfg
|
|
1081
1095
|
self._workspace_root = str(workspace_cfg.workspace_root)
|
|
1082
1096
|
self._agent_home_segment = workspace_cfg.agent_home_segment
|
|
1097
|
+
self._skill_discovery_layout = self._resolve_skill_discovery_layout()
|
|
1083
1098
|
from langchain_agentx.workspace import WorkspaceBoundary
|
|
1084
1099
|
|
|
1085
1100
|
self._workspace_boundary = WorkspaceBoundary.build(
|
|
@@ -1088,6 +1103,27 @@ class LoopGraphBuilder:
|
|
|
1088
1103
|
write_roots=list(self._config.workspace.write_roots) or None,
|
|
1089
1104
|
)
|
|
1090
1105
|
|
|
1106
|
+
def _resolve_skill_discovery_layout(self) -> Any | None:
|
|
1107
|
+
from langchain_agentx.tools.skill.source_paths import SkillDiscoveryLayout
|
|
1108
|
+
|
|
1109
|
+
sd = self._config.workspace.skill_discovery
|
|
1110
|
+
if sd is None:
|
|
1111
|
+
return None
|
|
1112
|
+
return SkillDiscoveryLayout(
|
|
1113
|
+
project_skills_dir=(
|
|
1114
|
+
Path(sd.project_skills_dir).expanduser()
|
|
1115
|
+
if sd.project_skills_dir is not None
|
|
1116
|
+
else None
|
|
1117
|
+
),
|
|
1118
|
+
global_skills_dir=(
|
|
1119
|
+
Path(sd.global_skills_dir).expanduser()
|
|
1120
|
+
if sd.global_skills_dir is not None
|
|
1121
|
+
else None
|
|
1122
|
+
),
|
|
1123
|
+
include_managed=sd.include_managed,
|
|
1124
|
+
include_user_home=sd.include_user_home,
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1091
1127
|
def _sync_tool_policy_roots_from_workspace(self) -> None:
|
|
1092
1128
|
"""挂载 A:compile 后以 AgentLoopConfig.workspace 刷新 loader policy roots。"""
|
|
1093
1129
|
loader = self._services.loader
|
|
@@ -1328,15 +1364,29 @@ class LoopGraphBuilder:
|
|
|
1328
1364
|
|
|
1329
1365
|
def _build_compaction_settings(self) -> CompactionSettings:
|
|
1330
1366
|
base = CompactionSettings()
|
|
1367
|
+
context_window = self._model_context_resolver.resolve_context_window(self._config)
|
|
1368
|
+
trigger_threshold = compute_autocompact_trigger_token_threshold(
|
|
1369
|
+
context_window_tokens=context_window,
|
|
1370
|
+
max_output_tokens_reserved=self._config.max_output_tokens_reserved,
|
|
1371
|
+
compact_buffer_tokens=self._config.compact_buffer_tokens,
|
|
1372
|
+
)
|
|
1373
|
+
auto_compact_enabled = resolve_is_auto_compact_enabled(
|
|
1374
|
+
config_is_auto_compact=self._config.is_auto_compact
|
|
1375
|
+
)
|
|
1376
|
+
# G1-A (CC 0331): auto + reactive 路径下不在 call 前 synthetic blocking_limit。
|
|
1377
|
+
enable_main_token_block = base.enable_main_model_context_token_block
|
|
1378
|
+
if auto_compact_enabled:
|
|
1379
|
+
enable_main_token_block = False
|
|
1331
1380
|
return CompactionSettings(
|
|
1332
1381
|
num_chars_per_token=base.num_chars_per_token,
|
|
1333
1382
|
prune_protect_tokens=base.prune_protect_tokens,
|
|
1334
1383
|
prune_minimum_tokens=base.prune_minimum_tokens,
|
|
1335
1384
|
prune_protected_tools=base.prune_protected_tools,
|
|
1336
|
-
default_max_context_tokens=
|
|
1337
|
-
self._config
|
|
1338
|
-
),
|
|
1385
|
+
default_max_context_tokens=context_window,
|
|
1339
1386
|
default_compress_trigger_fraction=base.default_compress_trigger_fraction,
|
|
1387
|
+
autocompact_trigger_token_threshold=trigger_threshold,
|
|
1388
|
+
is_auto_compact_enabled=auto_compact_enabled,
|
|
1389
|
+
max_consecutive_autocompact_failures=base.max_consecutive_autocompact_failures,
|
|
1340
1390
|
default_compress_keep_recent_messages=base.default_compress_keep_recent_messages,
|
|
1341
1391
|
autocompact_llm_prompt_kind=base.autocompact_llm_prompt_kind,
|
|
1342
1392
|
compact_custom_instructions=base.compact_custom_instructions,
|
|
@@ -1356,7 +1406,7 @@ class LoopGraphBuilder:
|
|
|
1356
1406
|
compaction_token_warn_fraction=base.compaction_token_warn_fraction,
|
|
1357
1407
|
compaction_block_compact_llm_fraction=base.compaction_block_compact_llm_fraction,
|
|
1358
1408
|
compaction_block_main_model_fraction=base.compaction_block_main_model_fraction,
|
|
1359
|
-
enable_main_model_context_token_block=
|
|
1409
|
+
enable_main_model_context_token_block=enable_main_token_block,
|
|
1360
1410
|
enable_tool_transcript_repair_before_llm=base.enable_tool_transcript_repair_before_llm,
|
|
1361
1411
|
emit_compaction_pipeline_meta=base.emit_compaction_pipeline_meta,
|
|
1362
1412
|
)
|
|
@@ -1400,19 +1450,21 @@ class LoopGraphBuilder:
|
|
|
1400
1450
|
context_schema=self._context_schema,
|
|
1401
1451
|
)
|
|
1402
1452
|
graph_inner.add_node("model", model_node)
|
|
1453
|
+
graph_inner.add_node(
|
|
1454
|
+
_PRE_MODEL_COMPACT_NODE,
|
|
1455
|
+
RunnableCallable(
|
|
1456
|
+
self._prune_and_compress_node,
|
|
1457
|
+
self._prune_and_compress_node_async,
|
|
1458
|
+
trace=False,
|
|
1459
|
+
),
|
|
1460
|
+
)
|
|
1403
1461
|
if tool_node is not None:
|
|
1404
1462
|
graph_inner.add_node("tools", tool_node)
|
|
1405
|
-
|
|
1406
|
-
graph_inner.add_node(
|
|
1407
|
-
"prune_and_compress",
|
|
1408
|
-
RunnableCallable(
|
|
1409
|
-
self._prune_and_compress_node,
|
|
1410
|
-
self._prune_and_compress_node_async,
|
|
1411
|
-
trace=False,
|
|
1412
|
-
),
|
|
1413
|
-
)
|
|
1414
1463
|
return graph_inner
|
|
1415
1464
|
|
|
1465
|
+
def _wire_prune_and_compress_to_before_model(self, graph_inner: Any) -> None:
|
|
1466
|
+
graph_inner.add_edge(_PRE_MODEL_COMPACT_NODE, _BEFORE_MODEL_NODE)
|
|
1467
|
+
|
|
1416
1468
|
def _prune_and_compress_node(self, state: dict[str, Any], runtime: Any) -> dict[str, Any]:
|
|
1417
1469
|
return self._run_prune_and_compress_with_trace(state)
|
|
1418
1470
|
|
|
@@ -1435,6 +1487,31 @@ class LoopGraphBuilder:
|
|
|
1435
1487
|
"skipping wait_for_extraction (use ainvoke for CC-aligned wait)"
|
|
1436
1488
|
)
|
|
1437
1489
|
|
|
1490
|
+
@staticmethod
|
|
1491
|
+
def _compact_progress_phase(
|
|
1492
|
+
progress: CompactProgress,
|
|
1493
|
+
*,
|
|
1494
|
+
seen: bool,
|
|
1495
|
+
) -> tuple[str, bool]:
|
|
1496
|
+
if not seen:
|
|
1497
|
+
return "start", True
|
|
1498
|
+
if progress.stage == "done" and progress.percent >= 100:
|
|
1499
|
+
return "end", True
|
|
1500
|
+
return "tick", True
|
|
1501
|
+
|
|
1502
|
+
def _merge_compact_progress_into_patch(
|
|
1503
|
+
self,
|
|
1504
|
+
patch: dict[str, Any],
|
|
1505
|
+
progress_events: list[dict[str, Any]],
|
|
1506
|
+
) -> None:
|
|
1507
|
+
if not progress_events:
|
|
1508
|
+
return
|
|
1509
|
+
meta = patch.get("compaction_pipeline_meta")
|
|
1510
|
+
if not isinstance(meta, dict):
|
|
1511
|
+
meta = {}
|
|
1512
|
+
patch["compaction_pipeline_meta"] = meta
|
|
1513
|
+
meta["compact_progress"] = progress_events
|
|
1514
|
+
|
|
1438
1515
|
def _apply_prune_and_compress(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
1439
1516
|
if self._session_memory_compact_bridge is not None:
|
|
1440
1517
|
compacted_messages = self._session_memory_compact_bridge.try_compact(
|
|
@@ -1455,6 +1532,55 @@ class LoopGraphBuilder:
|
|
|
1455
1532
|
patch["collapse_retry_available"] = bool(compaction_meta.get("tokens_freed_sum", 0))
|
|
1456
1533
|
return patch
|
|
1457
1534
|
|
|
1535
|
+
async def _apply_prune_and_compress_async(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
1536
|
+
if self._session_memory_compact_bridge is not None:
|
|
1537
|
+
compacted_messages = self._session_memory_compact_bridge.try_compact(
|
|
1538
|
+
list(state.get("messages") or [])
|
|
1539
|
+
)
|
|
1540
|
+
if compacted_messages is not None:
|
|
1541
|
+
return {
|
|
1542
|
+
"messages": compacted_messages,
|
|
1543
|
+
"collapse_retry_available": True,
|
|
1544
|
+
"compaction_pipeline_meta": {
|
|
1545
|
+
"autocompact_mode": "session_memory",
|
|
1546
|
+
"session_memory_compaction": "applied",
|
|
1547
|
+
},
|
|
1548
|
+
}
|
|
1549
|
+
progress_events: list[dict[str, Any]] = []
|
|
1550
|
+
seen_progress = False
|
|
1551
|
+
|
|
1552
|
+
def _on_progress(progress: CompactProgress) -> None:
|
|
1553
|
+
nonlocal seen_progress
|
|
1554
|
+
phase, seen_progress = self._compact_progress_phase(
|
|
1555
|
+
progress, seen=seen_progress
|
|
1556
|
+
)
|
|
1557
|
+
progress_events.append(
|
|
1558
|
+
{
|
|
1559
|
+
"phase": phase,
|
|
1560
|
+
"stage": progress.stage,
|
|
1561
|
+
"stage_index": progress.stage_index,
|
|
1562
|
+
"total_stages": progress.total_stages,
|
|
1563
|
+
"percent": progress.percent,
|
|
1564
|
+
"detail": progress.detail,
|
|
1565
|
+
}
|
|
1566
|
+
)
|
|
1567
|
+
|
|
1568
|
+
emit_progress = bool(
|
|
1569
|
+
self._compaction_settings.is_auto_compact_enabled
|
|
1570
|
+
and self._compaction_settings.emit_compaction_pipeline_meta
|
|
1571
|
+
)
|
|
1572
|
+
patch = await self._loop_context_compaction.arun(
|
|
1573
|
+
state,
|
|
1574
|
+
model=self._model,
|
|
1575
|
+
progress_callback=_on_progress if emit_progress else None,
|
|
1576
|
+
)
|
|
1577
|
+
if emit_progress:
|
|
1578
|
+
self._merge_compact_progress_into_patch(patch, progress_events)
|
|
1579
|
+
compaction_meta = patch.get("compaction_pipeline_meta")
|
|
1580
|
+
if isinstance(compaction_meta, dict):
|
|
1581
|
+
patch["collapse_retry_available"] = bool(compaction_meta.get("tokens_freed_sum", 0))
|
|
1582
|
+
return patch
|
|
1583
|
+
|
|
1458
1584
|
def _run_prune_and_compress_with_trace(self, state: dict[str, Any]) -> dict[str, Any]:
|
|
1459
1585
|
"""执行 compaction,并在当前 session 内记录 compaction span(sync 路径)。"""
|
|
1460
1586
|
collector = state.get("_trace_collector")
|
|
@@ -1520,7 +1646,7 @@ class LoopGraphBuilder:
|
|
|
1520
1646
|
try:
|
|
1521
1647
|
if self._session_memory_compact_bridge is not None:
|
|
1522
1648
|
await self._session_memory_compact_bridge.wait_for_extraction()
|
|
1523
|
-
patch = self.
|
|
1649
|
+
patch = await self._apply_prune_and_compress_async(state)
|
|
1524
1650
|
except Exception as exc:
|
|
1525
1651
|
if span_id is not None:
|
|
1526
1652
|
collector.span_end(
|
|
@@ -1567,6 +1693,7 @@ class LoopGraphBuilder:
|
|
|
1567
1693
|
skill_prefetch_provider=skill_prefetch_provider,
|
|
1568
1694
|
entry_node=entry_node,
|
|
1569
1695
|
)
|
|
1696
|
+
self._wire_prune_and_compress_to_before_model(graph_inner)
|
|
1570
1697
|
graph_inner.add_edge("hook_before_model", "model")
|
|
1571
1698
|
return {
|
|
1572
1699
|
"entry_node": entry_node,
|
|
@@ -1870,12 +1997,8 @@ class LoopGraphBuilder:
|
|
|
1870
1997
|
RunnableCallable(_task_event_reserve_node, None, trace=False),
|
|
1871
1998
|
)
|
|
1872
1999
|
graph_inner.add_edge("task_event_commit_ack", "task_event_reserve")
|
|
1873
|
-
# Phase 5A
|
|
1874
|
-
|
|
1875
|
-
graph_inner.add_edge("task_event_reserve", "prune_and_compress")
|
|
1876
|
-
graph_inner.add_edge("prune_and_compress", raw_loop_entry_node)
|
|
1877
|
-
else:
|
|
1878
|
-
graph_inner.add_edge("task_event_reserve", raw_loop_entry_node)
|
|
2000
|
+
# Phase 5A / proactive autocompact:task 注入后再压缩,再进 hook_before_model。
|
|
2001
|
+
graph_inner.add_edge("task_event_reserve", _PRE_MODEL_COMPACT_NODE)
|
|
1879
2002
|
return "task_event_commit_ack"
|
|
1880
2003
|
|
|
1881
2004
|
def _find_skill_prefetch_provider(
|
|
@@ -1958,10 +2081,10 @@ class LoopGraphBuilder:
|
|
|
1958
2081
|
)
|
|
1959
2082
|
graph_inner.add_edge(START, "skill_init")
|
|
1960
2083
|
graph_inner.add_edge("skill_init", "hook_loop_start")
|
|
1961
|
-
graph_inner.add_edge("hook_loop_start",
|
|
2084
|
+
graph_inner.add_edge("hook_loop_start", _PRE_MODEL_COMPACT_NODE)
|
|
1962
2085
|
return
|
|
1963
2086
|
graph_inner.add_edge(START, "hook_loop_start")
|
|
1964
|
-
graph_inner.add_edge("hook_loop_start",
|
|
2087
|
+
graph_inner.add_edge("hook_loop_start", _PRE_MODEL_COMPACT_NODE)
|
|
1965
2088
|
|
|
1966
2089
|
def _wire_main_edges(self, *, graph_inner: Any, tool_node: Any, routing: dict[str, Any]) -> None:
|
|
1967
2090
|
loop_entry_node = routing["loop_entry_node"]
|
|
@@ -2014,8 +2137,8 @@ class LoopGraphBuilder:
|
|
|
2014
2137
|
tools_model_destination = loop_entry_node
|
|
2015
2138
|
tools_to_model_destinations = [loop_entry_node, exit_node]
|
|
2016
2139
|
else:
|
|
2017
|
-
tools_model_destination =
|
|
2018
|
-
tools_to_model_destinations = [
|
|
2140
|
+
tools_model_destination = _PRE_MODEL_COMPACT_NODE
|
|
2141
|
+
tools_to_model_destinations = [_PRE_MODEL_COMPACT_NODE, exit_node]
|
|
2019
2142
|
|
|
2020
2143
|
graph_inner.add_conditional_edges(
|
|
2021
2144
|
"tools",
|
|
@@ -2030,8 +2153,6 @@ class LoopGraphBuilder:
|
|
|
2030
2153
|
),
|
|
2031
2154
|
tools_to_model_destinations,
|
|
2032
2155
|
)
|
|
2033
|
-
if not wire_task_event_nodes:
|
|
2034
|
-
graph_inner.add_edge("prune_and_compress", loop_entry_node)
|
|
2035
2156
|
graph_inner.add_edge(loop_exit_node, "hook_after_model")
|
|
2036
2157
|
graph_inner.add_edge("hook_after_model", "hook_stop")
|
|
2037
2158
|
self._wire_loop_controller(
|
|
@@ -2053,11 +2174,12 @@ class LoopGraphBuilder:
|
|
|
2053
2174
|
wire_task_event_nodes: bool,
|
|
2054
2175
|
tools_destination: str | None = None,
|
|
2055
2176
|
) -> None:
|
|
2177
|
+
model_gate = _pre_model_gate_destination(loop_entry_node)
|
|
2056
2178
|
graph_inner.add_node(
|
|
2057
2179
|
"loop_controller",
|
|
2058
2180
|
RunnableCallable(
|
|
2059
2181
|
_make_loop_controller_node(
|
|
2060
|
-
model_destination=
|
|
2182
|
+
model_destination=model_gate,
|
|
2061
2183
|
tools_destination=tools_destination or loop_entry_node,
|
|
2062
2184
|
structured_output_tools=self._structured_output_tools,
|
|
2063
2185
|
end_destination=exit_node,
|
|
@@ -2081,7 +2203,7 @@ class LoopGraphBuilder:
|
|
|
2081
2203
|
def _reactive_compact_node(state, runtime):
|
|
2082
2204
|
node = ReactiveCompactNode(
|
|
2083
2205
|
compaction_service=default_loop_context_compaction(),
|
|
2084
|
-
loop_entry_destination=
|
|
2206
|
+
loop_entry_destination=model_gate,
|
|
2085
2207
|
end_destination=exit_node,
|
|
2086
2208
|
)
|
|
2087
2209
|
return node(state)
|
|
@@ -2090,13 +2212,14 @@ class LoopGraphBuilder:
|
|
|
2090
2212
|
"reactive_compact",
|
|
2091
2213
|
RunnableCallable(_reactive_compact_node, None, trace=False),
|
|
2092
2214
|
)
|
|
2215
|
+
reactive_destinations = [model_gate, exit_node]
|
|
2093
2216
|
graph_inner.add_conditional_edges(
|
|
2094
2217
|
"reactive_compact",
|
|
2095
|
-
lambda state: state.get("destination",
|
|
2096
|
-
|
|
2218
|
+
lambda state: state.get("destination", model_gate),
|
|
2219
|
+
reactive_destinations,
|
|
2097
2220
|
)
|
|
2098
2221
|
|
|
2099
|
-
loop_controller_destinations = [
|
|
2222
|
+
loop_controller_destinations = [model_gate, exit_node, "reactive_compact"]
|
|
2100
2223
|
if tools_destination is not None:
|
|
2101
2224
|
loop_controller_destinations.insert(0, tools_destination)
|
|
2102
2225
|
if wire_task_event_nodes and "task_event_commit_ack" not in loop_controller_destinations:
|
|
@@ -2118,16 +2241,17 @@ class LoopGraphBuilder:
|
|
|
2118
2241
|
wire_task_event_nodes: bool,
|
|
2119
2242
|
) -> None:
|
|
2120
2243
|
graph_inner.add_edge(loop_exit_node, "hook_after_model")
|
|
2244
|
+
model_gate = _pre_model_gate_destination(loop_entry_node)
|
|
2121
2245
|
graph_inner.add_conditional_edges(
|
|
2122
2246
|
"hook_after_model",
|
|
2123
2247
|
RunnableCallable(
|
|
2124
2248
|
_make_model_to_model_edge(
|
|
2125
|
-
model_destination=
|
|
2249
|
+
model_destination=model_gate,
|
|
2126
2250
|
end_destination="hook_stop",
|
|
2127
2251
|
),
|
|
2128
2252
|
trace=False,
|
|
2129
2253
|
),
|
|
2130
|
-
[
|
|
2254
|
+
[model_gate, "hook_stop"],
|
|
2131
2255
|
)
|
|
2132
2256
|
self._wire_loop_controller(
|
|
2133
2257
|
graph_inner=graph_inner,
|
|
@@ -128,6 +128,7 @@ class LangchainAgentEventType(Enum):
|
|
|
128
128
|
HOOK_PROGRESS = "hook-progress"
|
|
129
129
|
HOOK_FINISHED = "hook-finished"
|
|
130
130
|
COMPACT_BOUNDARY = "compact-boundary"
|
|
131
|
+
COMPACT_PROGRESS = "compact-progress"
|
|
131
132
|
MEMORY_SAVED = "memory-saved"
|
|
132
133
|
SUBAGENT_START = "subagent-start"
|
|
133
134
|
SUBAGENT_END = "subagent-end"
|
|
@@ -850,6 +851,16 @@ class AgentEventProjector:
|
|
|
850
851
|
|
|
851
852
|
if self._is_compact_node(name):
|
|
852
853
|
meta = extract_compaction_meta_from_chain_end_data(data)
|
|
854
|
+
if isinstance(meta, dict) and self.enable_compact_events:
|
|
855
|
+
raw_progress = meta.get("compact_progress")
|
|
856
|
+
if isinstance(raw_progress, list):
|
|
857
|
+
for item in raw_progress:
|
|
858
|
+
if isinstance(item, dict):
|
|
859
|
+
yield self._event(
|
|
860
|
+
event_type=LangchainAgentEventType.COMPACT_PROGRESS,
|
|
861
|
+
data=dict(item),
|
|
862
|
+
step_index=self.state.step_index or None,
|
|
863
|
+
)
|
|
853
864
|
if is_user_visible_compact(meta):
|
|
854
865
|
if self.enable_compact_events:
|
|
855
866
|
compact_data = self._extract_compact_summary(data)
|
|
@@ -131,6 +131,7 @@ class ToolRuntimeLoader:
|
|
|
131
131
|
workspace_root: str,
|
|
132
132
|
agent_home_segment: str = ".langchain_agentx",
|
|
133
133
|
session_cwd: str | Path | None = None,
|
|
134
|
+
skill_discovery: Any | None = None,
|
|
134
135
|
) -> LoopLoader:
|
|
135
136
|
"""构造独立的 per-loop 工具注册表(不污染 loader 主 registry)。"""
|
|
136
137
|
return LoopLoader(
|
|
@@ -138,6 +139,7 @@ class ToolRuntimeLoader:
|
|
|
138
139
|
workspace_root=workspace_root,
|
|
139
140
|
agent_home_segment=agent_home_segment,
|
|
140
141
|
session_cwd=session_cwd,
|
|
142
|
+
skill_discovery=skill_discovery,
|
|
141
143
|
)
|
|
142
144
|
|
|
143
145
|
def create_session(self, session_id: str | None = None) -> AgentSessionStore:
|
|
@@ -34,11 +34,13 @@ class LoopLoader:
|
|
|
34
34
|
workspace_root: str,
|
|
35
35
|
agent_home_segment: str = ".langchain_agentx",
|
|
36
36
|
session_cwd: str | Path | None = None,
|
|
37
|
+
skill_discovery: Any | None = None,
|
|
37
38
|
) -> None:
|
|
38
39
|
self._process = process
|
|
39
40
|
self._workspace_root = workspace_root
|
|
40
41
|
self._agent_home = agent_home_segment
|
|
41
42
|
self._session_cwd = session_cwd
|
|
43
|
+
self._skill_discovery = skill_discovery
|
|
42
44
|
self._registry = RuntimeToolRegistry(
|
|
43
45
|
pipeline=process.pipeline,
|
|
44
46
|
adapter=process.adapter,
|
|
@@ -107,7 +109,7 @@ class LoopLoader:
|
|
|
107
109
|
agent_home_segment=self._agent_home,
|
|
108
110
|
session_cwd=effective_cwd,
|
|
109
111
|
)
|
|
110
|
-
self._merge_skill_read_roots_policy(cfg)
|
|
112
|
+
self._merge_skill_read_roots_policy(cfg, discovery=self._skill_discovery)
|
|
111
113
|
self._inject_project_memory_policy(cfg)
|
|
112
114
|
plugin_ex = PluginCacheGlobExclusions(
|
|
113
115
|
cache_root=str(cfg.plugin_cache_dir.resolve()),
|
|
@@ -211,11 +213,19 @@ class LoopLoader:
|
|
|
211
213
|
setter(patterns)
|
|
212
214
|
return patterns
|
|
213
215
|
|
|
214
|
-
def _merge_skill_read_roots_policy(
|
|
216
|
+
def _merge_skill_read_roots_policy(
|
|
217
|
+
self,
|
|
218
|
+
workspace_cfg: Any,
|
|
219
|
+
*,
|
|
220
|
+
discovery: Any | None = None,
|
|
221
|
+
) -> None:
|
|
215
222
|
"""G-2:skill 发现目录并入 read_roots(在 project_memory 注入之前)。"""
|
|
216
223
|
from langchain_agentx.tools.skill.source_paths import resolve_skill_read_roots
|
|
217
224
|
|
|
218
|
-
roots = resolve_skill_read_roots(
|
|
225
|
+
roots = resolve_skill_read_roots(
|
|
226
|
+
workspace_cfg,
|
|
227
|
+
discovery=discovery if discovery is not None else self._skill_discovery,
|
|
228
|
+
)
|
|
219
229
|
if not roots:
|
|
220
230
|
return
|
|
221
231
|
base = self._process.effective_policy_config()
|
|
@@ -35,7 +35,7 @@ if TYPE_CHECKING:
|
|
|
35
35
|
from langchain_agentx.plugin.registries import SkillRegistry
|
|
36
36
|
from langchain_agentx.workspace.config import ResolvedAgentStateConfig
|
|
37
37
|
|
|
38
|
-
SkillLoadedFrom = Literal["bundled", "plugin", "managed", "user", "project", "dynamic"]
|
|
38
|
+
SkillLoadedFrom = Literal["bundled", "plugin", "managed", "user", "project", "global", "dynamic"]
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
@dataclass(frozen=True, slots=True)
|
|
@@ -57,6 +57,8 @@ class MultiSourceSkillCommandRepository:
|
|
|
57
57
|
|
|
58
58
|
for spec in self._sources:
|
|
59
59
|
skills_dir = spec.skills_dir
|
|
60
|
+
if not skills_dir.expanduser().resolve().is_dir():
|
|
61
|
+
continue
|
|
60
62
|
if self._gitignore_guard.should_skip_skills_dir(skills_dir, cwd=self._cwd):
|
|
61
63
|
continue
|
|
62
64
|
for cmd in self._dir_loader.load_commands_from_dir(skills_dir):
|
|
@@ -17,7 +17,7 @@ from langchain_agentx.workspace.config import ResolvedAgentStateConfig
|
|
|
17
17
|
|
|
18
18
|
from .loader import SkillCommandRepository
|
|
19
19
|
from .multi_source_repository import MultiSourceSkillCommandRepository
|
|
20
|
-
from .source_paths import resolve_skill_source_specs
|
|
20
|
+
from .source_paths import SkillDiscoveryLayout, resolve_skill_source_specs
|
|
21
21
|
|
|
22
22
|
SkillRepository = Union[SkillCommandRepository, MultiSourceSkillCommandRepository]
|
|
23
23
|
|
|
@@ -26,13 +26,15 @@ def build_skill_repository(
|
|
|
26
26
|
workspace_cfg: ResolvedAgentStateConfig,
|
|
27
27
|
*,
|
|
28
28
|
skills_root: Path | None = None,
|
|
29
|
+
discovery: SkillDiscoveryLayout | None = None,
|
|
29
30
|
) -> SkillRepository:
|
|
30
31
|
if skills_root is not None:
|
|
31
32
|
return SkillCommandRepository(Path(skills_root).expanduser().resolve())
|
|
33
|
+
cwd = workspace_cfg.session_cwd or workspace_cfg.workspace_root
|
|
32
34
|
return MultiSourceSkillCommandRepository(
|
|
33
|
-
resolve_skill_source_specs(workspace_cfg),
|
|
35
|
+
resolve_skill_source_specs(workspace_cfg, discovery=discovery),
|
|
34
36
|
workspace_root=workspace_cfg.workspace_root,
|
|
35
|
-
cwd=
|
|
37
|
+
cwd=cwd,
|
|
36
38
|
)
|
|
37
39
|
|
|
38
40
|
|
|
@@ -26,7 +26,21 @@ from langchain_agentx.workspace.project_key import resolve_config_home_dir
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
27
|
from .models import SkillCommand
|
|
28
28
|
|
|
29
|
-
DiskSkillSource = Literal["managed", "user", "project"]
|
|
29
|
+
DiskSkillSource = Literal["managed", "user", "project", "global"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True, slots=True)
|
|
33
|
+
class SkillDiscoveryLayout:
|
|
34
|
+
"""显式 skill 发现层级(Workflow 节点 / 双 workspace 宿主)。
|
|
35
|
+
|
|
36
|
+
first-wins 顺序:project → global → user → managed(与 CC 项目覆盖全局一致)。
|
|
37
|
+
未设 ``discovery`` 时保留 legacy:managed → user → project。
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
project_skills_dir: Path | None = None
|
|
41
|
+
global_skills_dir: Path | None = None
|
|
42
|
+
include_managed: bool = False
|
|
43
|
+
include_user_home: bool = False
|
|
30
44
|
|
|
31
45
|
|
|
32
46
|
@dataclass(frozen=True, slots=True)
|
|
@@ -61,23 +75,35 @@ def resolve_managed_skills_dir(agent_home_segment: str) -> Path:
|
|
|
61
75
|
return resolve_managed_settings_root() / segment / "skills"
|
|
62
76
|
|
|
63
77
|
|
|
78
|
+
def _existing_skills_dir(path: Path) -> Path | None:
|
|
79
|
+
try:
|
|
80
|
+
resolved = path.expanduser().resolve()
|
|
81
|
+
except OSError:
|
|
82
|
+
return None
|
|
83
|
+
if resolved.is_dir():
|
|
84
|
+
return resolved
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
64
88
|
def resolve_skill_read_roots(
|
|
65
89
|
workspace_cfg: ResolvedAgentStateConfig,
|
|
66
90
|
*,
|
|
67
91
|
project_skills_dir: Path | None = None,
|
|
92
|
+
discovery: SkillDiscoveryLayout | None = None,
|
|
68
93
|
) -> tuple[Path, ...]:
|
|
69
|
-
"""Skill
|
|
94
|
+
"""Skill 可读根(仅 **已存在** 的目录;S-5 发现语义,非 boundary 错误)。"""
|
|
70
95
|
seen: set[Path] = set()
|
|
71
96
|
roots: list[Path] = []
|
|
72
97
|
for spec in resolve_skill_source_specs(
|
|
73
98
|
workspace_cfg,
|
|
74
99
|
project_skills_dir=project_skills_dir,
|
|
100
|
+
discovery=discovery,
|
|
75
101
|
):
|
|
76
|
-
|
|
77
|
-
if
|
|
102
|
+
existing = _existing_skills_dir(spec.skills_dir)
|
|
103
|
+
if existing is None or existing in seen:
|
|
78
104
|
continue
|
|
79
|
-
seen.add(
|
|
80
|
-
roots.append(
|
|
105
|
+
seen.add(existing)
|
|
106
|
+
roots.append(existing)
|
|
81
107
|
return tuple(roots)
|
|
82
108
|
|
|
83
109
|
|
|
@@ -85,20 +111,44 @@ def resolve_skill_source_specs(
|
|
|
85
111
|
workspace_cfg: ResolvedAgentStateConfig,
|
|
86
112
|
*,
|
|
87
113
|
project_skills_dir: Path | None = None,
|
|
114
|
+
discovery: SkillDiscoveryLayout | None = None,
|
|
88
115
|
) -> list[SkillSourceSpec]:
|
|
89
|
-
"""
|
|
116
|
+
"""Skill 源列表;repository first-wins 按列表顺序。"""
|
|
90
117
|
segment = workspace_cfg.agent_home_segment
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
118
|
+
if discovery is None:
|
|
119
|
+
project_dir = project_skills_dir or workspace_cfg.skills_dir
|
|
120
|
+
specs: list[SkillSourceSpec] = []
|
|
121
|
+
if not policy_skills_disabled():
|
|
122
|
+
specs.append(SkillSourceSpec(resolve_managed_skills_dir(segment), "managed"))
|
|
123
|
+
specs.append(SkillSourceSpec(resolve_user_skills_dir(segment), "user"))
|
|
124
|
+
specs.append(SkillSourceSpec(Path(project_dir).expanduser().resolve(), "project"))
|
|
125
|
+
return specs
|
|
126
|
+
|
|
127
|
+
project_dir = (
|
|
128
|
+
discovery.project_skills_dir
|
|
129
|
+
or project_skills_dir
|
|
130
|
+
or workspace_cfg.skills_dir
|
|
131
|
+
)
|
|
132
|
+
specs = [
|
|
133
|
+
SkillSourceSpec(Path(project_dir).expanduser().resolve(), "project"),
|
|
134
|
+
]
|
|
135
|
+
if discovery.global_skills_dir is not None:
|
|
136
|
+
specs.append(
|
|
137
|
+
SkillSourceSpec(
|
|
138
|
+
Path(discovery.global_skills_dir).expanduser().resolve(),
|
|
139
|
+
"global",
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
if discovery.include_user_home:
|
|
143
|
+
specs.append(SkillSourceSpec(resolve_user_skills_dir(segment), "user"))
|
|
144
|
+
if not policy_skills_disabled() and discovery.include_managed:
|
|
94
145
|
specs.append(SkillSourceSpec(resolve_managed_skills_dir(segment), "managed"))
|
|
95
|
-
specs.append(SkillSourceSpec(resolve_user_skills_dir(segment), "user"))
|
|
96
|
-
specs.append(SkillSourceSpec(Path(project_dir).expanduser().resolve(), "project"))
|
|
97
146
|
return specs
|
|
98
147
|
|
|
99
148
|
|
|
100
149
|
__all__ = [
|
|
101
150
|
"DiskSkillSource",
|
|
151
|
+
"SkillDiscoveryLayout",
|
|
102
152
|
"SkillSourceSpec",
|
|
103
153
|
"SourcedSkillEntry",
|
|
104
154
|
"policy_skills_disabled",
|
|
@@ -149,6 +149,10 @@ class ProjectMemoryLayoutResolver:
|
|
|
149
149
|
key_sess = self.sanitizer.sanitize(str(cwd))
|
|
150
150
|
l2_project_dir = config_home / _PROJECTS_DIRNAME / key_l2
|
|
151
151
|
sess_project_dir = config_home / _PROJECTS_DIRNAME / key_sess
|
|
152
|
+
# SSOT:布局解析即保证 projects/{key} 存在,避免启动链路先读/扫描 transcript 时
|
|
153
|
+
# FileNotFoundError(子目录首次启动、key_sess 全新时必现)。
|
|
154
|
+
l2_project_dir.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
sess_project_dir.mkdir(parents=True, exist_ok=True)
|
|
152
156
|
override = _resolve_memory_dir_override(memory_dir_override)
|
|
153
157
|
return ProjectMemoryLayout(
|
|
154
158
|
agent_home_segment=agent_home_segment.strip(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
langchain_agentx/__init__.py,sha256=
|
|
1
|
+
langchain_agentx/__init__.py,sha256=t5u-2g4slzGHVtYyJTJcFQgv_d5BfqHyJWeSDe9FMHY,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
|
|
@@ -29,10 +29,11 @@ langchain_agentx/loop/benchmark_frozen_config.py,sha256=YI4IaDVMBOEucF36F2oNv5mw
|
|
|
29
29
|
langchain_agentx/loop/loop_abort.py,sha256=cKlHxZh0u7-drCVqEiRtzkO270MHw6grJuZ1NpKoGfo,3250
|
|
30
30
|
langchain_agentx/loop/message_contract.py,sha256=Twl83TneU2PRdxgGRBxs6S9_ovRT6cb5WeCUzd6oJnE,2196
|
|
31
31
|
langchain_agentx/loop/run_context.py,sha256=dhNiLhvqD_QYacGMA0B5P3wWpSe3O6wU_XcVEzb86Z4,8303
|
|
32
|
-
langchain_agentx/loop/config/__init__.py,sha256=
|
|
32
|
+
langchain_agentx/loop/config/__init__.py,sha256=0pf3Mml7LZpQ1C3_AtROFQClrBQegaGCG4YCTfSr9v8,1137
|
|
33
33
|
langchain_agentx/loop/config/agent_config.py,sha256=tOnAHFor6dkle3FvUOVx0YsmiYVTH11UrWSyTmOLV0c,3100
|
|
34
|
+
langchain_agentx/loop/config/compaction_config.py,sha256=MmXqY47-9nPDhdXvnU92Xp0XeaZ605ErQwX9d2emEkM,1176
|
|
34
35
|
langchain_agentx/loop/config/interaction_profile.py,sha256=UWrlI3aTWjYRq1mDeXL-xL-iCrQU-bXDXhETP6q9icY,10020
|
|
35
|
-
langchain_agentx/loop/config/loop_config.py,sha256=
|
|
36
|
+
langchain_agentx/loop/config/loop_config.py,sha256=3kYDXg91PSDNVUdHGm6In0mHJRpfrkITs4OLKdOngYY,16180
|
|
36
37
|
langchain_agentx/loop/config/loop_runtime_overlay.py,sha256=PuB4vFdqCHXRbAFhAJIrtwq9pUIrHafyn0ApD4s06KU,2440
|
|
37
38
|
langchain_agentx/loop/config/model_context_resolver.py,sha256=o9RR0Vnu07TD4Y3V8jk5V1NfkMcPcLjE8uaaLOe22Qo,4067
|
|
38
39
|
langchain_agentx/loop/config/model_retry_config.py,sha256=bakCbGeNRO-_SXMT_LLE3nu3m9scZe1MSxPdyEGO99w,8178
|
|
@@ -45,13 +46,13 @@ langchain_agentx/loop/context/__init__.py,sha256=RXVTFjHcH6_8q1x13Qk9ZP8RVbqZoyh
|
|
|
45
46
|
langchain_agentx/loop/context/blocking_guard.py,sha256=Rmt5qZdE8Zf0bt-Q6WwMSfG869772PE8w1mNPvBQRCQ,3789
|
|
46
47
|
langchain_agentx/loop/context/compaction_service.py,sha256=MwAPPaT31XbzgwqpuknGBZlYBRKxwyhWXDifEnBgh2E,3725
|
|
47
48
|
langchain_agentx/loop/context/message_utils.py,sha256=6qIwtHeh63OlMqOnPUpEEWRknU9KJTGkcY1v3tz6feY,2010
|
|
48
|
-
langchain_agentx/loop/context/pipeline.py,sha256=
|
|
49
|
+
langchain_agentx/loop/context/pipeline.py,sha256=PrhCWWKUGGwMGd-F8UX6vt3U7tlZo2ZZDLV0CTTDSJ4,8279
|
|
49
50
|
langchain_agentx/loop/context/query_source.py,sha256=-wqVpST64jXF1ZelcGdvIsJE4ngv54eco4gmeS3pyyg,5118
|
|
50
51
|
langchain_agentx/loop/context/recovery_mode.py,sha256=ytHGnE-g91pDQIkyVcfxEw6XgJGZ5eVJ12jb3zaeXWU,1100
|
|
51
|
-
langchain_agentx/loop/context/settings.py,sha256=
|
|
52
|
-
langchain_agentx/loop/context/types.py,sha256=
|
|
52
|
+
langchain_agentx/loop/context/settings.py,sha256=EO35T2P5tIUIqkO42ffLTRD-U-w2aRxeAYuHFHYc0nE,6589
|
|
53
|
+
langchain_agentx/loop/context/types.py,sha256=jWXibGiJY6sQnd5-OQu9z7ZZM8Efi1X-6M8zJHNutqw,3637
|
|
53
54
|
langchain_agentx/loop/context/stages/__init__.py,sha256=wk9CFh8_Akw8qs3Dldnx14VE1MhyF5i9Cg3phTVChhg,892
|
|
54
|
-
langchain_agentx/loop/context/stages/autocompact.py,sha256=
|
|
55
|
+
langchain_agentx/loop/context/stages/autocompact.py,sha256=7segcCEl0kk9hK6YRHAbM67e8As1sE_CRaUUfUnHFh0,13581
|
|
55
56
|
langchain_agentx/loop/context/stages/base.py,sha256=LVfCp-DLySrTExy0J-M40tDJvzbOXxfZxXvi7XQayA8,1380
|
|
56
57
|
langchain_agentx/loop/context/stages/collapse.py,sha256=yOf3nfWZr83rq2qnhUpuTCLFHcGvF7my8klMLmfZ7jc,2971
|
|
57
58
|
langchain_agentx/loop/context/stages/microcompact.py,sha256=WnT58w85xcaSzsGEww5TEnU1noAzkao3ZgAARo65a7Y,3142
|
|
@@ -60,7 +61,7 @@ langchain_agentx/loop/context/stages/snip.py,sha256=AZy3D2XPrL1GQU_gE9isKApcphXw
|
|
|
60
61
|
langchain_agentx/loop/context/stages/tool_result_budget.py,sha256=zQcJynRnC8QF30cEDduHkso6jChYjckVcnOjYSrCrtg,3833
|
|
61
62
|
langchain_agentx/loop/exit/__init__.py,sha256=3-CMz5ApkJeBc549vRfgNABenyYKVbib4IdkErVtGcM,528
|
|
62
63
|
langchain_agentx/loop/exit/api_error_detection.py,sha256=QzyMTov4VrkpHmn288Tw7DN00hiyJNXMKlOrCbvsiAA,1479
|
|
63
|
-
langchain_agentx/loop/exit/exit_logic.py,sha256=
|
|
64
|
+
langchain_agentx/loop/exit/exit_logic.py,sha256=3I63n9pyjHD9CfpboWSLNm2O2J8c5PlVjcxesir40k8,14068
|
|
64
65
|
langchain_agentx/loop/exit/query_source.py,sha256=b_VB13zxNbLAqUAuDt1E3SII3jNLagY-FdN6SbczTsI,2082
|
|
65
66
|
langchain_agentx/loop/exit/reason_codes.py,sha256=Nk1x2ipftRIRa4U1Z2goyXXWB9TsPFL9TW6WfvtMCEo,4810
|
|
66
67
|
langchain_agentx/loop/exit/resolution.py,sha256=taIMHzAUtgrQoU9446-CUXBN0PyCujhl6wBQfbxR1qI,1427
|
|
@@ -68,7 +69,7 @@ langchain_agentx/loop/exit/terminal.py,sha256=_DAZk-kS4Xd0nxMlayLo60-uj1Y5ozgBjL
|
|
|
68
69
|
langchain_agentx/loop/exit/withheld_error.py,sha256=LfX3j7Rgd9VoMzlz7GOs9TPuRe7RXr_Gxweyotx3tqQ,1846
|
|
69
70
|
langchain_agentx/loop/graph/__init__.py,sha256=9pUUqn7G87n3lV45iYIU24j0wcTHyCD--SSdtv3urh4,136
|
|
70
71
|
langchain_agentx/loop/graph/builtin_loop_control.py,sha256=31o4H6SDGC_vECSliXecuVEnzlD1ahlVZ5JzEpxYY04,9248
|
|
71
|
-
langchain_agentx/loop/graph/factory.py,sha256=
|
|
72
|
+
langchain_agentx/loop/graph/factory.py,sha256=n0lszl2jYBq1G_rzgmt0-0mCmTa3zuTqrVCow4lUMso,109909
|
|
72
73
|
langchain_agentx/loop/graph/graph_edges.py,sha256=kW6Npx-nMIPzcaisCxVPkTwf5Y9GOKy1NWCNkJWeJBQ,47370
|
|
73
74
|
langchain_agentx/loop/graph/reactive_compact_node.py,sha256=633b5hTnR3BK8GBEKWhzOwGB0BQrVRKozNxDomWFjLo,8762
|
|
74
75
|
langchain_agentx/loop/graph/runtime_tools_node.py,sha256=wQkbc0ophyefpPZEc0q1vKoJOIdS4S6d6hSsrkdRLDk,7387
|
|
@@ -217,7 +218,7 @@ langchain_agentx/observability/evaluation/checkers/tool_behavior.py,sha256=o1nH8
|
|
|
217
218
|
langchain_agentx/observability/events/__init__.py,sha256=Zr9Iguf9XsJQLsCJy13YFdBtXHPEnI9aSWXWmA2MisA,1497
|
|
218
219
|
langchain_agentx/observability/events/accumulated_text_scope.py,sha256=M4OvuQTgReF8SFrOmr-M5vyOJpzfvxj_DKD5bVpGb70,3169
|
|
219
220
|
langchain_agentx/observability/events/agent_defaults.py,sha256=gmX0P__lux5xbCb8dtAlVCb3wZ7avaqFleoW4Q5anw4,1187
|
|
220
|
-
langchain_agentx/observability/events/agent_event_projector.py,sha256=
|
|
221
|
+
langchain_agentx/observability/events/agent_event_projector.py,sha256=SthsPhWuM__Bfm3GoxHw-JCoMQ5xgn_eDgi5n4ib6-4,64600
|
|
221
222
|
langchain_agentx/observability/events/api_retry_projection.py,sha256=XGb09sNOi6k1ZGW4YqMLBE8GDxNwNMprk62jV9TS9dQ,2433
|
|
222
223
|
langchain_agentx/observability/events/compact_event_gate.py,sha256=50RJemwB1lnQn_NFWOFSgU418ZUR8dbd_v0538PT7zM,4751
|
|
223
224
|
langchain_agentx/observability/events/factory.py,sha256=gqMATIK9tBQzaPWSPhCCSPuCq-WF562ZDTnFUaMm9Qc,1570
|
|
@@ -406,8 +407,8 @@ langchain_agentx/tool_runtime/failure_event_emitter.py,sha256=C7c4Gc7Vo_R6_ah2Xx
|
|
|
406
407
|
langchain_agentx/tool_runtime/identical_call_cache.py,sha256=v8WWuTHWR5ThujcMO4Ty28mZS_w-JN24267fZ4PHWK8,3865
|
|
407
408
|
langchain_agentx/tool_runtime/inner_permission_chain.py,sha256=50wYXRu7Hi-rvDiI2QxSLC4uBAsJwG3Vp-v3AutLY4E,17629
|
|
408
409
|
langchain_agentx/tool_runtime/limits.py,sha256=imE1IetmyKQM82INR0VkP3qZNM4bjUJuf1CG5BkY0uw,356
|
|
409
|
-
langchain_agentx/tool_runtime/loader.py,sha256=
|
|
410
|
-
langchain_agentx/tool_runtime/loop_loader.py,sha256=
|
|
410
|
+
langchain_agentx/tool_runtime/loader.py,sha256=PL9WTWhK4BffoVsIjvCZP45tXbE8a2Zc0vp0bJKg2ew,9541
|
|
411
|
+
langchain_agentx/tool_runtime/loop_loader.py,sha256=p_gDv9HWwPnO9KBRJWp8bNBuGq_-zh-yB8Ns0X3qQqA,9920
|
|
411
412
|
langchain_agentx/tool_runtime/loop_runtime_context.py,sha256=HBZMdtKD7MpU8o7JmURFZGPTfxr2aFFlFF2nBZR_n8s,3613
|
|
412
413
|
langchain_agentx/tool_runtime/messages.py,sha256=Fp-ZiLAbOQAQa8uIv-owE8PXtWyhpYSXxtzy3PVbKSw,3631
|
|
413
414
|
langchain_agentx/tool_runtime/models.py,sha256=cO9EgLKPXiktHl_xXtBwLQrCo6mj4tbT4WoPmuFLE0w,20345
|
|
@@ -606,7 +607,7 @@ langchain_agentx/tools/shared/read_state_guard.py,sha256=YSDyACWwBGVl-GhpR7qDzYg
|
|
|
606
607
|
langchain_agentx/tools/shared/staleness.py,sha256=wTSpnLdazuxTdbC5U7zNLdWxkMPq4F7M0TbrIBOBbKA,2519
|
|
607
608
|
langchain_agentx/tools/skill/__init__.py,sha256=hNIgFeIcwpifU34Itwv5y25G4dvqBxY1XJEYfrOsBmc,132
|
|
608
609
|
langchain_agentx/tools/skill/argument_substitution.py,sha256=xj_naOGCgPG7x9HAE2GzYsPQ50iDueWGjU02Oa3Ml98,2669
|
|
609
|
-
langchain_agentx/tools/skill/catalog.py,sha256=
|
|
610
|
+
langchain_agentx/tools/skill/catalog.py,sha256=kYCchsAgz32PQxkhRViewUpmaJW3mvRF1AhBEugGC88,16387
|
|
610
611
|
langchain_agentx/tools/skill/dynamic_catalog.py,sha256=kc9tcAJMbvieIaO0SN89b58I0OIjPRsccKv8pLVoyXI,5409
|
|
611
612
|
langchain_agentx/tools/skill/dynamic_discoverer.py,sha256=1zUcWHJn96UFJeDY9NPsjpZAJ3iQFAq_Y7REyUGXfMo,2978
|
|
612
613
|
langchain_agentx/tools/skill/file_discovery.py,sha256=mEMSg5eqGbv0bU7ZWY_i7WdYZsL618pMo0wMrbunbSc,2202
|
|
@@ -615,14 +616,14 @@ langchain_agentx/tools/skill/guards.py,sha256=tO6hLncKW0eMpWLrdnZLeMzfknL0ufbE8N
|
|
|
615
616
|
langchain_agentx/tools/skill/hook_registration.py,sha256=2V5srDxBFFnMVEvVbw1Rj39qcij4AtWPzgvL7--D72c,3739
|
|
616
617
|
langchain_agentx/tools/skill/loader.py,sha256=E0P90vvja5thi-1nPT4OkWtOniBkISvumA5agV150XU,10086
|
|
617
618
|
langchain_agentx/tools/skill/models.py,sha256=oo6hsDQtZth_xMetL38NzyxOrDQNzurmTgH8b55VV10,3144
|
|
618
|
-
langchain_agentx/tools/skill/multi_source_repository.py,sha256=
|
|
619
|
+
langchain_agentx/tools/skill/multi_source_repository.py,sha256=P1IRodDmnB1hNTqBcsbWvCFqesCSdE2mW-zhxee8dyE,3370
|
|
619
620
|
langchain_agentx/tools/skill/paths_matcher.py,sha256=J4SOBThray_72mrYiQUfJ5Icu6TPwhctFLugXvMIEkI,3487
|
|
620
621
|
langchain_agentx/tools/skill/plugin_loader.py,sha256=qT0VcIWkKFmRGGH8-e_f4y1XIpjY71TVbnZwnu2M9_M,1191
|
|
621
622
|
langchain_agentx/tools/skill/policy.py,sha256=SFlW627gyo_-wyIppAqoytBmrLPlSGw-mbR00gjOJQY,1541
|
|
622
623
|
langchain_agentx/tools/skill/prompt.py,sha256=tvSqvCGroVdloveiWIYxIPQmpESqzPAs5ZJztvHBdOI,1782
|
|
623
624
|
langchain_agentx/tools/skill/registry_permission.py,sha256=ns_S6IBOdacrZcgHzCOj9rjlPOIecavF5hW-pHmalcY,3258
|
|
624
|
-
langchain_agentx/tools/skill/repository_factory.py,sha256=
|
|
625
|
-
langchain_agentx/tools/skill/source_paths.py,sha256=
|
|
625
|
+
langchain_agentx/tools/skill/repository_factory.py,sha256=aFYIw65ctz1sLfEmn8E3YfpgTvTc1YI9XkueWpeiXtA,1359
|
|
626
|
+
langchain_agentx/tools/skill/source_paths.py,sha256=x-WHorUgt2xyI5hcvALT2e6Ra-HDpKgIv7plQezRkws,5324
|
|
626
627
|
langchain_agentx/tools/skill/tool.py,sha256=vhiwjeyST9BhsMZmKk8-Vej3JId0AxTPrQ5ebFvguG4,19711
|
|
627
628
|
langchain_agentx/tools/skill/bundled/__init__.py,sha256=gaCH_mROpv0lIgLjin1VIFu-8hbP2nY1PHoF7vZt2Ns,4516
|
|
628
629
|
langchain_agentx/tools/skill/bundled/prompts.py,sha256=gcaTnNRlWfHNPgO3EfZxU9vMYbOapMrAduvgaVc-_Fc,4838
|
|
@@ -760,14 +761,14 @@ langchain_agentx/workspace/path_canonical.py,sha256=Z_mmDJQMP4WmBhjBpRUGKGg7XCsK
|
|
|
760
761
|
langchain_agentx/workspace/path_key_normalizer.py,sha256=ICk9uGiiNFH-zaXJG3_vx91uOpgWmgOPIjJoSYevKOc,1113
|
|
761
762
|
langchain_agentx/workspace/plans.py,sha256=wo0ogpErnmwBrNwsHHCzARnIfHM7xnhpAMiN39SjCCI,11813
|
|
762
763
|
langchain_agentx/workspace/project_key.py,sha256=iA_C6oLM8ebWX6C4GXpJp-SO_n6O1heNcyNebC513QA,2808
|
|
763
|
-
langchain_agentx/workspace/project_memory_layout.py,sha256=
|
|
764
|
+
langchain_agentx/workspace/project_memory_layout.py,sha256=F-PlZpOCnXQGh24LF8nINgjkLmA6yTJ6tcdWzEzxL0U,8092
|
|
764
765
|
langchain_agentx/workspace/resolver.py,sha256=stWBxa-jkNO2nZKEJr89Qb4805FcEFm76NpybWb-LYs,6444
|
|
765
766
|
langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXOUBGr1Gjnl8CfCoTIuYc,7558
|
|
766
767
|
langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
|
|
767
768
|
langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
|
|
768
769
|
langchain_agentx/workspace/view.py,sha256=PGasqTaqhlD03SXXazHuw4RHfV681AIdlsYqL70pEjc,4774
|
|
769
|
-
langchain_agentx_python-2.3.
|
|
770
|
-
langchain_agentx_python-2.3.
|
|
771
|
-
langchain_agentx_python-2.3.
|
|
772
|
-
langchain_agentx_python-2.3.
|
|
773
|
-
langchain_agentx_python-2.3.
|
|
770
|
+
langchain_agentx_python-2.3.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
771
|
+
langchain_agentx_python-2.3.4.dist-info/METADATA,sha256=zMKaBp2iVa62_ASW-y0xHpoqDFuMu64NDF414ESG2XA,23942
|
|
772
|
+
langchain_agentx_python-2.3.4.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
|
|
773
|
+
langchain_agentx_python-2.3.4.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
|
|
774
|
+
langchain_agentx_python-2.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{langchain_agentx_python-2.3.2.dist-info → langchain_agentx_python-2.3.4.dist-info}/top_level.txt
RENAMED
|
File without changes
|