langchain-agentx-python 1.2.2__py3-none-any.whl → 1.2.3__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/loop_config.py +2 -0
- langchain_agentx/loop/config/thinking_config.py +105 -0
- langchain_agentx/loop/config/ui_display_context.py +58 -0
- langchain_agentx/loop/graph/factory.py +1 -0
- langchain_agentx/loop/model/model_node.py +36 -2
- langchain_agentx/loop/subagent/context.py +11 -1
- langchain_agentx/loop/subagent/display_projector.py +45 -0
- langchain_agentx/loop/subagent/graph.py +4 -1
- langchain_agentx/loop/subagent/orchestrator.py +49 -1
- langchain_agentx/loop/subagent/progress.py +64 -5
- langchain_agentx/loop/subagent/reasoning_extractor.py +42 -0
- langchain_agentx/loop/subagent/runner.py +20 -1
- langchain_agentx/provider/compatible_chat_openai.py +3 -0
- langchain_agentx/provider/semantics/profile.py +46 -2
- langchain_agentx/tool_runtime/adapter.py +22 -0
- langchain_agentx/tool_runtime/models.py +7 -0
- langchain_agentx/tools/agent/tool.py +20 -4
- {langchain_agentx_python-1.2.2.dist-info → langchain_agentx_python-1.2.3.dist-info}/METADATA +1 -1
- {langchain_agentx_python-1.2.2.dist-info → langchain_agentx_python-1.2.3.dist-info}/RECORD +23 -19
- {langchain_agentx_python-1.2.2.dist-info → langchain_agentx_python-1.2.3.dist-info}/LICENSE +0 -0
- {langchain_agentx_python-1.2.2.dist-info → langchain_agentx_python-1.2.3.dist-info}/WHEEL +0 -0
- {langchain_agentx_python-1.2.2.dist-info → langchain_agentx_python-1.2.3.dist-info}/top_level.txt +0 -0
langchain_agentx/__init__.py
CHANGED
|
@@ -43,11 +43,13 @@ class PromptConfig:
|
|
|
43
43
|
- QueryParams.systemPrompt → system
|
|
44
44
|
- options.customSystemPrompt → override_system
|
|
45
45
|
- options.appendSystemPrompt → append_system
|
|
46
|
+
- options.thinkingConfig → thinking_enabled
|
|
46
47
|
"""
|
|
47
48
|
system: str | SystemMessage | None = None
|
|
48
49
|
override_system: str | None = None
|
|
49
50
|
append_system: str | None = None
|
|
50
51
|
dynamic_sections: tuple[SystemPromptSection, ...] = ()
|
|
52
|
+
thinking_enabled: bool = True # 是否启用 thinking(对齐 CC thinkingConfig)
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
@dataclass(frozen=True)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""loop/config/thinking_config.py — Thinking 生成配置
|
|
2
|
+
|
|
3
|
+
职责:
|
|
4
|
+
定义 ThinkingConfig 数据类,根据 subagent mode (plain/fork/coordinate)
|
|
5
|
+
决定是否启用 thinking 功能。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
SubagentOrchestrator → ThinkingConfig.from_subagent_mode() →
|
|
9
|
+
SubagentContext.thinking_enabled → AgentLoopConfig
|
|
10
|
+
|
|
11
|
+
当前裁剪范围:
|
|
12
|
+
v1 基础实现:mode 驱动启用/禁用;预留父配置覆盖路径。
|
|
13
|
+
|
|
14
|
+
CC 对齐:
|
|
15
|
+
- runAgent.ts:682-683: plain mode 默认 { type: 'disabled' }
|
|
16
|
+
- fork mode 继承父配置或默认启用
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from typing import Any, Literal
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class ThinkingConfig:
|
|
27
|
+
"""Thinking 生成配置(对齐 CC thinkingConfig)。
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
enabled: 是否启用 thinking
|
|
31
|
+
type: Thinking 类型(预留,对齐 CC { type: 'enabled' | 'disabled' })
|
|
32
|
+
budget_tokens: Thinking token 预算(预留)
|
|
33
|
+
"""
|
|
34
|
+
enabled: bool = True
|
|
35
|
+
type: Literal["enabled", "disabled", "auto"] = "auto"
|
|
36
|
+
budget_tokens: int | None = None
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def disabled(cls) -> "ThinkingConfig":
|
|
40
|
+
"""返回禁用配置(对齐 CC { type: 'disabled' })。"""
|
|
41
|
+
return cls(enabled=False, type="disabled")
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def enabled_config(cls) -> "ThinkingConfig":
|
|
45
|
+
"""返回启用配置(对齐 CC { type: 'enabled' })。"""
|
|
46
|
+
return cls(enabled=True, type="enabled")
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def from_subagent_mode(
|
|
50
|
+
cls,
|
|
51
|
+
mode: str,
|
|
52
|
+
parent_thinking_config: Any | None = None,
|
|
53
|
+
*,
|
|
54
|
+
parent_thinking_enabled: bool | None = None,
|
|
55
|
+
) -> "ThinkingConfig":
|
|
56
|
+
"""根据 subagent mode 决定是否启用 thinking。
|
|
57
|
+
|
|
58
|
+
CC 对齐规则:
|
|
59
|
+
- plain mode: 禁用 thinking(除非父配置显式启用)
|
|
60
|
+
- fork mode: 继承父 thinking_enabled / thinkingConfig(对齐 runAgent useExactTools)
|
|
61
|
+
- coordinate mode: 同 fork,继承父配置
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
mode: Subagent 模式 ("plain" | "fork" | "coordinate")
|
|
65
|
+
parent_thinking_config: 父 ThinkingConfig 或 dict(优先于 mode 规则)
|
|
66
|
+
parent_thinking_enabled: 父 ToolExecutionContext.thinking_enabled(fork/coordinate 继承)
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
ThinkingConfig 实例
|
|
70
|
+
"""
|
|
71
|
+
# 优先级 1:父显式 thinkingConfig
|
|
72
|
+
if parent_thinking_config is not None:
|
|
73
|
+
if isinstance(parent_thinking_config, dict):
|
|
74
|
+
parent_enabled = parent_thinking_config.get("enabled", True)
|
|
75
|
+
parent_type = parent_thinking_config.get("type", "auto")
|
|
76
|
+
return cls(
|
|
77
|
+
enabled=parent_enabled,
|
|
78
|
+
type=parent_type if parent_type in ("enabled", "disabled", "auto") else "auto",
|
|
79
|
+
)
|
|
80
|
+
elif isinstance(parent_thinking_config, ThinkingConfig):
|
|
81
|
+
return parent_thinking_config
|
|
82
|
+
|
|
83
|
+
# 优先级 2:mode 规则
|
|
84
|
+
if mode == "plain":
|
|
85
|
+
return cls.disabled()
|
|
86
|
+
|
|
87
|
+
# fork / coordinate:继承父 loop thinking_enabled(CC fork inherit)
|
|
88
|
+
if parent_thinking_enabled is not None:
|
|
89
|
+
enabled = bool(parent_thinking_enabled)
|
|
90
|
+
return cls(
|
|
91
|
+
enabled=enabled,
|
|
92
|
+
type="enabled" if enabled else "disabled",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return cls.enabled_config()
|
|
96
|
+
|
|
97
|
+
def to_dict(self) -> dict[str, Any]:
|
|
98
|
+
"""转换为字典(传递给 Provider)。"""
|
|
99
|
+
return {
|
|
100
|
+
"enabled": self.enabled,
|
|
101
|
+
"type": self.type,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
__all__ = ["ThinkingConfig"]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""loop/config/ui_display_context.py — UI 展示意图(CLI → SDK seam)
|
|
2
|
+
|
|
3
|
+
职责:
|
|
4
|
+
定义宿主(CLI)注入的 transcript / verbose / thinking 展示意图;
|
|
5
|
+
供 ToolExecutionContext 与 SubagentOrchestrator 读取。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
RunnableConfig.configurable["ui_display_context"] → LangChainAdapter → ToolExecutionContext
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅数据契约与解析;不做 TUI 渲染(langchain_agentx_cli 职责)。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import Any, Mapping
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class UiDisplayContext:
|
|
22
|
+
"""宿主 UI 展示意图(对齐 CC verbose + isTranscriptMode)。"""
|
|
23
|
+
|
|
24
|
+
is_transcript_mode: bool = False
|
|
25
|
+
verbose: bool = False
|
|
26
|
+
thinking_enabled: bool | None = None
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_mapping(cls, raw: Mapping[str, Any] | None) -> UiDisplayContext | None:
|
|
30
|
+
if not raw or not isinstance(raw, Mapping):
|
|
31
|
+
return None
|
|
32
|
+
is_transcript = bool(raw.get("is_transcript_mode", False))
|
|
33
|
+
verbose = bool(raw.get("verbose", False))
|
|
34
|
+
thinking_raw = raw.get("thinking_enabled")
|
|
35
|
+
thinking_enabled = None if thinking_raw is None else bool(thinking_raw)
|
|
36
|
+
return cls(
|
|
37
|
+
is_transcript_mode=is_transcript,
|
|
38
|
+
verbose=verbose,
|
|
39
|
+
thinking_enabled=thinking_enabled,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_configurable(cls, configurable: Mapping[str, Any] | None) -> UiDisplayContext | None:
|
|
44
|
+
if not configurable:
|
|
45
|
+
return None
|
|
46
|
+
raw = configurable.get("ui_display_context")
|
|
47
|
+
if isinstance(raw, UiDisplayContext):
|
|
48
|
+
return raw
|
|
49
|
+
if isinstance(raw, Mapping):
|
|
50
|
+
return cls.from_mapping(raw)
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
def should_show_thinking_full(self) -> bool:
|
|
54
|
+
"""对齐 CC Message.tsx:verbose || isTranscriptMode 时展示完整 thinking。"""
|
|
55
|
+
return self.verbose or self.is_transcript_mode
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
__all__ = ["UiDisplayContext"]
|
|
@@ -302,6 +302,7 @@ def _flatten_config_for_runtime(config: AgentLoopConfig) -> _FlatLoopConfig:
|
|
|
302
302
|
model_profile_registry=config.model_profile_registry,
|
|
303
303
|
compact_buffer_tokens=config.compact_buffer_tokens,
|
|
304
304
|
max_output_tokens_reserved=config.max_output_tokens_reserved,
|
|
305
|
+
thinking_enabled=config.prompt.thinking_enabled,
|
|
305
306
|
)
|
|
306
307
|
|
|
307
308
|
|
|
@@ -447,6 +447,38 @@ class ModelNode(Runnable):
|
|
|
447
447
|
store = (config.get("configurable", {}) or {}).get("session_store")
|
|
448
448
|
return get_effort_override(store)
|
|
449
449
|
|
|
450
|
+
@staticmethod
|
|
451
|
+
def _get_thinking_enabled_from_config(config: RunnableConfig | None) -> bool | None:
|
|
452
|
+
"""从 runtime config 读取 thinking_enabled intent(bool,非 vendor kwargs)。"""
|
|
453
|
+
if not isinstance(config, dict):
|
|
454
|
+
return None
|
|
455
|
+
configurable = config.get("configurable", {}) or {}
|
|
456
|
+
loop_config = configurable.get("loop_config")
|
|
457
|
+
if loop_config is not None and hasattr(loop_config, "get"):
|
|
458
|
+
return bool(loop_config.get("thinking_enabled", True))
|
|
459
|
+
return None
|
|
460
|
+
|
|
461
|
+
@staticmethod
|
|
462
|
+
def _thinking_invoke_kwargs(model: Any, config: RunnableConfig | None) -> dict[str, Any]:
|
|
463
|
+
"""按 provider family 决定是否在 invoke 时传入 Claude thinking kwargs。
|
|
464
|
+
|
|
465
|
+
对实际 invoke 目标做 unwrap(Corrector / RunnableBinding),避免外层 wrapper
|
|
466
|
+
导致 family 误判从而跳过 thinking。
|
|
467
|
+
"""
|
|
468
|
+
thinking_enabled = ModelNode._get_thinking_enabled_from_config(config)
|
|
469
|
+
if thinking_enabled is None:
|
|
470
|
+
return {}
|
|
471
|
+
from langchain_agentx.provider.semantics.profile import (
|
|
472
|
+
get_default_profile_resolver,
|
|
473
|
+
unwrap_chat_model_for_profile,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
chat_model = unwrap_chat_model_for_profile(model)
|
|
477
|
+
profile = get_default_profile_resolver().resolve_from_model_instance(chat_model)
|
|
478
|
+
if profile.family != "anthropic":
|
|
479
|
+
return {}
|
|
480
|
+
return {"thinking": {"type": "enabled" if thinking_enabled else "disabled"}}
|
|
481
|
+
|
|
450
482
|
# ===== 模型调用 =====
|
|
451
483
|
|
|
452
484
|
def _invoke_bound(self, req: Any, *, config: RunnableConfig | None = None) -> Any:
|
|
@@ -465,7 +497,8 @@ class ModelNode(Runnable):
|
|
|
465
497
|
)
|
|
466
498
|
model_ = self._bind_llm_callback_handler(model_, config)
|
|
467
499
|
messages = _messages_for_model_invoke(req)
|
|
468
|
-
|
|
500
|
+
thinking_kwargs = self._thinking_invoke_kwargs(model_, config)
|
|
501
|
+
output = model_.invoke(messages, config=config, **thinking_kwargs)
|
|
469
502
|
if self._name:
|
|
470
503
|
output.name = self._name
|
|
471
504
|
handled_output = handle_model_output(
|
|
@@ -496,7 +529,8 @@ class ModelNode(Runnable):
|
|
|
496
529
|
)
|
|
497
530
|
model_ = self._bind_llm_callback_handler(model_, config)
|
|
498
531
|
messages = _messages_for_model_invoke(req)
|
|
499
|
-
|
|
532
|
+
thinking_kwargs = self._thinking_invoke_kwargs(model_, config)
|
|
533
|
+
output = await model_.ainvoke(messages, config=config, **thinking_kwargs)
|
|
500
534
|
if self._name:
|
|
501
535
|
output.name = self._name
|
|
502
536
|
handled_output = handle_model_output(
|
|
@@ -27,7 +27,7 @@ import shutil
|
|
|
27
27
|
import uuid
|
|
28
28
|
from collections.abc import Callable
|
|
29
29
|
from dataclasses import dataclass, field
|
|
30
|
-
from typing import Any
|
|
30
|
+
from typing import Any, Literal
|
|
31
31
|
|
|
32
32
|
from langchain_agentx.loop.context.query_source import LoopCompilationContext, QuerySource
|
|
33
33
|
from langchain_agentx.loop.runtime.subagent_execution_paths import (
|
|
@@ -97,6 +97,10 @@ class SubagentContext:
|
|
|
97
97
|
can_use_tool: Callable[[str, dict[str, Any]], bool] | None = None
|
|
98
98
|
register_transcript: bool = True
|
|
99
99
|
|
|
100
|
+
# Thinking 与显示控制字段(对齐 CC thinkingConfig + isTranscriptMode)
|
|
101
|
+
thinking_enabled: bool = True # 是否启用 thinking(默认启用,plain mode 会被禁用)
|
|
102
|
+
display_mode: Literal["default", "transcript"] = "default" # 显示模式(transcript 显示详细内容)
|
|
103
|
+
|
|
100
104
|
|
|
101
105
|
@dataclass
|
|
102
106
|
class SubagentResult:
|
|
@@ -137,6 +141,8 @@ def create_subagent_context(
|
|
|
137
141
|
fork_worktree_parent_cwd: str | None = None,
|
|
138
142
|
fork_worktree_path: str | None = None,
|
|
139
143
|
available_tools: list[Any] | None = None,
|
|
144
|
+
thinking_enabled: bool = True,
|
|
145
|
+
display_mode: Literal["default", "transcript"] = "default",
|
|
140
146
|
) -> SubagentContext:
|
|
141
147
|
"""工厂函数:从父上下文创建子 Agent 上下文
|
|
142
148
|
|
|
@@ -158,6 +164,8 @@ def create_subagent_context(
|
|
|
158
164
|
在最终任务 user 消息前插入 CC 等价 worktree 提示(父 cwd 由 ``fork_worktree_parent_cwd`` 提供)。
|
|
159
165
|
fork_worktree_parent_cwd: 父 Agent 工作目录(须在 ``run_with_cwd_override`` 子 cwd 生效前由编排层捕获)。
|
|
160
166
|
fork_worktree_path: 子 Agent 的 worktree 根路径(通常即 ``path_prep.worktree_dir``)。
|
|
167
|
+
thinking_enabled: 是否启用 thinking(由 SubagentOrchestrator 根据 mode 决定)
|
|
168
|
+
display_mode: 显示模式("default" | "transcript")
|
|
161
169
|
|
|
162
170
|
Returns:
|
|
163
171
|
SubagentContext 实例
|
|
@@ -291,6 +299,8 @@ def create_subagent_context(
|
|
|
291
299
|
capabilities=getattr(parent_ctx, "capabilities", None),
|
|
292
300
|
runnable_config=getattr(parent_ctx, "runnable_config", None),
|
|
293
301
|
available_tools=list(available_tools or []),
|
|
302
|
+
thinking_enabled=thinking_enabled,
|
|
303
|
+
display_mode=display_mode,
|
|
294
304
|
)
|
|
295
305
|
|
|
296
306
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""loop/subagent/display_projector.py — Subagent execution_trace → UI 投影
|
|
2
|
+
|
|
3
|
+
职责:
|
|
4
|
+
将 SubagentExecutionTrace 投影为 present_for_display 的 tool_calls 列表;
|
|
5
|
+
default 摘要 / transcript 全量由 display_mode 决定(trace 对象内仍全量)。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
AgentRuntimeTool.present_for_display → SubagentDisplayProjector
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅 dict 投影;不渲染 TUI。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any, Literal
|
|
17
|
+
|
|
18
|
+
from langchain_agentx.loop.subagent.progress import SubagentExecutionTrace
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SubagentDisplayProjector:
|
|
22
|
+
"""execution_trace → present_for_display 字段投影。"""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def project_tool_calls(
|
|
26
|
+
trace: SubagentExecutionTrace,
|
|
27
|
+
*,
|
|
28
|
+
display_mode: Literal["default", "transcript"] | None = None,
|
|
29
|
+
) -> list[dict[str, Any]]:
|
|
30
|
+
mode = display_mode if display_mode is not None else trace.display_mode
|
|
31
|
+
return trace.to_dict_list(display_mode=mode)
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def resolve_display_mode(
|
|
35
|
+
trace: SubagentExecutionTrace | None,
|
|
36
|
+
ctx_display_mode: Literal["default", "transcript"] | None = None,
|
|
37
|
+
) -> Literal["default", "transcript"]:
|
|
38
|
+
if ctx_display_mode in ("default", "transcript"):
|
|
39
|
+
return ctx_display_mode
|
|
40
|
+
if trace is not None:
|
|
41
|
+
return trace.display_mode
|
|
42
|
+
return "default"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = ["SubagentDisplayProjector"]
|
|
@@ -65,7 +65,10 @@ def create_subagent_graph(
|
|
|
65
65
|
# Phase 3: config-first 模式
|
|
66
66
|
config = AgentLoopConfig(
|
|
67
67
|
session_id=session_id,
|
|
68
|
-
prompt=PromptConfig(
|
|
68
|
+
prompt=PromptConfig(
|
|
69
|
+
system=ctx.system_prompt or None,
|
|
70
|
+
thinking_enabled=ctx.thinking_enabled,
|
|
71
|
+
),
|
|
69
72
|
workspace=AgentStateConfig(
|
|
70
73
|
workspace_root=ctx.workspace_root or None,
|
|
71
74
|
agent_home_segment=ctx.agent_home_segment,
|
|
@@ -14,7 +14,7 @@ from __future__ import annotations
|
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
16
|
import uuid
|
|
17
|
-
from typing import TYPE_CHECKING, Any
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
18
18
|
|
|
19
19
|
from langchain_agentx.loop.runtime import RuntimeContextFactory, SubagentContextOverrides
|
|
20
20
|
from langchain_agentx.loop.runtime.subagent_execution_paths import (
|
|
@@ -41,6 +41,7 @@ from langchain_agentx.loop.subagent.runner import (
|
|
|
41
41
|
from langchain_agentx.loop.subagent.async_runner import register_background_agent
|
|
42
42
|
from langchain_agentx.observability.logging import ObservabilityLoggerAdapter, build_log_context
|
|
43
43
|
from langchain_agentx.workspace import resolve_agent_state_config
|
|
44
|
+
from langchain_agentx.loop.config.thinking_config import ThinkingConfig
|
|
44
45
|
|
|
45
46
|
if TYPE_CHECKING:
|
|
46
47
|
from langchain_agentx.loop.runtime import AgentLoopConfig
|
|
@@ -71,6 +72,39 @@ class SubagentOrchestrator:
|
|
|
71
72
|
self._mode = mode
|
|
72
73
|
self._background_output_dir = background_output_dir
|
|
73
74
|
|
|
75
|
+
def _resolve_thinking_enabled(
|
|
76
|
+
self,
|
|
77
|
+
parent_ctx: "ToolExecutionContext",
|
|
78
|
+
) -> bool:
|
|
79
|
+
"""解析 subagent 的 thinking 启用状态(对齐 CC thinkingConfig)。
|
|
80
|
+
|
|
81
|
+
CC 对齐规则:
|
|
82
|
+
- plain mode: 禁用 thinking({ type: 'disabled' })
|
|
83
|
+
- fork mode: 继承父配置或默认启用
|
|
84
|
+
- coordinate mode: 继承父配置或默认启用
|
|
85
|
+
"""
|
|
86
|
+
# 从父上下文获取 thinking 配置(如果有)
|
|
87
|
+
parent_thinking_config = getattr(parent_ctx, "thinking_config", None)
|
|
88
|
+
parent_thinking_enabled = getattr(parent_ctx, "thinking_enabled", None)
|
|
89
|
+
config = ThinkingConfig.from_subagent_mode(
|
|
90
|
+
self._mode,
|
|
91
|
+
parent_thinking_config,
|
|
92
|
+
parent_thinking_enabled=parent_thinking_enabled,
|
|
93
|
+
)
|
|
94
|
+
return config.enabled
|
|
95
|
+
|
|
96
|
+
def _resolve_display_mode(
|
|
97
|
+
self,
|
|
98
|
+
parent_ctx: "ToolExecutionContext",
|
|
99
|
+
) -> Literal["default", "transcript"]:
|
|
100
|
+
"""解析显示模式(对齐 CC isTranscriptMode)。
|
|
101
|
+
|
|
102
|
+
从父上下文读取 transcript 模式标志;默认为 default。
|
|
103
|
+
"""
|
|
104
|
+
if getattr(parent_ctx, "is_transcript_mode", False):
|
|
105
|
+
return "transcript"
|
|
106
|
+
return "default"
|
|
107
|
+
|
|
74
108
|
def invoke_subagent(
|
|
75
109
|
self,
|
|
76
110
|
inp: "AgentToolInput",
|
|
@@ -166,6 +200,11 @@ class SubagentOrchestrator:
|
|
|
166
200
|
inject_fork_wt = self._mode == "fork" and prepared.worktree_dir is not None
|
|
167
201
|
with run_with_cwd_override(effective_cwd):
|
|
168
202
|
output_dir = self._resolve_background_output_dir(runtime_ctx)
|
|
203
|
+
|
|
204
|
+
# 解析 thinking_enabled 和 display_mode
|
|
205
|
+
thinking_enabled = self._resolve_thinking_enabled(ctx)
|
|
206
|
+
display_mode = self._resolve_display_mode(ctx)
|
|
207
|
+
|
|
169
208
|
subagent_ctx = create_subagent_context(
|
|
170
209
|
parent_ctx=ctx,
|
|
171
210
|
task=subagent_task,
|
|
@@ -183,6 +222,8 @@ class SubagentOrchestrator:
|
|
|
183
222
|
fork_worktree_parent_cwd=parent_notice_cwd,
|
|
184
223
|
fork_worktree_path=prepared.worktree_dir,
|
|
185
224
|
available_tools=runtime_tools,
|
|
225
|
+
thinking_enabled=thinking_enabled,
|
|
226
|
+
display_mode=display_mode,
|
|
186
227
|
)
|
|
187
228
|
scope = propagate_subagent_tool_scope(
|
|
188
229
|
parent_ctx=ctx,
|
|
@@ -359,6 +400,11 @@ class SubagentOrchestrator:
|
|
|
359
400
|
inject_fork_wt = self._mode == "fork" and prepared.worktree_dir is not None
|
|
360
401
|
with run_with_cwd_override(effective_cwd):
|
|
361
402
|
output_dir = self._resolve_background_output_dir(runtime_ctx)
|
|
403
|
+
|
|
404
|
+
# 解析 thinking_enabled 和 display_mode
|
|
405
|
+
thinking_enabled = self._resolve_thinking_enabled(ctx)
|
|
406
|
+
display_mode = self._resolve_display_mode(ctx)
|
|
407
|
+
|
|
362
408
|
subagent_ctx = create_subagent_context(
|
|
363
409
|
parent_ctx=ctx,
|
|
364
410
|
task=subagent_task,
|
|
@@ -376,6 +422,8 @@ class SubagentOrchestrator:
|
|
|
376
422
|
fork_worktree_parent_cwd=parent_notice_cwd,
|
|
377
423
|
fork_worktree_path=prepared.worktree_dir,
|
|
378
424
|
available_tools=runtime_tools,
|
|
425
|
+
thinking_enabled=thinking_enabled,
|
|
426
|
+
display_mode=display_mode,
|
|
379
427
|
)
|
|
380
428
|
scope = propagate_subagent_tool_scope(
|
|
381
429
|
parent_ctx=ctx,
|
|
@@ -21,6 +21,9 @@ class SubagentToolCall:
|
|
|
21
21
|
error_message: str | None = None
|
|
22
22
|
payload: str | None = None
|
|
23
23
|
meta: dict[str, Any] = field(default_factory=dict)
|
|
24
|
+
# Phase 8: thinking 内容(对齐 CC reasoning_content)
|
|
25
|
+
thinking_content: str | None = None
|
|
26
|
+
display_mode: Literal["default", "transcript"] = "default"
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
@dataclass
|
|
@@ -33,11 +36,47 @@ class SubagentExecutionTrace:
|
|
|
33
36
|
tool_calls: list[SubagentToolCall] = field(default_factory=list)
|
|
34
37
|
total_steps: int = 0
|
|
35
38
|
terminal_reason: str | None = None
|
|
39
|
+
display_mode: Literal["default", "transcript"] = "default" # Phase 8: 显示模式
|
|
40
|
+
_pending_thinking_content: str | None = field(default=None, repr=False)
|
|
36
41
|
|
|
37
42
|
def add_tool_call(self, call: SubagentToolCall) -> None:
|
|
38
43
|
"""添加工具调用记录。"""
|
|
39
44
|
self.tool_calls.append(call)
|
|
40
45
|
self.total_steps = max(self.total_steps, call.step)
|
|
46
|
+
self.apply_pending_thinking(step=call.step)
|
|
47
|
+
|
|
48
|
+
def stage_thinking_content(self, thinking_content: str) -> None:
|
|
49
|
+
"""暂存 model 步 reasoning,待下一 tool_call 或 flush 时写入。"""
|
|
50
|
+
if not thinking_content:
|
|
51
|
+
return
|
|
52
|
+
self._pending_thinking_content = thinking_content
|
|
53
|
+
|
|
54
|
+
def apply_pending_thinking(self, *, step: int) -> None:
|
|
55
|
+
"""将暂存 reasoning 写入指定 step 的 tool_call。"""
|
|
56
|
+
if not self._pending_thinking_content:
|
|
57
|
+
return
|
|
58
|
+
self.add_thinking_content(step=step, thinking_content=self._pending_thinking_content)
|
|
59
|
+
self._pending_thinking_content = None
|
|
60
|
+
|
|
61
|
+
def attach_thinking_to_last_call(self, thinking_content: str) -> None:
|
|
62
|
+
"""将 reasoning 挂到最后一个 tool_call(无后续 tool 的最终 model 步)。"""
|
|
63
|
+
if not thinking_content or not self.tool_calls:
|
|
64
|
+
return
|
|
65
|
+
last = self.tool_calls[-1]
|
|
66
|
+
last.thinking_content = thinking_content
|
|
67
|
+
|
|
68
|
+
def add_thinking_content(self, step: int, thinking_content: str) -> None:
|
|
69
|
+
"""添加 thinking 内容到最近的工具调用(Phase 8)。
|
|
70
|
+
|
|
71
|
+
用于记录模型的 reasoning_content,在 transcript 模式下展示。
|
|
72
|
+
"""
|
|
73
|
+
if not thinking_content:
|
|
74
|
+
return
|
|
75
|
+
# 查找对应的工具调用
|
|
76
|
+
for call in self.tool_calls:
|
|
77
|
+
if call.step == step:
|
|
78
|
+
call.thinking_content = thinking_content
|
|
79
|
+
break
|
|
41
80
|
|
|
42
81
|
def update_last_status(
|
|
43
82
|
self,
|
|
@@ -57,17 +96,37 @@ class SubagentExecutionTrace:
|
|
|
57
96
|
if error_message:
|
|
58
97
|
last.error_message = error_message
|
|
59
98
|
|
|
60
|
-
def to_dict_list(self) -> list[dict[str, Any]]:
|
|
61
|
-
"""转换为字典列表,用于 JSON 序列化。
|
|
99
|
+
def to_dict_list(self, *, display_mode: Literal["default", "transcript"] | None = None) -> list[dict[str, Any]]:
|
|
100
|
+
"""转换为字典列表,用于 JSON 序列化。
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
display_mode: 显示模式
|
|
104
|
+
- None: 使用 trace.display_mode(默认)
|
|
105
|
+
- "default": 仅返回摘要(step, tool_name, summary, status)
|
|
106
|
+
- "transcript": 返回完整信息(含 tool_input, payload, thinking_content)
|
|
107
|
+
"""
|
|
108
|
+
effective_mode = display_mode if display_mode is not None else self.display_mode
|
|
109
|
+
if effective_mode == "transcript":
|
|
110
|
+
return [
|
|
111
|
+
{
|
|
112
|
+
"step": c.step,
|
|
113
|
+
"tool_name": c.tool_name,
|
|
114
|
+
"tool_input": c.tool_input,
|
|
115
|
+
"summary": c.summary,
|
|
116
|
+
"status": c.status,
|
|
117
|
+
"error_message": c.error_message,
|
|
118
|
+
"payload": c.payload,
|
|
119
|
+
"thinking_content": c.thinking_content,
|
|
120
|
+
}
|
|
121
|
+
for c in self.tool_calls
|
|
122
|
+
]
|
|
123
|
+
# default 模式:仅返回摘要
|
|
62
124
|
return [
|
|
63
125
|
{
|
|
64
126
|
"step": c.step,
|
|
65
127
|
"tool_name": c.tool_name,
|
|
66
|
-
"tool_input": c.tool_input,
|
|
67
128
|
"summary": c.summary,
|
|
68
129
|
"status": c.status,
|
|
69
|
-
"error_message": c.error_message,
|
|
70
|
-
"payload": c.payload,
|
|
71
130
|
}
|
|
72
131
|
for c in self.tool_calls
|
|
73
132
|
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""loop/subagent/reasoning_extractor.py — 子 Agent 模型响应 reasoning 抽取
|
|
2
|
+
|
|
3
|
+
职责:
|
|
4
|
+
从 AIMessage 按 provider family 抽取 reasoning_text(Claude thinking / OpenAI reasoning_content)。
|
|
5
|
+
|
|
6
|
+
链路位置:
|
|
7
|
+
runner._handle_chat_model_end_event → SubagentReasoningExtractor → execution_trace
|
|
8
|
+
|
|
9
|
+
当前裁剪范围:
|
|
10
|
+
仅 subagent runner 使用;不做 UI 渲染或 trace 持久化。
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from langchain_agentx.provider.semantics.runtime import (
|
|
18
|
+
create_runtime_from_messages,
|
|
19
|
+
create_runtime_from_model,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SubagentReasoningExtractor:
|
|
24
|
+
"""从 model 响应抽取 reasoning 文本(委托 ProviderSemanticsRuntime)。"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, model: Any | None = None) -> None:
|
|
27
|
+
self._model = model
|
|
28
|
+
|
|
29
|
+
def extract(self, message: Any) -> str:
|
|
30
|
+
if message is None:
|
|
31
|
+
return ""
|
|
32
|
+
if self._model is not None and not isinstance(self._model, str):
|
|
33
|
+
runtime = create_runtime_from_model(self._model)
|
|
34
|
+
else:
|
|
35
|
+
runtime = create_runtime_from_messages([message])
|
|
36
|
+
turn = runtime.normalize_model_end(message)
|
|
37
|
+
if turn is None or not turn.reasoning_text:
|
|
38
|
+
return ""
|
|
39
|
+
return str(turn.reasoning_text).strip()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = ["SubagentReasoningExtractor"]
|
|
@@ -382,10 +382,26 @@ async def _handle_chat_model_end_event(
|
|
|
382
382
|
data: dict[str, Any],
|
|
383
383
|
collected_assistant_messages: list[Any],
|
|
384
384
|
total_steps: int,
|
|
385
|
+
trace: SubagentExecutionTrace | None = None,
|
|
386
|
+
model: Any | None = None,
|
|
385
387
|
) -> int:
|
|
386
388
|
output = data.get("output")
|
|
387
389
|
if output is not None:
|
|
388
390
|
collected_assistant_messages.append(output)
|
|
391
|
+
if trace is not None:
|
|
392
|
+
from langchain_agentx.loop.subagent.reasoning_extractor import (
|
|
393
|
+
SubagentReasoningExtractor,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
reasoning = SubagentReasoningExtractor(model=model).extract(output)
|
|
397
|
+
if reasoning:
|
|
398
|
+
has_tool_calls = bool(getattr(output, "tool_calls", None))
|
|
399
|
+
if has_tool_calls:
|
|
400
|
+
trace.stage_thinking_content(reasoning)
|
|
401
|
+
elif trace.tool_calls:
|
|
402
|
+
trace.attach_thinking_to_last_call(reasoning)
|
|
403
|
+
else:
|
|
404
|
+
trace.stage_thinking_content(reasoning)
|
|
389
405
|
has_tool_calls = bool(output is not None and getattr(output, "tool_calls", None))
|
|
390
406
|
if not has_tool_calls:
|
|
391
407
|
total_steps += 1
|
|
@@ -491,6 +507,8 @@ async def _collect_subagent_events(
|
|
|
491
507
|
data=data,
|
|
492
508
|
collected_assistant_messages=collected_assistant_messages,
|
|
493
509
|
total_steps=total_steps,
|
|
510
|
+
trace=trace,
|
|
511
|
+
model=getattr(ctx, "model", None),
|
|
494
512
|
)
|
|
495
513
|
elif event_name == "on_chain_end" and event.get("name") == "LangGraph":
|
|
496
514
|
terminal_reason, child_run_id, loop_snapshot = _handle_langgraph_end_event(
|
|
@@ -621,7 +639,8 @@ async def run_subagent(
|
|
|
621
639
|
)
|
|
622
640
|
else:
|
|
623
641
|
transcript = NoOpTranscriptManager()
|
|
624
|
-
trace
|
|
642
|
+
# Phase 8: 使用 ctx.display_mode 设置 trace 的显示模式
|
|
643
|
+
trace = SubagentExecutionTrace(display_mode=ctx.display_mode)
|
|
625
644
|
|
|
626
645
|
try:
|
|
627
646
|
graph = create_subagent_graph(ctx=ctx, loader=loader, model=resolved_model)
|
|
@@ -58,8 +58,11 @@ class CompatibleChatOpenAI(ChatOpenAI):
|
|
|
58
58
|
**kwargs: Any,
|
|
59
59
|
) -> dict[str, Any]:
|
|
60
60
|
"""与基类一致,并为 chat/completions 的 assistant 历史补上 ``reasoning_content``。"""
|
|
61
|
+
kwargs.pop("thinking_enabled", None)
|
|
62
|
+
kwargs.pop("thinking", None)
|
|
61
63
|
lc_messages = self._convert_input(input_).to_messages()
|
|
62
64
|
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
|
|
65
|
+
payload.pop("thinking", None)
|
|
63
66
|
serialized = payload.get("messages")
|
|
64
67
|
if not isinstance(serialized, list) or len(serialized) != len(lc_messages):
|
|
65
68
|
return payload
|
|
@@ -28,6 +28,44 @@ if TYPE_CHECKING:
|
|
|
28
28
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
|
+
_MAX_UNWRAP_DEPTH = 8
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def unwrap_chat_model_for_profile(model: Any) -> Any:
|
|
35
|
+
"""剥开 SDK/LangChain 包装,定位用于 family 识别的底层 ChatModel。
|
|
36
|
+
|
|
37
|
+
常见链路:ToolCallDegradationCorrector → RunnableBinding → ChatAnthropic。
|
|
38
|
+
仅对已知 wrapper 类型 peel,避免 Mock 等对象的自动属性误触发。
|
|
39
|
+
"""
|
|
40
|
+
seen: set[int] = set()
|
|
41
|
+
current = model
|
|
42
|
+
for _ in range(_MAX_UNWRAP_DEPTH):
|
|
43
|
+
obj_id = id(current)
|
|
44
|
+
if obj_id in seen:
|
|
45
|
+
break
|
|
46
|
+
seen.add(obj_id)
|
|
47
|
+
|
|
48
|
+
cls = current.__class__
|
|
49
|
+
class_name = f"{cls.__module__}.{cls.__name__}"
|
|
50
|
+
inner = None
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
cls.__name__ == "ToolCallDegradationCorrector"
|
|
54
|
+
or "tool_call_degradation_corrector" in cls.__module__
|
|
55
|
+
):
|
|
56
|
+
candidate = getattr(current, "_llm", None)
|
|
57
|
+
if candidate is not None and candidate is not current:
|
|
58
|
+
inner = candidate
|
|
59
|
+
elif cls.__name__ == "RunnableBinding":
|
|
60
|
+
candidate = getattr(current, "bound", None)
|
|
61
|
+
if candidate is not None and candidate is not current:
|
|
62
|
+
inner = candidate
|
|
63
|
+
|
|
64
|
+
if inner is None:
|
|
65
|
+
break
|
|
66
|
+
current = inner
|
|
67
|
+
return current
|
|
68
|
+
|
|
31
69
|
|
|
32
70
|
class ProviderProfileResolver:
|
|
33
71
|
"""Provider Profile 解析器。
|
|
@@ -54,14 +92,19 @@ class ProviderProfileResolver:
|
|
|
54
92
|
Returns:
|
|
55
93
|
ProviderProfile 实例
|
|
56
94
|
"""
|
|
95
|
+
chat_model = unwrap_chat_model_for_profile(model)
|
|
57
96
|
# 获取模型类名
|
|
58
|
-
model_class_name =
|
|
97
|
+
model_class_name = (
|
|
98
|
+
f"{chat_model.__class__.__module__}.{chat_model.__class__.__name__}"
|
|
99
|
+
)
|
|
59
100
|
|
|
60
101
|
# 识别 family
|
|
61
102
|
family = self._identify_family(model_class_name)
|
|
62
103
|
|
|
63
104
|
# 获取模型名称(用于调试)
|
|
64
|
-
model_name = getattr(
|
|
105
|
+
model_name = getattr(chat_model, "model_name", None) or getattr(
|
|
106
|
+
chat_model, "model", None
|
|
107
|
+
)
|
|
65
108
|
|
|
66
109
|
# 根据 family 构造 capabilities
|
|
67
110
|
capabilities = self._get_capabilities_for_family(family)
|
|
@@ -193,4 +236,5 @@ def get_default_profile_resolver() -> ProviderProfileResolver:
|
|
|
193
236
|
__all__ = [
|
|
194
237
|
"ProviderProfileResolver",
|
|
195
238
|
"get_default_profile_resolver",
|
|
239
|
+
"unwrap_chat_model_for_profile",
|
|
196
240
|
]
|
|
@@ -288,6 +288,25 @@ class LangChainAdapter:
|
|
|
288
288
|
if "network_access_allowed" in configurable:
|
|
289
289
|
network_access_allowed = bool(configurable.get("network_access_allowed"))
|
|
290
290
|
|
|
291
|
+
from langchain_agentx.loop.config.ui_display_context import UiDisplayContext
|
|
292
|
+
|
|
293
|
+
ui_display = UiDisplayContext.from_configurable(configurable)
|
|
294
|
+
is_transcript_mode = bool(ui_display.is_transcript_mode) if ui_display else False
|
|
295
|
+
ui_verbose = bool(ui_display.verbose) if ui_display else False
|
|
296
|
+
|
|
297
|
+
thinking_enabled: bool | None = None
|
|
298
|
+
if ui_display is not None and ui_display.thinking_enabled is not None:
|
|
299
|
+
thinking_enabled = ui_display.thinking_enabled
|
|
300
|
+
elif loop_config is not None:
|
|
301
|
+
if hasattr(loop_config, "get"):
|
|
302
|
+
raw_te = loop_config.get("thinking_enabled")
|
|
303
|
+
if raw_te is not None:
|
|
304
|
+
thinking_enabled = bool(raw_te)
|
|
305
|
+
else:
|
|
306
|
+
prompt_cfg = getattr(loop_config, "prompt", None)
|
|
307
|
+
if prompt_cfg is not None:
|
|
308
|
+
thinking_enabled = getattr(prompt_cfg, "thinking_enabled", None)
|
|
309
|
+
|
|
291
310
|
effective_tool_flags = dict(tool_flags or {})
|
|
292
311
|
if "permission_mode" in configurable:
|
|
293
312
|
effective_tool_flags["permission_mode"] = configurable["permission_mode"]
|
|
@@ -343,6 +362,9 @@ class LangChainAdapter:
|
|
|
343
362
|
capabilities=capabilities,
|
|
344
363
|
network_access_allowed=network_access_allowed,
|
|
345
364
|
runnable_config=runnable_config_to_pass,
|
|
365
|
+
is_transcript_mode=is_transcript_mode,
|
|
366
|
+
thinking_enabled=thinking_enabled,
|
|
367
|
+
ui_verbose=ui_verbose,
|
|
346
368
|
)
|
|
347
369
|
|
|
348
370
|
def apply_pipeline_side_effects_immediate(
|
|
@@ -143,6 +143,13 @@ class ToolExecutionContext:
|
|
|
143
143
|
session_hint_drafts: list[Any] = field(default_factory=list)
|
|
144
144
|
"""defer 模式下由 ToolStateBridge 收集的 SessionHintDraft(单 call 局部)。"""
|
|
145
145
|
|
|
146
|
+
# UI 展示意图(CLI → SDK;对齐 CC verbose / isTranscriptMode)
|
|
147
|
+
is_transcript_mode: bool = False
|
|
148
|
+
thinking_enabled: bool | None = None
|
|
149
|
+
"""父 loop PromptConfig.thinking_enabled;fork subagent 继承用。"""
|
|
150
|
+
ui_verbose: bool = False
|
|
151
|
+
"""宿主 verbose 模式(主 Agent thinking 展示策略,供 CLI 投影层读取)。"""
|
|
152
|
+
|
|
146
153
|
def persist_permission_updates(
|
|
147
154
|
self,
|
|
148
155
|
updates: Sequence["PermissionUpdate"],
|
|
@@ -389,11 +389,14 @@ class AgentRuntimeTool(RuntimeTool):
|
|
|
389
389
|
) -> dict[str, Any] | None:
|
|
390
390
|
"""out-of-band:子 agent 状态摘要与执行轨迹(对位 CC AgentTool UI)。
|
|
391
391
|
|
|
392
|
+
Phase 7/8: 根据 display_mode 过滤内容
|
|
393
|
+
- transcript 模式:返回完整 tool_calls(含 tool_input, payload, thinking_content)
|
|
394
|
+
- default 模式:返回摘要(仅 step, tool_name, summary, status)
|
|
395
|
+
|
|
392
396
|
返回结构包含:
|
|
393
397
|
- subagent_type, status, total_steps, terminal_reason, agent_id, child_session_id
|
|
394
|
-
- tool_calls:
|
|
398
|
+
- tool_calls: 工具调用列表(根据 display_mode 过滤)
|
|
395
399
|
"""
|
|
396
|
-
_ = ctx
|
|
397
400
|
inp = AgentToolInput.model_validate(data)
|
|
398
401
|
out = result if isinstance(result, AgentToolOutput) else AgentToolOutput.model_validate(result)
|
|
399
402
|
|
|
@@ -407,10 +410,23 @@ class AgentRuntimeTool(RuntimeTool):
|
|
|
407
410
|
**_agent_termination_meta(out),
|
|
408
411
|
}
|
|
409
412
|
|
|
410
|
-
# 提取 execution_trace 中的 tool_calls
|
|
413
|
+
# Phase 7/8: 提取 execution_trace 中的 tool_calls,由 DisplayProjector 按 display_mode 投影
|
|
411
414
|
if out.execution_trace is not None:
|
|
415
|
+
from langchain_agentx.loop.subagent.display_projector import SubagentDisplayProjector
|
|
412
416
|
from langchain_agentx.loop.subagent.progress import SubagentExecutionTrace
|
|
417
|
+
|
|
413
418
|
if isinstance(out.execution_trace, SubagentExecutionTrace):
|
|
414
|
-
|
|
419
|
+
ctx_mode = getattr(ctx, "is_transcript_mode", False)
|
|
420
|
+
ctx_display: Literal["default", "transcript"] = (
|
|
421
|
+
"transcript" if ctx_mode else "default"
|
|
422
|
+
)
|
|
423
|
+
display_mode = SubagentDisplayProjector.resolve_display_mode(
|
|
424
|
+
out.execution_trace,
|
|
425
|
+
ctx_display_mode=ctx_display,
|
|
426
|
+
)
|
|
427
|
+
display_data["tool_calls"] = SubagentDisplayProjector.project_tool_calls(
|
|
428
|
+
out.execution_trace,
|
|
429
|
+
display_mode=display_mode,
|
|
430
|
+
)
|
|
415
431
|
|
|
416
432
|
return display_data
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
langchain_agentx/__init__.py,sha256=
|
|
1
|
+
langchain_agentx/__init__.py,sha256=h3W5rAgNSkziLvbazQZL4kQJHc87DVInXdfhUid5RsY,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
|
|
@@ -30,12 +30,14 @@ langchain_agentx/loop/loop_abort.py,sha256=hcAWC4pVkGgMZx4qlzTzP6rKpPMn5so4YXQ7a
|
|
|
30
30
|
langchain_agentx/loop/config/__init__.py,sha256=WdRQVXuvGU4myaGOZ8guFFvUb66jfsAIp9DLANJGieg,1083
|
|
31
31
|
langchain_agentx/loop/config/agent_config.py,sha256=tOnAHFor6dkle3FvUOVx0YsmiYVTH11UrWSyTmOLV0c,3100
|
|
32
32
|
langchain_agentx/loop/config/interaction_profile.py,sha256=UWrlI3aTWjYRq1mDeXL-xL-iCrQU-bXDXhETP6q9icY,10020
|
|
33
|
-
langchain_agentx/loop/config/loop_config.py,sha256=
|
|
33
|
+
langchain_agentx/loop/config/loop_config.py,sha256=fiQ68EIqGY9NQMdCjCzkBcFWGGy_kutGOjFekl2mxno,14270
|
|
34
34
|
langchain_agentx/loop/config/loop_runtime_overlay.py,sha256=70FDSWs9b-Y5tZ8O3D_7PricJa8mxpy0NCDpA6wbxCo,2242
|
|
35
35
|
langchain_agentx/loop/config/model_context_resolver.py,sha256=o9RR0Vnu07TD4Y3V8jk5V1NfkMcPcLjE8uaaLOe22Qo,4067
|
|
36
36
|
langchain_agentx/loop/config/runtime_settings.py,sha256=uKkwyxgx5u34qbjP1DFrIRt0EygRCtsH8tMN0kWA0Yo,1551
|
|
37
|
+
langchain_agentx/loop/config/thinking_config.py,sha256=V33oksp7XCzPKZ3gef83I2CSOlc8HSdFJQNv5sI832k,3649
|
|
37
38
|
langchain_agentx/loop/config/token_estimator.py,sha256=2-Rp2Bdc5a-60D1nm88z4-sNzS25nv8hkKIfaBqE78E,4883
|
|
38
39
|
langchain_agentx/loop/config/tool_execution_engine_resolver.py,sha256=6UtW_P_TmVSiz8-TmeNCPPTt73xEQye-csmXTtBZsnw,1550
|
|
40
|
+
langchain_agentx/loop/config/ui_display_context.py,sha256=DQMJsc-Q3_CLJIjg83dAb1CQkFMqiqMnEeQyawJGW4c,2003
|
|
39
41
|
langchain_agentx/loop/context/__init__.py,sha256=RXVTFjHcH6_8q1x13Qk9ZP8RVbqZoyhlY37MXigC-DY,2240
|
|
40
42
|
langchain_agentx/loop/context/blocking_guard.py,sha256=Rmt5qZdE8Zf0bt-Q6WwMSfG869772PE8w1mNPvBQRCQ,3789
|
|
41
43
|
langchain_agentx/loop/context/compaction_service.py,sha256=MwAPPaT31XbzgwqpuknGBZlYBRKxwyhWXDifEnBgh2E,3725
|
|
@@ -62,7 +64,7 @@ langchain_agentx/loop/exit/terminal.py,sha256=LUqgkJYo0ySWSR-CXwaN8xAY4pjoV0n3Nv
|
|
|
62
64
|
langchain_agentx/loop/exit/withheld_error.py,sha256=LfX3j7Rgd9VoMzlz7GOs9TPuRe7RXr_Gxweyotx3tqQ,1846
|
|
63
65
|
langchain_agentx/loop/graph/__init__.py,sha256=9pUUqn7G87n3lV45iYIU24j0wcTHyCD--SSdtv3urh4,136
|
|
64
66
|
langchain_agentx/loop/graph/builtin_loop_control.py,sha256=jagBvSo8fj6Yjl95rkp5A0W8RgyVf4P-EXMmGweURl8,7184
|
|
65
|
-
langchain_agentx/loop/graph/factory.py,sha256=
|
|
67
|
+
langchain_agentx/loop/graph/factory.py,sha256=xSRLX6uV1Es_nYTm-RtVs29D3SUYaj8jkudbT1_U8-k,91560
|
|
66
68
|
langchain_agentx/loop/graph/graph_edges.py,sha256=YisRMKFh3-vEJmrC6JF2RGlJgltf6TtdvApv5SoywSs,43356
|
|
67
69
|
langchain_agentx/loop/graph/reactive_compact_node.py,sha256=AN31reohf25zRiqdPTmFCIQ1N6VwKn1oIkRkRuGAp4k,8725
|
|
68
70
|
langchain_agentx/loop/graph/runtime_tools_node.py,sha256=5iBPvHl2hOdfnRZrwtnoD4J_SzEuQKC6VQUJyPuthgA,5089
|
|
@@ -84,7 +86,7 @@ langchain_agentx/loop/hook/executors/prompt.py,sha256=oYwOk-acH-dopzfzNUEjpPdXGk
|
|
|
84
86
|
langchain_agentx/loop/injection/__init__.py,sha256=rEiAXYqdr_Ry8mhHBu6xZKLypnyJOxcxNe6sJns1rnc,362
|
|
85
87
|
langchain_agentx/loop/injection/dedup.py,sha256=5wv-Tb8HUean9C82VazVIkTkQdIZi9gewMABukiclpI,2548
|
|
86
88
|
langchain_agentx/loop/model/__init__.py,sha256=8wOiwa2Yvh7QdR6ilhk4faI7_iwNT6QSEFgewJNhhOw,57
|
|
87
|
-
langchain_agentx/loop/model/model_node.py,sha256=
|
|
89
|
+
langchain_agentx/loop/model/model_node.py,sha256=4NcjP03CioxJA2wVuUREYN5s9ierLPj8RfdsaI0-vkQ,35844
|
|
88
90
|
langchain_agentx/loop/model/model_nodes.py,sha256=SqcPXJu4krh6IkrR1PHOMtaZPPZ0F09PY3_dtYtBhIk,29493
|
|
89
91
|
langchain_agentx/loop/model/orphan_tool_results.py,sha256=TK8zDO63NYj0f0wDlREYN0qN8lpqnWRv9TMGDMQ3Z1Y,2493
|
|
90
92
|
langchain_agentx/loop/model/retrier.py,sha256=9Zfcu51QsErI2GbsLxKCz_msoH6cdc4ReQyxC1qUW9k,11086
|
|
@@ -119,16 +121,18 @@ langchain_agentx/loop/stream/transcript_gate.py,sha256=4AH3O0l8OrY_GQdqf-TOKOVS3
|
|
|
119
121
|
langchain_agentx/loop/subagent/__init__.py,sha256=MWa1IWt7eSdJhTnPCoIpvk5NMsPN6hknghqhX5kqwnY,2477
|
|
120
122
|
langchain_agentx/loop/subagent/active_run_registry.py,sha256=KurhAMDTBnRkdxNidaMUynCTRzdDy67Tu2NwJt3UC_g,4310
|
|
121
123
|
langchain_agentx/loop/subagent/async_runner.py,sha256=eElKINVzPr6zFe9a4W_Vg1NckBCMdkJn2MD9ez82mCQ,7605
|
|
122
|
-
langchain_agentx/loop/subagent/context.py,sha256=
|
|
124
|
+
langchain_agentx/loop/subagent/context.py,sha256=Fanqracs0D29JjnksUkHI-7afXwswlTcxCyObck9frc,20848
|
|
125
|
+
langchain_agentx/loop/subagent/display_projector.py,sha256=KZWCiKd-GvVY-ewCdkE4sDmBrIMmx4OICWL8XdxeBp0,1442
|
|
123
126
|
langchain_agentx/loop/subagent/fork_boilerplate.py,sha256=2Uh3cwegL7V-DvtLIxjX7ik5uW5mXfY2NBzP-biREso,5005
|
|
124
127
|
langchain_agentx/loop/subagent/fork_ports.py,sha256=hZATCvlYOxNVZtR3PoOVbI1GkA0KhHEyux6lUIjG5_4,1323
|
|
125
128
|
langchain_agentx/loop/subagent/fork_worktree_notice.py,sha256=WKAJVxvRMJfbzRe_07Mgrm_zQIQCYx4RwsUnWSSOnkg,1389
|
|
126
129
|
langchain_agentx/loop/subagent/forked_runner.py,sha256=ZEwkNlbwp1-PlJB5COHMKUu_w8EnSMLRc8s0umZ4NuM,6667
|
|
127
|
-
langchain_agentx/loop/subagent/graph.py,sha256=
|
|
128
|
-
langchain_agentx/loop/subagent/orchestrator.py,sha256
|
|
129
|
-
langchain_agentx/loop/subagent/progress.py,sha256=
|
|
130
|
+
langchain_agentx/loop/subagent/graph.py,sha256=SoreAH0Co4daKkc0YKnzZEhNw3VzpCgAkZM1BEI5hmo,3891
|
|
131
|
+
langchain_agentx/loop/subagent/orchestrator.py,sha256=tFcFTS8OdRw4H5XOBLQgHaV6-llFjve-BH2_FTh1CEY,30563
|
|
132
|
+
langchain_agentx/loop/subagent/progress.py,sha256=5SJD8XVA5emizGaRhD192Xc9D3nYb3BlHdnVm6g19Sc,6180
|
|
130
133
|
langchain_agentx/loop/subagent/prompt.py,sha256=p7RgzibNfEJdfX8nBnZGEwQLa3_EgOn4E56T9MBRIEc,1722
|
|
131
|
-
langchain_agentx/loop/subagent/
|
|
134
|
+
langchain_agentx/loop/subagent/reasoning_extractor.py,sha256=KrCQtBdKzsjAEfA7H5COuXzBhuGGonT2Su8IEKYlUpA,1334
|
|
135
|
+
langchain_agentx/loop/subagent/runner.py,sha256=LMf3ybGf637mphbVNEdPHhmUJzY95wtIGKKhdBkuvhg,26404
|
|
132
136
|
langchain_agentx/loop/subagent/subagent_termination_classifier.py,sha256=ULArQeAibgy6ETSf4vRR0L6X1rsDM7-ZuLzRWQ_pmeY,3158
|
|
133
137
|
langchain_agentx/loop/subagent/transcript.py,sha256=oH8n9zAL5NneZ8-wm5Jm7DQ0B0z-dgBR7KaoR-uU3B8,7182
|
|
134
138
|
langchain_agentx/memory/__init__.py,sha256=Vd2ykwjYHSKWo6Tfx0CC6GJI54LBn2K1RRVo4IoDz3Q,49
|
|
@@ -218,7 +222,7 @@ langchain_agentx/plugin/registries.py,sha256=tNegcbaI3_T3GV58RgEpoFWtOMOyBiK25Qi
|
|
|
218
222
|
langchain_agentx/plugin/types.py,sha256=GJSOR-QDmKrIv_O0xE7qxYc_nmNUlLdxM5bNVHR2_1Q,3810
|
|
219
223
|
langchain_agentx/provider/__init__.py,sha256=Wd9Ci9IXMyeoYi_F8jah8q9fp4-1G5dGAhU_mZqxkFc,750
|
|
220
224
|
langchain_agentx/provider/anthropic.py,sha256=BOWmlzx2kX79hJm_Kv9Z2CHwtNQJNiRFCTEK4mDRqkE,4182
|
|
221
|
-
langchain_agentx/provider/compatible_chat_openai.py,sha256=
|
|
225
|
+
langchain_agentx/provider/compatible_chat_openai.py,sha256=2mSvVLov7nGjRuQ0n7UuBGtUkhz2zYedMQpTQvmHVQA,4119
|
|
222
226
|
langchain_agentx/provider/env.py,sha256=bmELhixPGoJ3b0rtgEPCB512gJF2cVS00bRQfclkwD4,1040
|
|
223
227
|
langchain_agentx/provider/model_profile.py,sha256=Fl-5QOISuEB0EEQL5XyB2QMQ5zZlbWO6u0EAuZiWHMY,6246
|
|
224
228
|
langchain_agentx/provider/openai.py,sha256=mNPRPhxtkKRhBVnXqJD8r1Vmo22HNM-hT-Di_VNL-ng,2996
|
|
@@ -227,7 +231,7 @@ langchain_agentx/provider/semantics/answer_assembler.py,sha256=wR0fe-OqyUmK1wrZX
|
|
|
227
231
|
langchain_agentx/provider/semantics/contracts.py,sha256=Jsw9kMt4lMPI8rYo2xB8UB5FKTOqKbPZxoerOXfE0LA,6578
|
|
228
232
|
langchain_agentx/provider/semantics/finish_reason_normalizer.py,sha256=GmIHYpUtP3MSFRgbLQxlc3zuthip5Z5ZfMUZcKkDjDw,7230
|
|
229
233
|
langchain_agentx/provider/semantics/message_normalizer.py,sha256=f22_eD3thaDKznCWI1uOM-ek5AE4sLX6UGq3F4Ld5-4,3521
|
|
230
|
-
langchain_agentx/provider/semantics/profile.py,sha256=
|
|
234
|
+
langchain_agentx/provider/semantics/profile.py,sha256=I8W1R_VNx6J4GWbBIhBH-UgFmxpx6unlg7hCdugYFJY,8283
|
|
231
235
|
langchain_agentx/provider/semantics/registry.py,sha256=wV2T74nyZSIoQi4cOAm3SkmuouRpzq2cUL66xFuM3Rs,7861
|
|
232
236
|
langchain_agentx/provider/semantics/request_codec.py,sha256=cq9u3CidOmrHlQWQbdEzCGd5HuGvvHLVX1KXELbgzjg,2017
|
|
233
237
|
langchain_agentx/provider/semantics/runtime.py,sha256=vbMRPK9aBsc34SeS-ZBnMAiDEND29DSjx0zUbEoCeO0,11273
|
|
@@ -345,7 +349,7 @@ langchain_agentx/task_runtime/tasks/trace_cleanup/executor.py,sha256=qez3WnzBTag
|
|
|
345
349
|
langchain_agentx/task_runtime/tasks/trace_cleanup/scheduler.py,sha256=lqWlCqucs26fhr4M9LfgY8I0xuNDxwSLkre6XjAJDhA,6877
|
|
346
350
|
langchain_agentx/tool_runtime/__init__.py,sha256=WBRUXxT-NzW37ubRueWH4UFF7b557V0atZ9nB_yxr1Y,4925
|
|
347
351
|
langchain_agentx/tool_runtime/accept_edits_fast_path.py,sha256=9Tr75ED1LfHHSrtD5Z3wHxe0PBTOHF6ms_dz5g9l2zg,1820
|
|
348
|
-
langchain_agentx/tool_runtime/adapter.py,sha256=
|
|
352
|
+
langchain_agentx/tool_runtime/adapter.py,sha256=lPgTPpn5X2QyD8C6m-m5GxbNFXzBfRntLi4-HQkcZgs,32279
|
|
349
353
|
langchain_agentx/tool_runtime/base.py,sha256=Ez9W42DsaiZcWDDO8UfRKv3E7_Z6WBcABkwdAi6qTK8,21390
|
|
350
354
|
langchain_agentx/tool_runtime/capabilities.py,sha256=KHwu5KLqrpPKLOTwHhnvIIs9MTmQYeuS2hAFmI_Nsy8,3837
|
|
351
355
|
langchain_agentx/tool_runtime/denial_tracking.py,sha256=FOJDGk-bWR2FpEPjFK_vLa1sP8z4U5ZCF7BNgcGwNQs,8139
|
|
@@ -359,7 +363,7 @@ langchain_agentx/tool_runtime/loader.py,sha256=RrijbkWOGYqG-1tZV-4s7wyWb2GDKQ9re
|
|
|
359
363
|
langchain_agentx/tool_runtime/loop_loader.py,sha256=qMKDEWyRAgEriAvF4iN-NqPuQhuIoDhpdk6bcGx2T0Y,7943
|
|
360
364
|
langchain_agentx/tool_runtime/loop_runtime_context.py,sha256=HBZMdtKD7MpU8o7JmURFZGPTfxr2aFFlFF2nBZR_n8s,3613
|
|
361
365
|
langchain_agentx/tool_runtime/messages.py,sha256=Fp-ZiLAbOQAQa8uIv-owE8PXtWyhpYSXxtzy3PVbKSw,3631
|
|
362
|
-
langchain_agentx/tool_runtime/models.py,sha256=
|
|
366
|
+
langchain_agentx/tool_runtime/models.py,sha256=5Uk7v74dxqiPAP3OEVlmRzxbarhf4m3VEqmghLOhr-k,20153
|
|
363
367
|
langchain_agentx/tool_runtime/outer_permission_resolver.py,sha256=Mffl4asUU4KrN653Xu54w_a_nLjDQS4VtpzjAGvFTlg,3405
|
|
364
368
|
langchain_agentx/tool_runtime/override.py,sha256=EmHlRZxkO2cllaw_9iYKupQrA-rBsUe0HdHbOyF_a8I,9804
|
|
365
369
|
langchain_agentx/tool_runtime/path_safety.py,sha256=SlqDTQioNfgdw4_KasgNB9J2R7qIRYYjpTIwnLvu_TY,7962
|
|
@@ -456,7 +460,7 @@ langchain_agentx/tools/agent/models.py,sha256=jPxah6JgPt0UlZVyrAQxQeZmVUDOBV7xyl
|
|
|
456
460
|
langchain_agentx/tools/agent/prompt.py,sha256=QQA40AfA5sEIl_LG7kn7UlUKFBOzsHSs_M8c5n-p7YU,8093
|
|
457
461
|
langchain_agentx/tools/agent/prompt_scope.py,sha256=xQup3hosuN0B6TFq7jYV5SE6gzZNwR6BOQ552dfef5I,2677
|
|
458
462
|
langchain_agentx/tools/agent/scope.py,sha256=6F2ZJgSvt-gFeciyKIQxaIiW5PBW9zUeTj3P0uxqrrg,6324
|
|
459
|
-
langchain_agentx/tools/agent/tool.py,sha256=
|
|
463
|
+
langchain_agentx/tools/agent/tool.py,sha256=jHzQGFo37yMQnnadw7X-OhFERZreA6KbDf06WZ9orIU,17230
|
|
460
464
|
langchain_agentx/tools/agent/built_in/__init__.py,sha256=4YUHj8nUcUOvJW9p158W_t_RBoQt8Uh7eTt0idnCTE4,657
|
|
461
465
|
langchain_agentx/tools/agent/built_in/agentx_guide.py,sha256=ICEiocaH7-kSYeZJTHcpe4xEU9SYAkkcNKskTu8v1xk,2698
|
|
462
466
|
langchain_agentx/tools/agent/built_in/explore.py,sha256=PQaeqRvtV645M95o2844cCj5l6h5yjvNpfYTZrcZHqU,3483
|
|
@@ -651,8 +655,8 @@ langchain_agentx/workspace/root_grant_manager.py,sha256=W3gy8HTevbERbXqIgRcjzOXO
|
|
|
651
655
|
langchain_agentx/workspace/tool_boundary.py,sha256=UDwX6swpSLsx9HNkYuuRRiV5o7ZZG_Bru2Yn0c5kNX8,5724
|
|
652
656
|
langchain_agentx/workspace/validators.py,sha256=tQt-6TOcL8Fw7Ig5ebA9S7vGWh1rby920eFW6x8Tk9E,1439
|
|
653
657
|
langchain_agentx/workspace/view.py,sha256=Ip02CZNI-ZjF5k0OYT3q7RUR_auMEw83VJ6rQsaBcYU,4588
|
|
654
|
-
langchain_agentx_python-1.2.
|
|
655
|
-
langchain_agentx_python-1.2.
|
|
656
|
-
langchain_agentx_python-1.2.
|
|
657
|
-
langchain_agentx_python-1.2.
|
|
658
|
-
langchain_agentx_python-1.2.
|
|
658
|
+
langchain_agentx_python-1.2.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
659
|
+
langchain_agentx_python-1.2.3.dist-info/METADATA,sha256=N4tAxp5fwPKzBz1-D0n25HdwIsFUskU8RqHS3Br1SR4,24846
|
|
660
|
+
langchain_agentx_python-1.2.3.dist-info/WHEEL,sha256=51RkbunBAw4BWsgaQWTpPhg4Diwp3c9P5iaLk67Hdtg,92
|
|
661
|
+
langchain_agentx_python-1.2.3.dist-info/top_level.txt,sha256=Ge284pniNt8xea0OLk2o9o32GqVpDhOYk20fwE-0xxA,17
|
|
662
|
+
langchain_agentx_python-1.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{langchain_agentx_python-1.2.2.dist-info → langchain_agentx_python-1.2.3.dist-info}/top_level.txt
RENAMED
|
File without changes
|