agent-switch 0.1.0__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.
- agent_core/__init__.py +80 -0
- agent_core/abc.py +27 -0
- agent_core/adapter_base.py +437 -0
- agent_core/backends/__init__.py +14 -0
- agent_core/backends/deepagents/__init__.py +7 -0
- agent_core/backends/deepagents/adapter.py +134 -0
- agent_core/backends/deepagents/hooks_middleware.py +403 -0
- agent_core/backends/deepagents/mapping.py +426 -0
- agent_core/backends/qcoder/__init__.py +7 -0
- agent_core/backends/qcoder/adapter.py +113 -0
- agent_core/backends/qcoder/hooks_bridge.py +201 -0
- agent_core/backends/qcoder/mapping.py +371 -0
- agent_core/backends/stub.py +52 -0
- agent_core/exceptions.py +54 -0
- agent_core/factory.py +27 -0
- agent_core/hooks/__init__.py +53 -0
- agent_core/hooks/base.py +78 -0
- agent_core/hooks/context.py +167 -0
- agent_core/hooks/dispatcher.py +123 -0
- agent_core/hooks/emitter.py +161 -0
- agent_core/hooks/enums.py +34 -0
- agent_core/hooks/result.py +41 -0
- agent_core/logging.py +194 -0
- agent_core/registry.py +43 -0
- agent_core/types/__init__.py +30 -0
- agent_core/types/config.py +87 -0
- agent_core/types/enums.py +21 -0
- agent_core/types/mcp.py +31 -0
- agent_core/types/message.py +76 -0
- agent_core/types/model.py +19 -0
- agent_core/types/response.py +38 -0
- agent_core/types/skill.py +14 -0
- agent_core/types/subagent.py +27 -0
- agent_core/types/tool.py +18 -0
- agent_core/utils/__init__.py +7 -0
- agent_core/utils/input.py +24 -0
- agent_switch-0.1.0.dist-info/METADATA +380 -0
- agent_switch-0.1.0.dist-info/RECORD +39 -0
- agent_switch-0.1.0.dist-info/WHEEL +4 -0
agent_core/__init__.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""agent-switch:Agent SDK 统一抽象层(deepagents、Qcoder SDK 等)。
|
|
2
|
+
|
|
3
|
+
业务代码通过统一 API(``create_agent`` + ``run`` / ``stream``)切换底层框架,
|
|
4
|
+
上层类型与调用方式不变。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from agent_core.abc import AgentAdapter
|
|
10
|
+
from agent_core.adapter_base import BaseAgentAdapter
|
|
11
|
+
from agent_core.backends import DeepAgentsAdapter, QcoderAdapter # 触发后端注册
|
|
12
|
+
from agent_core.exceptions import (
|
|
13
|
+
AgentConfigError,
|
|
14
|
+
AgentCoreError,
|
|
15
|
+
BackendNotFoundError,
|
|
16
|
+
BackendNotImplementedError,
|
|
17
|
+
HookBlockedError,
|
|
18
|
+
)
|
|
19
|
+
from agent_core.factory import create_agent
|
|
20
|
+
from agent_core.hooks import (
|
|
21
|
+
AgentHookEvent,
|
|
22
|
+
BaseAgentHooks,
|
|
23
|
+
HookOutcome,
|
|
24
|
+
HookResult,
|
|
25
|
+
resolve_hook_event,
|
|
26
|
+
)
|
|
27
|
+
from agent_core.logging import configure_logging
|
|
28
|
+
from agent_core.registry import BackendRegistry
|
|
29
|
+
from agent_core.types import (
|
|
30
|
+
AgentBackend,
|
|
31
|
+
AgentChunk,
|
|
32
|
+
AgentConfig,
|
|
33
|
+
AgentMcpConfig,
|
|
34
|
+
AgentMcpServer,
|
|
35
|
+
AgentMessage,
|
|
36
|
+
AgentModel,
|
|
37
|
+
AgentResponse,
|
|
38
|
+
AgentSkillsConfig,
|
|
39
|
+
AgentSubagent,
|
|
40
|
+
AgentTool,
|
|
41
|
+
MessageRole,
|
|
42
|
+
ToolCall,
|
|
43
|
+
ToolResult,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
__version__ = "0.1.0"
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"AgentAdapter",
|
|
50
|
+
"AgentBackend",
|
|
51
|
+
"AgentChunk",
|
|
52
|
+
"AgentConfig",
|
|
53
|
+
"AgentConfigError",
|
|
54
|
+
"AgentCoreError",
|
|
55
|
+
"AgentHookEvent",
|
|
56
|
+
"AgentMcpConfig",
|
|
57
|
+
"AgentMcpServer",
|
|
58
|
+
"AgentMessage",
|
|
59
|
+
"AgentModel",
|
|
60
|
+
"AgentResponse",
|
|
61
|
+
"AgentSkillsConfig",
|
|
62
|
+
"AgentSubagent",
|
|
63
|
+
"AgentTool",
|
|
64
|
+
"BackendNotFoundError",
|
|
65
|
+
"BackendNotImplementedError",
|
|
66
|
+
"BackendRegistry",
|
|
67
|
+
"BaseAgentAdapter",
|
|
68
|
+
"BaseAgentHooks",
|
|
69
|
+
"DeepAgentsAdapter",
|
|
70
|
+
"HookBlockedError",
|
|
71
|
+
"HookOutcome",
|
|
72
|
+
"HookResult",
|
|
73
|
+
"MessageRole",
|
|
74
|
+
"QcoderAdapter",
|
|
75
|
+
"ToolCall",
|
|
76
|
+
"ToolResult",
|
|
77
|
+
"configure_logging",
|
|
78
|
+
"create_agent",
|
|
79
|
+
"resolve_hook_event",
|
|
80
|
+
]
|
agent_core/abc.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Agent 适配器抽象基类。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import AsyncIterator
|
|
7
|
+
|
|
8
|
+
from agent_core.types import AgentChunk, AgentConfig, AgentMessage, AgentResponse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AgentAdapter(ABC):
|
|
12
|
+
"""Agent SDK 统一抽象:业务代码只依赖本接口,不直接依赖底层框架。"""
|
|
13
|
+
|
|
14
|
+
backend_name: str = ""
|
|
15
|
+
|
|
16
|
+
def __init__(self, config: AgentConfig | None = None) -> None:
|
|
17
|
+
self._default_config = config
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def run(self, input: str | list[AgentMessage], config: AgentConfig | None = None) -> AgentResponse:
|
|
21
|
+
"""同步运行一次 Agent 会话,返回完整响应。"""
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def stream(self, input: str | list[AgentMessage], config: AgentConfig | None = None) -> AsyncIterator[AgentChunk]:
|
|
26
|
+
"""流式运行,逐块返回 AgentChunk。"""
|
|
27
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"""适配器基类:提供 hooks 生命周期编排。
|
|
2
|
+
|
|
3
|
+
事件分两层触发:
|
|
4
|
+
|
|
5
|
+
- **agent 级**(每次 agent 执行一次):``beforeAgent → beforePrompt → afterAgent``。
|
|
6
|
+
对 deepagents 后端,这三个事件由注入的 ``AgentHooksMiddleware`` 在 SDK 内部的
|
|
7
|
+
``before_agent`` / ``after_agent``(图的 entry / exit 节点)触发;对 stub 后端
|
|
8
|
+
由本类在 ``run`` / ``stream`` 主流程中触发。
|
|
9
|
+
- **调用级**(每次 LLM / 工具调用一次):``beforeLLM / afterLLM / beforeTool /
|
|
10
|
+
afterTool / afterToolError``。对 deepagents 后端由中间件的 ``wrap_model_call`` /
|
|
11
|
+
``wrap_tool_call`` 触发。
|
|
12
|
+
- ``afterStop``(reason 为 ``complete`` / ``error``)始终由本类在 run/stream 边界触发。
|
|
13
|
+
|
|
14
|
+
尚未桥接(不触发)的事件:``beforePermission / beforeSubagent / afterSubagent``。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import uuid
|
|
20
|
+
|
|
21
|
+
from agent_core.abc import AgentAdapter
|
|
22
|
+
from agent_core.exceptions import HookBlockedError
|
|
23
|
+
from agent_core.hooks.context import AgentHookContext
|
|
24
|
+
from agent_core.hooks.dispatcher import AgentHooksDispatcher
|
|
25
|
+
from agent_core.hooks.emitter import (
|
|
26
|
+
apply_messages_modify,
|
|
27
|
+
build_after_agent_context,
|
|
28
|
+
build_after_llm_context,
|
|
29
|
+
build_after_stop_context,
|
|
30
|
+
build_before_agent_context,
|
|
31
|
+
build_before_llm_context,
|
|
32
|
+
build_before_prompt_context,
|
|
33
|
+
)
|
|
34
|
+
from agent_core.hooks.enums import AgentHookEvent
|
|
35
|
+
from agent_core.hooks.result import HookOutcome, HookResult
|
|
36
|
+
from agent_core.logging import get_logger
|
|
37
|
+
from agent_core.types import AgentConfig, AgentMessage, AgentResponse
|
|
38
|
+
from agent_core.utils.input import normalize_input
|
|
39
|
+
|
|
40
|
+
_logger = get_logger("agent_core.adapter")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class BaseAgentAdapter(AgentAdapter):
|
|
44
|
+
"""带 hooks 生命周期管理的适配器基类。
|
|
45
|
+
|
|
46
|
+
子类(如 QcoderAdapter / DeepAgentsAdapter)只需实现 ``run`` / ``stream``,
|
|
47
|
+
并调用本类提供的生命周期方法即可获得统一的 hooks 行为。
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
#: True 时 beforeLLM / afterLLM 由后端中间件按「每次调用」触发,
|
|
51
|
+
#: adapter 层不再重复触发(deepagents 通过 wrap_model_call 桥接)。
|
|
52
|
+
call_hooks_via_middleware: bool = False
|
|
53
|
+
#: True 时 beforeAgent / beforePrompt / afterAgent 由后端中间件的
|
|
54
|
+
#: before_agent / after_agent 节点钩子触发(deepagents 的 entry / exit 节点)。
|
|
55
|
+
#: 注意:错误路径的 afterAgent 仍在 adapter 层触发 —— 因为图抛异常时
|
|
56
|
+
#: after_agent 节点不会执行,只能由 _finalize_run_error_* 补发。
|
|
57
|
+
agent_hooks_via_middleware: bool = False
|
|
58
|
+
|
|
59
|
+
def __init__(self, config: AgentConfig | None = None) -> None:
|
|
60
|
+
super().__init__(config)
|
|
61
|
+
# 每次 run/stream 生成一对相关 ID:
|
|
62
|
+
# session_id 标记一次会话,correlation_id 用于把同一次调用产生的
|
|
63
|
+
# 日志 / hooks 上下文关联起来。中间件通过 session_provider 闭包读取它们。
|
|
64
|
+
self._session_id: str | None = None
|
|
65
|
+
self._correlation_id: str | None = None
|
|
66
|
+
|
|
67
|
+
# ---- 内部工具 ----
|
|
68
|
+
|
|
69
|
+
def _resolve_config(self, config: AgentConfig | None) -> AgentConfig | None:
|
|
70
|
+
"""配置解析:调用时传入的 config 优先,否则使用构造时配置。
|
|
71
|
+
|
|
72
|
+
这样既支持 ``create_agent(backend, config)`` 一次配置多次使用,
|
|
73
|
+
也支持 ``agent.run(input, AgentConfig(...))`` 按次覆盖(不污染默认配置)。
|
|
74
|
+
"""
|
|
75
|
+
return config if config is not None else self._default_config
|
|
76
|
+
|
|
77
|
+
def _resolve_hooks_dispatcher(self, config: AgentConfig | None) -> AgentHooksDispatcher | None:
|
|
78
|
+
"""根据(解析后的)config 构建 hooks 派发器;无 hooks 时返回 None。
|
|
79
|
+
|
|
80
|
+
返回 None 意味着后续所有 emit 都走「无 hooks」快捷路径(直接返回
|
|
81
|
+
CONTINUE 结果),避免为没有 hooks 的调用创建派发器。
|
|
82
|
+
"""
|
|
83
|
+
resolved = self._resolve_config(config)
|
|
84
|
+
if resolved is None or not resolved.hooks:
|
|
85
|
+
return None
|
|
86
|
+
return AgentHooksDispatcher(resolved.hooks)
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def _new_session_ids() -> tuple[str, str]:
|
|
90
|
+
"""生成 session_id / correlation_id:取 uuid 前 12 位十六进制,足够短且唯一。"""
|
|
91
|
+
return uuid.uuid4().hex[:12], uuid.uuid4().hex[:12]
|
|
92
|
+
|
|
93
|
+
def _emit_hook_sync(self, dispatcher: AgentHooksDispatcher | None, event: AgentHookEvent, context: AgentHookContext) -> HookResult:
|
|
94
|
+
"""同步派发单个事件。
|
|
95
|
+
|
|
96
|
+
dispatcher 为 None(未配置 hooks)时直接返回默认 CONTINUE 结果;
|
|
97
|
+
否则调用 dispatcher.emit_sync —— 其内部用 asyncio.run 驱动异步 hook,
|
|
98
|
+
因此不能在已有运行中事件循环的上下文中调用(会抛 RuntimeError)。
|
|
99
|
+
"""
|
|
100
|
+
if dispatcher is None:
|
|
101
|
+
return HookResult()
|
|
102
|
+
return dispatcher.emit_sync(event, context)
|
|
103
|
+
|
|
104
|
+
async def _emit_hook_async(self, dispatcher: AgentHooksDispatcher | None, event: AgentHookEvent, context: AgentHookContext) -> HookResult:
|
|
105
|
+
"""异步派发单个事件(stream 等异步路径使用)。"""
|
|
106
|
+
if dispatcher is None:
|
|
107
|
+
return HookResult()
|
|
108
|
+
return await dispatcher.emit(event, context)
|
|
109
|
+
|
|
110
|
+
def _raise_if_hook_blocked(self, event: AgentHookEvent, hook_result: HookResult | None) -> None:
|
|
111
|
+
"""hook 返回 BLOCK 时抛出 HookBlockedError。
|
|
112
|
+
|
|
113
|
+
BLOCK 语义:拦截并终止本次调用。抛出后由 run/stream 的异常路径接管
|
|
114
|
+
(触发 afterAgent(error) + afterStop(error) 后重抛给调用方)。
|
|
115
|
+
"""
|
|
116
|
+
if hook_result is not None and hook_result.outcome is HookOutcome.BLOCK:
|
|
117
|
+
raise HookBlockedError(hook_event=event, reason=hook_result.reason)
|
|
118
|
+
|
|
119
|
+
# ---- 生命周期:前置 ----
|
|
120
|
+
|
|
121
|
+
def _prepare_messages_sync(self, input: str | list[AgentMessage], config: AgentConfig | None) -> list[AgentMessage]:
|
|
122
|
+
"""同步前置:normalize → beforeAgent → beforePrompt → apply_messages_modify。
|
|
123
|
+
|
|
124
|
+
流程说明:
|
|
125
|
+
1. 先把输入归一化为 list[AgentMessage](str 会被包装成 user 消息);
|
|
126
|
+
2. 生成本次调用的 session_id / correlation_id(后续事件共用);
|
|
127
|
+
3. 依次触发 beforeAgent / beforePrompt,任一返回 BLOCK 即中止;
|
|
128
|
+
4. beforePrompt 返回 MODIFY 时,用 data["messages"] 替换输入消息。
|
|
129
|
+
"""
|
|
130
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
131
|
+
messages = normalize_input(input)
|
|
132
|
+
# 会话 ID 必须在这里生成:即使没有 hooks,后续中间件 / 日志也需要它
|
|
133
|
+
self._session_id, self._correlation_id = self._new_session_ids()
|
|
134
|
+
if self.agent_hooks_via_middleware:
|
|
135
|
+
# beforeAgent / beforePrompt 由中间件 before_agent(entry 节点)触发,
|
|
136
|
+
# adapter 层只做归一化与会话 ID 生成,避免重复触发
|
|
137
|
+
return messages
|
|
138
|
+
session_result = self._emit_hook_sync(
|
|
139
|
+
dispatcher,
|
|
140
|
+
AgentHookEvent.BEFORE_AGENT,
|
|
141
|
+
build_before_agent_context(
|
|
142
|
+
self.backend_name,
|
|
143
|
+
messages,
|
|
144
|
+
config,
|
|
145
|
+
session_id=self._session_id,
|
|
146
|
+
correlation_id=self._correlation_id,
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
self._raise_if_hook_blocked(AgentHookEvent.BEFORE_AGENT, session_result)
|
|
150
|
+
prompt_result = self._emit_hook_sync(
|
|
151
|
+
dispatcher,
|
|
152
|
+
AgentHookEvent.BEFORE_PROMPT,
|
|
153
|
+
build_before_prompt_context(
|
|
154
|
+
self.backend_name,
|
|
155
|
+
messages,
|
|
156
|
+
config,
|
|
157
|
+
session_id=self._session_id,
|
|
158
|
+
correlation_id=self._correlation_id,
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
self._raise_if_hook_blocked(AgentHookEvent.BEFORE_PROMPT, prompt_result)
|
|
162
|
+
# MODIFY 时替换消息列表;未修改时原样返回
|
|
163
|
+
return apply_messages_modify(messages, prompt_result)
|
|
164
|
+
|
|
165
|
+
async def _prepare_messages_async(self, input: str | list[AgentMessage], config: AgentConfig | None) -> list[AgentMessage]:
|
|
166
|
+
"""异步版前置:normalize → beforeAgent → beforePrompt → apply_messages_modify。"""
|
|
167
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
168
|
+
messages = normalize_input(input)
|
|
169
|
+
self._session_id, self._correlation_id = self._new_session_ids()
|
|
170
|
+
if self.agent_hooks_via_middleware:
|
|
171
|
+
# beforeAgent / beforePrompt 由中间件 before_agent 节点触发
|
|
172
|
+
return messages
|
|
173
|
+
session_result = await self._emit_hook_async(
|
|
174
|
+
dispatcher,
|
|
175
|
+
AgentHookEvent.BEFORE_AGENT,
|
|
176
|
+
build_before_agent_context(
|
|
177
|
+
self.backend_name,
|
|
178
|
+
messages,
|
|
179
|
+
config,
|
|
180
|
+
session_id=self._session_id,
|
|
181
|
+
correlation_id=self._correlation_id,
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
self._raise_if_hook_blocked(AgentHookEvent.BEFORE_AGENT, session_result)
|
|
185
|
+
prompt_result = await self._emit_hook_async(
|
|
186
|
+
dispatcher,
|
|
187
|
+
AgentHookEvent.BEFORE_PROMPT,
|
|
188
|
+
build_before_prompt_context(
|
|
189
|
+
self.backend_name,
|
|
190
|
+
messages,
|
|
191
|
+
config,
|
|
192
|
+
session_id=self._session_id,
|
|
193
|
+
correlation_id=self._correlation_id,
|
|
194
|
+
),
|
|
195
|
+
)
|
|
196
|
+
self._raise_if_hook_blocked(AgentHookEvent.BEFORE_PROMPT, prompt_result)
|
|
197
|
+
return apply_messages_modify(messages, prompt_result)
|
|
198
|
+
|
|
199
|
+
def _emit_before_llm_sync(self, messages: list[AgentMessage], config: AgentConfig | None) -> list[AgentMessage]:
|
|
200
|
+
"""beforeLLM → apply_messages_modify,返回可能被改写的消息列表。
|
|
201
|
+
|
|
202
|
+
这是发给 SDK 前最后一次改写机会(例如注入上下文、替换 prompt)。
|
|
203
|
+
"""
|
|
204
|
+
if self.call_hooks_via_middleware:
|
|
205
|
+
# beforeLLM 由后端中间件按调用触发,adapter 层跳过
|
|
206
|
+
return messages
|
|
207
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
208
|
+
result = self._emit_hook_sync(
|
|
209
|
+
dispatcher,
|
|
210
|
+
AgentHookEvent.BEFORE_LLM,
|
|
211
|
+
build_before_llm_context(
|
|
212
|
+
self.backend_name,
|
|
213
|
+
messages,
|
|
214
|
+
config,
|
|
215
|
+
session_id=self._session_id,
|
|
216
|
+
correlation_id=self._correlation_id,
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
self._raise_if_hook_blocked(AgentHookEvent.BEFORE_LLM, result)
|
|
220
|
+
return apply_messages_modify(messages, result)
|
|
221
|
+
|
|
222
|
+
async def _emit_before_llm_async(self, messages: list[AgentMessage], config: AgentConfig | None) -> list[AgentMessage]:
|
|
223
|
+
"""异步版:beforeLLM → apply_messages_modify。"""
|
|
224
|
+
if self.call_hooks_via_middleware:
|
|
225
|
+
# beforeLLM 由后端中间件按调用触发,adapter 层跳过
|
|
226
|
+
return messages
|
|
227
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
228
|
+
result = await self._emit_hook_async(
|
|
229
|
+
dispatcher,
|
|
230
|
+
AgentHookEvent.BEFORE_LLM,
|
|
231
|
+
build_before_llm_context(
|
|
232
|
+
self.backend_name,
|
|
233
|
+
messages,
|
|
234
|
+
config,
|
|
235
|
+
session_id=self._session_id,
|
|
236
|
+
correlation_id=self._correlation_id,
|
|
237
|
+
),
|
|
238
|
+
)
|
|
239
|
+
self._raise_if_hook_blocked(AgentHookEvent.BEFORE_LLM, result)
|
|
240
|
+
return apply_messages_modify(messages, result)
|
|
241
|
+
|
|
242
|
+
# ---- 生命周期:收尾 ----
|
|
243
|
+
|
|
244
|
+
def _finalize_run_success_sync(self, config: AgentConfig | None, response: AgentResponse) -> None:
|
|
245
|
+
"""run 成功收尾:afterLLM → afterAgent → afterStop(complete)。
|
|
246
|
+
|
|
247
|
+
中间件桥接开关(call/agent hooks via middleware)为 True 时跳过对应事件,
|
|
248
|
+
避免 adapter 层与 SDK 内部重复触发。
|
|
249
|
+
"""
|
|
250
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
251
|
+
if not self.call_hooks_via_middleware:
|
|
252
|
+
after_llm = self._emit_hook_sync(
|
|
253
|
+
dispatcher,
|
|
254
|
+
AgentHookEvent.AFTER_LLM,
|
|
255
|
+
build_after_llm_context(
|
|
256
|
+
self.backend_name,
|
|
257
|
+
response=response,
|
|
258
|
+
session_id=self._session_id,
|
|
259
|
+
correlation_id=self._correlation_id,
|
|
260
|
+
),
|
|
261
|
+
)
|
|
262
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_LLM, after_llm)
|
|
263
|
+
if not self.agent_hooks_via_middleware:
|
|
264
|
+
after_agent = self._emit_hook_sync(
|
|
265
|
+
dispatcher,
|
|
266
|
+
AgentHookEvent.AFTER_AGENT,
|
|
267
|
+
build_after_agent_context(
|
|
268
|
+
self.backend_name,
|
|
269
|
+
response=response,
|
|
270
|
+
session_id=self._session_id,
|
|
271
|
+
correlation_id=self._correlation_id,
|
|
272
|
+
),
|
|
273
|
+
)
|
|
274
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_AGENT, after_agent)
|
|
275
|
+
after_stop = self._emit_hook_sync(
|
|
276
|
+
dispatcher,
|
|
277
|
+
AgentHookEvent.AFTER_STOP,
|
|
278
|
+
build_after_stop_context(
|
|
279
|
+
self.backend_name,
|
|
280
|
+
reason="complete",
|
|
281
|
+
response=response,
|
|
282
|
+
session_id=self._session_id,
|
|
283
|
+
correlation_id=self._correlation_id,
|
|
284
|
+
),
|
|
285
|
+
)
|
|
286
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_STOP, after_stop)
|
|
287
|
+
|
|
288
|
+
async def _finalize_run_success_async(self, config: AgentConfig | None, response: AgentResponse) -> None:
|
|
289
|
+
"""异步版 run 成功收尾:afterLLM → afterAgent → afterStop(complete)。"""
|
|
290
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
291
|
+
if not self.call_hooks_via_middleware:
|
|
292
|
+
after_llm = await self._emit_hook_async(
|
|
293
|
+
dispatcher,
|
|
294
|
+
AgentHookEvent.AFTER_LLM,
|
|
295
|
+
build_after_llm_context(
|
|
296
|
+
self.backend_name,
|
|
297
|
+
response=response,
|
|
298
|
+
session_id=self._session_id,
|
|
299
|
+
correlation_id=self._correlation_id,
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_LLM, after_llm)
|
|
303
|
+
if not self.agent_hooks_via_middleware:
|
|
304
|
+
after_agent = await self._emit_hook_async(
|
|
305
|
+
dispatcher,
|
|
306
|
+
AgentHookEvent.AFTER_AGENT,
|
|
307
|
+
build_after_agent_context(
|
|
308
|
+
self.backend_name,
|
|
309
|
+
response=response,
|
|
310
|
+
session_id=self._session_id,
|
|
311
|
+
correlation_id=self._correlation_id,
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_AGENT, after_agent)
|
|
315
|
+
after_stop = await self._emit_hook_async(
|
|
316
|
+
dispatcher,
|
|
317
|
+
AgentHookEvent.AFTER_STOP,
|
|
318
|
+
build_after_stop_context(
|
|
319
|
+
self.backend_name,
|
|
320
|
+
reason="complete",
|
|
321
|
+
response=response,
|
|
322
|
+
session_id=self._session_id,
|
|
323
|
+
correlation_id=self._correlation_id,
|
|
324
|
+
),
|
|
325
|
+
)
|
|
326
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_STOP, after_stop)
|
|
327
|
+
|
|
328
|
+
def _finalize_run_error_sync(self, config: AgentConfig | None, error: BaseException) -> None:
|
|
329
|
+
"""run 失败收尾:afterAgent → afterStop(error)。
|
|
330
|
+
|
|
331
|
+
与成功路径不同:错误路径没有 afterLLM(LLM 调用可能根本没发生)。
|
|
332
|
+
即使 agent_hooks_via_middleware=True,afterAgent(error) 也在此补发 ——
|
|
333
|
+
因为 SDK 图抛异常时 exit 节点(after_agent)不会执行。
|
|
334
|
+
"""
|
|
335
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
336
|
+
after_agent = self._emit_hook_sync(
|
|
337
|
+
dispatcher,
|
|
338
|
+
AgentHookEvent.AFTER_AGENT,
|
|
339
|
+
build_after_agent_context(
|
|
340
|
+
self.backend_name,
|
|
341
|
+
error=error,
|
|
342
|
+
session_id=self._session_id,
|
|
343
|
+
correlation_id=self._correlation_id,
|
|
344
|
+
),
|
|
345
|
+
)
|
|
346
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_AGENT, after_agent)
|
|
347
|
+
after_stop = self._emit_hook_sync(
|
|
348
|
+
dispatcher,
|
|
349
|
+
AgentHookEvent.AFTER_STOP,
|
|
350
|
+
build_after_stop_context(
|
|
351
|
+
self.backend_name,
|
|
352
|
+
reason="error",
|
|
353
|
+
error=error,
|
|
354
|
+
session_id=self._session_id,
|
|
355
|
+
correlation_id=self._correlation_id,
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_STOP, after_stop)
|
|
359
|
+
|
|
360
|
+
async def _finalize_run_error_async(self, config: AgentConfig | None, error: BaseException) -> None:
|
|
361
|
+
"""异步版 run 失败收尾:afterAgent → afterStop(error)。"""
|
|
362
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
363
|
+
after_agent = await self._emit_hook_async(
|
|
364
|
+
dispatcher,
|
|
365
|
+
AgentHookEvent.AFTER_AGENT,
|
|
366
|
+
build_after_agent_context(
|
|
367
|
+
self.backend_name,
|
|
368
|
+
error=error,
|
|
369
|
+
session_id=self._session_id,
|
|
370
|
+
correlation_id=self._correlation_id,
|
|
371
|
+
),
|
|
372
|
+
)
|
|
373
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_AGENT, after_agent)
|
|
374
|
+
after_stop = await self._emit_hook_async(
|
|
375
|
+
dispatcher,
|
|
376
|
+
AgentHookEvent.AFTER_STOP,
|
|
377
|
+
build_after_stop_context(
|
|
378
|
+
self.backend_name,
|
|
379
|
+
reason="error",
|
|
380
|
+
error=error,
|
|
381
|
+
session_id=self._session_id,
|
|
382
|
+
correlation_id=self._correlation_id,
|
|
383
|
+
),
|
|
384
|
+
)
|
|
385
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_STOP, after_stop)
|
|
386
|
+
|
|
387
|
+
async def _finalize_stream_success_async(self, config: AgentConfig | None, response: AgentResponse) -> None:
|
|
388
|
+
"""stream 成功收尾:afterLLM → afterAgent → afterStop。
|
|
389
|
+
|
|
390
|
+
流式场景在全部 chunk 消费完后调用;response 由流中收集到的
|
|
391
|
+
delta_content 拼接而成(见 _build_stream_response)。
|
|
392
|
+
"""
|
|
393
|
+
dispatcher = self._resolve_hooks_dispatcher(config)
|
|
394
|
+
if not self.call_hooks_via_middleware:
|
|
395
|
+
after_llm = await self._emit_hook_async(
|
|
396
|
+
dispatcher,
|
|
397
|
+
AgentHookEvent.AFTER_LLM,
|
|
398
|
+
build_after_llm_context(
|
|
399
|
+
self.backend_name,
|
|
400
|
+
response=response,
|
|
401
|
+
session_id=self._session_id,
|
|
402
|
+
correlation_id=self._correlation_id,
|
|
403
|
+
),
|
|
404
|
+
)
|
|
405
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_LLM, after_llm)
|
|
406
|
+
if not self.agent_hooks_via_middleware:
|
|
407
|
+
after_agent = await self._emit_hook_async(
|
|
408
|
+
dispatcher,
|
|
409
|
+
AgentHookEvent.AFTER_AGENT,
|
|
410
|
+
build_after_agent_context(
|
|
411
|
+
self.backend_name,
|
|
412
|
+
response=response,
|
|
413
|
+
session_id=self._session_id,
|
|
414
|
+
correlation_id=self._correlation_id,
|
|
415
|
+
),
|
|
416
|
+
)
|
|
417
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_AGENT, after_agent)
|
|
418
|
+
after_stop = await self._emit_hook_async(
|
|
419
|
+
dispatcher,
|
|
420
|
+
AgentHookEvent.AFTER_STOP,
|
|
421
|
+
build_after_stop_context(
|
|
422
|
+
self.backend_name,
|
|
423
|
+
reason="complete",
|
|
424
|
+
response=response,
|
|
425
|
+
session_id=self._session_id,
|
|
426
|
+
correlation_id=self._correlation_id,
|
|
427
|
+
),
|
|
428
|
+
)
|
|
429
|
+
self._raise_if_hook_blocked(AgentHookEvent.AFTER_STOP, after_stop)
|
|
430
|
+
|
|
431
|
+
def _build_stream_response(self, final_content: str) -> AgentResponse:
|
|
432
|
+
"""基于收集到的 delta_content 构造流式收尾用响应。
|
|
433
|
+
|
|
434
|
+
流式过程中逐块累加 delta_content 得到完整文本,收尾时用它构造
|
|
435
|
+
AgentResponse 供 afterLLM / afterAgent / afterStop 的 Context 使用。
|
|
436
|
+
"""
|
|
437
|
+
return AgentResponse(content=final_content, backend=self.backend_name)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""后端注册:import 本包即注册 deepagents / qcoder 适配器。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from agent_core.backends.deepagents import DeepAgentsAdapter
|
|
6
|
+
from agent_core.backends.qcoder import QcoderAdapter
|
|
7
|
+
from agent_core.registry import BackendRegistry
|
|
8
|
+
from agent_core.types import AgentBackend
|
|
9
|
+
|
|
10
|
+
# 自动注册内置后端(重复注册覆盖)
|
|
11
|
+
BackendRegistry.register(AgentBackend.DEEPAGENTS, DeepAgentsAdapter)
|
|
12
|
+
BackendRegistry.register(AgentBackend.QCODER, QcoderAdapter)
|
|
13
|
+
|
|
14
|
+
__all__ = ["DeepAgentsAdapter", "QcoderAdapter"]
|