glaip-sdk 0.6.10__py3-none-any.whl → 0.7.27__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.
- glaip_sdk/__init__.py +42 -5
- glaip_sdk/agents/base.py +295 -37
- glaip_sdk/agents/component.py +233 -0
- glaip_sdk/branding.py +113 -2
- glaip_sdk/cli/account_store.py +15 -0
- glaip_sdk/cli/auth.py +14 -8
- glaip_sdk/cli/commands/accounts.py +1 -1
- glaip_sdk/cli/commands/agents/__init__.py +116 -0
- glaip_sdk/cli/commands/agents/_common.py +562 -0
- glaip_sdk/cli/commands/agents/create.py +155 -0
- glaip_sdk/cli/commands/agents/delete.py +64 -0
- glaip_sdk/cli/commands/agents/get.py +89 -0
- glaip_sdk/cli/commands/agents/list.py +129 -0
- glaip_sdk/cli/commands/agents/run.py +264 -0
- glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
- glaip_sdk/cli/commands/agents/update.py +112 -0
- glaip_sdk/cli/commands/common_config.py +15 -12
- glaip_sdk/cli/commands/configure.py +1 -2
- glaip_sdk/cli/commands/mcps/__init__.py +94 -0
- glaip_sdk/cli/commands/mcps/_common.py +459 -0
- glaip_sdk/cli/commands/mcps/connect.py +82 -0
- glaip_sdk/cli/commands/mcps/create.py +152 -0
- glaip_sdk/cli/commands/mcps/delete.py +73 -0
- glaip_sdk/cli/commands/mcps/get.py +212 -0
- glaip_sdk/cli/commands/mcps/list.py +69 -0
- glaip_sdk/cli/commands/mcps/tools.py +235 -0
- glaip_sdk/cli/commands/mcps/update.py +190 -0
- glaip_sdk/cli/commands/models.py +2 -4
- glaip_sdk/cli/commands/shared/__init__.py +21 -0
- glaip_sdk/cli/commands/shared/formatters.py +91 -0
- glaip_sdk/cli/commands/tools/__init__.py +69 -0
- glaip_sdk/cli/commands/tools/_common.py +80 -0
- glaip_sdk/cli/commands/tools/create.py +228 -0
- glaip_sdk/cli/commands/tools/delete.py +61 -0
- glaip_sdk/cli/commands/tools/get.py +103 -0
- glaip_sdk/cli/commands/tools/list.py +69 -0
- glaip_sdk/cli/commands/tools/script.py +49 -0
- glaip_sdk/cli/commands/tools/update.py +102 -0
- glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
- glaip_sdk/cli/commands/transcripts/_common.py +9 -0
- glaip_sdk/cli/commands/transcripts/clear.py +5 -0
- glaip_sdk/cli/commands/transcripts/detail.py +5 -0
- glaip_sdk/cli/commands/{transcripts.py → transcripts_original.py} +2 -1
- glaip_sdk/cli/commands/update.py +163 -17
- glaip_sdk/cli/config.py +1 -0
- glaip_sdk/cli/core/output.py +12 -7
- glaip_sdk/cli/entrypoint.py +20 -0
- glaip_sdk/cli/main.py +127 -39
- glaip_sdk/cli/pager.py +3 -3
- glaip_sdk/cli/resolution.py +2 -1
- glaip_sdk/cli/slash/accounts_controller.py +3 -1
- glaip_sdk/cli/slash/agent_session.py +1 -1
- glaip_sdk/cli/slash/remote_runs_controller.py +3 -1
- glaip_sdk/cli/slash/session.py +343 -20
- glaip_sdk/cli/slash/tui/__init__.py +29 -1
- glaip_sdk/cli/slash/tui/accounts.tcss +97 -6
- glaip_sdk/cli/slash/tui/accounts_app.py +1117 -126
- glaip_sdk/cli/slash/tui/clipboard.py +316 -0
- glaip_sdk/cli/slash/tui/context.py +92 -0
- glaip_sdk/cli/slash/tui/indicators.py +341 -0
- glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
- glaip_sdk/cli/slash/tui/layouts/__init__.py +14 -0
- glaip_sdk/cli/slash/tui/layouts/harlequin.py +184 -0
- glaip_sdk/cli/slash/tui/loading.py +43 -21
- glaip_sdk/cli/slash/tui/remote_runs_app.py +178 -20
- glaip_sdk/cli/slash/tui/terminal.py +407 -0
- glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
- glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
- glaip_sdk/cli/slash/tui/theme/manager.py +112 -0
- glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
- glaip_sdk/cli/slash/tui/toast.py +388 -0
- glaip_sdk/cli/transcript/history.py +1 -1
- glaip_sdk/cli/transcript/viewer.py +1 -1
- glaip_sdk/cli/tui_settings.py +125 -0
- glaip_sdk/cli/update_notifier.py +215 -7
- glaip_sdk/cli/validators.py +1 -1
- glaip_sdk/client/__init__.py +2 -1
- glaip_sdk/client/_schedule_payloads.py +89 -0
- glaip_sdk/client/agents.py +290 -16
- glaip_sdk/client/base.py +25 -0
- glaip_sdk/client/hitl.py +136 -0
- glaip_sdk/client/main.py +7 -5
- glaip_sdk/client/mcps.py +44 -13
- glaip_sdk/client/payloads/agent/__init__.py +23 -0
- glaip_sdk/client/{_agent_payloads.py → payloads/agent/requests.py} +28 -48
- glaip_sdk/client/payloads/agent/responses.py +43 -0
- glaip_sdk/client/run_rendering.py +414 -3
- glaip_sdk/client/schedules.py +439 -0
- glaip_sdk/client/tools.py +52 -23
- glaip_sdk/config/constants.py +22 -2
- glaip_sdk/guardrails/__init__.py +80 -0
- glaip_sdk/guardrails/serializer.py +91 -0
- glaip_sdk/hitl/__init__.py +48 -0
- glaip_sdk/hitl/base.py +64 -0
- glaip_sdk/hitl/callback.py +43 -0
- glaip_sdk/hitl/local.py +121 -0
- glaip_sdk/hitl/remote.py +523 -0
- glaip_sdk/models/__init__.py +47 -1
- glaip_sdk/models/_provider_mappings.py +101 -0
- glaip_sdk/models/_validation.py +97 -0
- glaip_sdk/models/agent.py +2 -1
- glaip_sdk/models/agent_runs.py +2 -1
- glaip_sdk/models/constants.py +141 -0
- glaip_sdk/models/model.py +170 -0
- glaip_sdk/models/schedule.py +224 -0
- glaip_sdk/payload_schemas/agent.py +1 -0
- glaip_sdk/payload_schemas/guardrails.py +34 -0
- glaip_sdk/ptc.py +145 -0
- glaip_sdk/registry/tool.py +270 -57
- glaip_sdk/runner/__init__.py +20 -3
- glaip_sdk/runner/deps.py +6 -6
- glaip_sdk/runner/langgraph.py +427 -39
- glaip_sdk/runner/logging_config.py +77 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +30 -9
- glaip_sdk/runner/ptc_adapter.py +98 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +25 -2
- glaip_sdk/schedules/__init__.py +22 -0
- glaip_sdk/schedules/base.py +291 -0
- glaip_sdk/tools/base.py +67 -14
- glaip_sdk/utils/__init__.py +1 -0
- glaip_sdk/utils/agent_config.py +8 -2
- glaip_sdk/utils/bundler.py +138 -2
- glaip_sdk/utils/import_resolver.py +427 -49
- glaip_sdk/utils/rendering/renderer/base.py +58 -0
- glaip_sdk/utils/runtime_config.py +3 -2
- glaip_sdk/utils/sync.py +31 -11
- glaip_sdk/utils/tool_detection.py +274 -6
- glaip_sdk/utils/tool_storage_provider.py +140 -0
- {glaip_sdk-0.6.10.dist-info → glaip_sdk-0.7.27.dist-info}/METADATA +51 -40
- glaip_sdk-0.7.27.dist-info/RECORD +227 -0
- {glaip_sdk-0.6.10.dist-info → glaip_sdk-0.7.27.dist-info}/WHEEL +2 -1
- glaip_sdk-0.7.27.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.7.27.dist-info/top_level.txt +1 -0
- glaip_sdk/cli/commands/agents.py +0 -1509
- glaip_sdk/cli/commands/mcps.py +0 -1356
- glaip_sdk/cli/commands/tools.py +0 -576
- glaip_sdk/cli/utils.py +0 -263
- glaip_sdk-0.6.10.dist-info/RECORD +0 -159
- glaip_sdk-0.6.10.dist-info/entry_points.txt +0 -3
glaip_sdk/runner/langgraph.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# pylint: disable=duplicate-code
|
|
1
2
|
"""LangGraph-based runner for local agent execution.
|
|
2
3
|
|
|
3
4
|
This module provides the LangGraphRunner which executes glaip-sdk agents
|
|
@@ -19,26 +20,64 @@ from __future__ import annotations
|
|
|
19
20
|
|
|
20
21
|
import asyncio
|
|
21
22
|
import inspect
|
|
23
|
+
import logging
|
|
22
24
|
from dataclasses import dataclass
|
|
23
25
|
from typing import TYPE_CHECKING, Any
|
|
24
26
|
|
|
27
|
+
from gllm_core.utils import LoggerManager
|
|
28
|
+
|
|
29
|
+
from glaip_sdk.client.run_rendering import AgentRunRenderingManager
|
|
30
|
+
from glaip_sdk.hitl import PauseResumeCallback
|
|
31
|
+
from glaip_sdk.models import DEFAULT_MODEL
|
|
25
32
|
from glaip_sdk.runner.base import BaseRunner
|
|
26
33
|
from glaip_sdk.runner.deps import (
|
|
27
34
|
check_local_runtime_available,
|
|
28
35
|
get_local_runtime_missing_message,
|
|
29
36
|
)
|
|
30
|
-
from glaip_sdk.
|
|
31
|
-
|
|
37
|
+
from glaip_sdk.runner.ptc_adapter import (
|
|
38
|
+
normalize_ptc_for_aip_agents,
|
|
39
|
+
validate_ptc_for_local_run,
|
|
40
|
+
)
|
|
41
|
+
from glaip_sdk.utils.tool_storage_provider import build_tool_output_manager
|
|
32
42
|
|
|
33
43
|
if TYPE_CHECKING:
|
|
34
44
|
from langchain_core.messages import BaseMessage
|
|
35
45
|
|
|
36
46
|
from glaip_sdk.agents.base import Agent
|
|
37
47
|
|
|
48
|
+
|
|
49
|
+
_AIP_LOGS_SWALLOWED = False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _swallow_aip_logs(level: int = logging.ERROR) -> None:
|
|
53
|
+
"""Consume noisy AIPAgents logs once (opt-in via runner flag)."""
|
|
54
|
+
global _AIP_LOGS_SWALLOWED
|
|
55
|
+
if _AIP_LOGS_SWALLOWED:
|
|
56
|
+
return
|
|
57
|
+
prefixes = ("aip_agents.",)
|
|
58
|
+
|
|
59
|
+
def _silence(name: str) -> None:
|
|
60
|
+
lg = logging.getLogger(name)
|
|
61
|
+
lg.handlers = [logging.NullHandler()]
|
|
62
|
+
lg.propagate = False
|
|
63
|
+
lg.setLevel(level)
|
|
64
|
+
|
|
65
|
+
# Silence any already-registered loggers under the given prefixes
|
|
66
|
+
for logger_name in logging.root.manager.loggerDict:
|
|
67
|
+
if any(logger_name.startswith(prefix) for prefix in prefixes):
|
|
68
|
+
_silence(logger_name)
|
|
69
|
+
|
|
70
|
+
# Also set the base prefix loggers so future children inherit silence
|
|
71
|
+
for prefix in prefixes:
|
|
72
|
+
_silence(prefix.rstrip("."))
|
|
73
|
+
_AIP_LOGS_SWALLOWED = True
|
|
74
|
+
|
|
75
|
+
|
|
38
76
|
logger = LoggerManager().get_logger(__name__)
|
|
39
77
|
|
|
40
|
-
|
|
41
|
-
|
|
78
|
+
|
|
79
|
+
# Constants for MCP configuration validation
|
|
80
|
+
_MCP_TRANSPORT_KEYS = {"url", "command", "args", "env", "timeout", "headers"}
|
|
42
81
|
|
|
43
82
|
|
|
44
83
|
def _convert_chat_history_to_messages(
|
|
@@ -56,7 +95,11 @@ def _convert_chat_history_to_messages(
|
|
|
56
95
|
if not chat_history:
|
|
57
96
|
return []
|
|
58
97
|
|
|
59
|
-
from langchain_core.messages import
|
|
98
|
+
from langchain_core.messages import ( # noqa: PLC0415
|
|
99
|
+
AIMessage,
|
|
100
|
+
HumanMessage,
|
|
101
|
+
SystemMessage,
|
|
102
|
+
)
|
|
60
103
|
|
|
61
104
|
messages: list[BaseMessage] = []
|
|
62
105
|
for msg in chat_history:
|
|
@@ -81,16 +124,17 @@ def _convert_chat_history_to_messages(
|
|
|
81
124
|
class LangGraphRunner(BaseRunner):
|
|
82
125
|
"""Runner implementation using aip-agents LangGraphReactAgent.
|
|
83
126
|
|
|
84
|
-
|
|
85
|
-
- Execute via `LangGraphReactAgent.
|
|
86
|
-
-
|
|
127
|
+
Current behavior:
|
|
128
|
+
- Execute via `LangGraphReactAgent.arun_sse_stream()` (normalized SSE-compatible stream)
|
|
129
|
+
- Route all events through `AgentRunRenderingManager.async_process_stream_events`
|
|
130
|
+
for unified rendering between local and remote agents
|
|
87
131
|
|
|
88
132
|
Attributes:
|
|
89
133
|
default_model: Model name to use when agent.model is not set.
|
|
90
134
|
Defaults to "gpt-4o-mini".
|
|
91
135
|
"""
|
|
92
136
|
|
|
93
|
-
default_model: str =
|
|
137
|
+
default_model: str = DEFAULT_MODEL
|
|
94
138
|
|
|
95
139
|
def run(
|
|
96
140
|
self,
|
|
@@ -99,6 +143,8 @@ class LangGraphRunner(BaseRunner):
|
|
|
99
143
|
verbose: bool = False,
|
|
100
144
|
runtime_config: dict[str, Any] | None = None,
|
|
101
145
|
chat_history: list[dict[str, str]] | None = None,
|
|
146
|
+
*,
|
|
147
|
+
swallow_aip_logs: bool = True,
|
|
102
148
|
**kwargs: Any,
|
|
103
149
|
) -> str:
|
|
104
150
|
"""Execute agent synchronously and return final response text.
|
|
@@ -113,6 +159,9 @@ class LangGraphRunner(BaseRunner):
|
|
|
113
159
|
chat_history: Optional list of prior conversation messages.
|
|
114
160
|
Each message is a dict with "role" and "content" keys.
|
|
115
161
|
Defaults to None.
|
|
162
|
+
swallow_aip_logs: When True (default), silence noisy logs from aip-agents,
|
|
163
|
+
gllm_inference, OpenAILMInvoker, and httpx. Set to False to honor user
|
|
164
|
+
logging configuration.
|
|
116
165
|
**kwargs: Additional keyword arguments passed to the backend.
|
|
117
166
|
|
|
118
167
|
Returns:
|
|
@@ -141,6 +190,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
141
190
|
verbose=verbose,
|
|
142
191
|
runtime_config=runtime_config,
|
|
143
192
|
chat_history=chat_history,
|
|
193
|
+
swallow_aip_logs=swallow_aip_logs,
|
|
144
194
|
**kwargs,
|
|
145
195
|
)
|
|
146
196
|
|
|
@@ -153,6 +203,8 @@ class LangGraphRunner(BaseRunner):
|
|
|
153
203
|
verbose: bool = False,
|
|
154
204
|
runtime_config: dict[str, Any] | None = None,
|
|
155
205
|
chat_history: list[dict[str, str]] | None = None,
|
|
206
|
+
*,
|
|
207
|
+
swallow_aip_logs: bool = True,
|
|
156
208
|
**kwargs: Any,
|
|
157
209
|
) -> str:
|
|
158
210
|
"""Execute agent asynchronously and return final response text.
|
|
@@ -167,6 +219,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
167
219
|
chat_history: Optional list of prior conversation messages.
|
|
168
220
|
Each message is a dict with "role" and "content" keys.
|
|
169
221
|
Defaults to None.
|
|
222
|
+
swallow_aip_logs: When True (default), silence noisy AIPAgents logs.
|
|
170
223
|
**kwargs: Additional keyword arguments passed to the backend.
|
|
171
224
|
|
|
172
225
|
Returns:
|
|
@@ -181,6 +234,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
181
234
|
verbose=verbose,
|
|
182
235
|
runtime_config=runtime_config,
|
|
183
236
|
chat_history=chat_history,
|
|
237
|
+
swallow_aip_logs=swallow_aip_logs,
|
|
184
238
|
**kwargs,
|
|
185
239
|
)
|
|
186
240
|
|
|
@@ -191,6 +245,8 @@ class LangGraphRunner(BaseRunner):
|
|
|
191
245
|
verbose: bool = False,
|
|
192
246
|
runtime_config: dict[str, Any] | None = None,
|
|
193
247
|
chat_history: list[dict[str, str]] | None = None,
|
|
248
|
+
*,
|
|
249
|
+
swallow_aip_logs: bool = True,
|
|
194
250
|
**kwargs: Any,
|
|
195
251
|
) -> str:
|
|
196
252
|
"""Internal async implementation of agent execution.
|
|
@@ -201,13 +257,25 @@ class LangGraphRunner(BaseRunner):
|
|
|
201
257
|
verbose: If True, emit debug trace output during execution.
|
|
202
258
|
runtime_config: Optional runtime configuration for tools, MCPs, etc.
|
|
203
259
|
chat_history: Optional list of prior conversation messages.
|
|
260
|
+
swallow_aip_logs: When True (default), silence noisy AIPAgents logs.
|
|
204
261
|
**kwargs: Additional keyword arguments passed to the backend.
|
|
205
262
|
|
|
206
263
|
Returns:
|
|
207
264
|
The final response text from the agent.
|
|
208
265
|
"""
|
|
266
|
+
# Optionally swallow noisy AIPAgents logs
|
|
267
|
+
if swallow_aip_logs:
|
|
268
|
+
_swallow_aip_logs()
|
|
269
|
+
|
|
270
|
+
# POC/MVP: Create pause/resume callback for interactive HITL input
|
|
271
|
+
pause_resume_callback = PauseResumeCallback()
|
|
272
|
+
|
|
209
273
|
# Build the local LangGraphReactAgent from the glaip_sdk Agent
|
|
210
|
-
local_agent = self.build_langgraph_agent(
|
|
274
|
+
local_agent = self.build_langgraph_agent(
|
|
275
|
+
agent,
|
|
276
|
+
runtime_config=runtime_config,
|
|
277
|
+
pause_resume_callback=pause_resume_callback,
|
|
278
|
+
)
|
|
211
279
|
|
|
212
280
|
# Convert chat history to LangChain messages for the agent
|
|
213
281
|
langchain_messages = _convert_chat_history_to_messages(chat_history)
|
|
@@ -219,20 +287,68 @@ class LangGraphRunner(BaseRunner):
|
|
|
219
287
|
agent.name,
|
|
220
288
|
)
|
|
221
289
|
|
|
222
|
-
#
|
|
223
|
-
|
|
290
|
+
# Use shared render manager for unified processing
|
|
291
|
+
render_manager = AgentRunRenderingManager(logger)
|
|
292
|
+
renderer = render_manager.create_renderer(kwargs.get("renderer"), verbose=verbose)
|
|
293
|
+
|
|
294
|
+
# POC/MVP: Set renderer on callback so LocalPromptHandler can pause/resume Live
|
|
295
|
+
pause_resume_callback.set_renderer(renderer)
|
|
296
|
+
|
|
297
|
+
meta = render_manager.build_initial_metadata(agent.name, message, kwargs)
|
|
298
|
+
render_manager.start_renderer(renderer, meta)
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
# Use shared async stream processor for unified event handling
|
|
302
|
+
(
|
|
303
|
+
final_text,
|
|
304
|
+
stats_usage,
|
|
305
|
+
started_monotonic,
|
|
306
|
+
finished_monotonic,
|
|
307
|
+
) = await render_manager.async_process_stream_events(
|
|
308
|
+
local_agent.arun_sse_stream(message, **kwargs),
|
|
309
|
+
renderer,
|
|
310
|
+
meta,
|
|
311
|
+
skip_final_render=True,
|
|
312
|
+
)
|
|
313
|
+
except KeyboardInterrupt:
|
|
314
|
+
try:
|
|
315
|
+
renderer.close()
|
|
316
|
+
finally:
|
|
317
|
+
raise
|
|
318
|
+
except Exception:
|
|
319
|
+
try:
|
|
320
|
+
renderer.close()
|
|
321
|
+
finally:
|
|
322
|
+
raise
|
|
323
|
+
finally:
|
|
324
|
+
# Cleanup PTC sandbox and MCP sessions
|
|
325
|
+
# Isolated cleanup steps so one failure doesn't skip the other
|
|
326
|
+
try:
|
|
327
|
+
await local_agent.cleanup()
|
|
328
|
+
except Exception as e:
|
|
329
|
+
logger.warning("Failed to cleanup agent resources: %s", e)
|
|
224
330
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
331
|
+
# Use shared finalizer to avoid code duplication
|
|
332
|
+
from glaip_sdk.client.run_rendering import ( # noqa: PLC0415
|
|
333
|
+
finalize_render_manager,
|
|
334
|
+
)
|
|
229
335
|
|
|
230
|
-
return
|
|
336
|
+
return finalize_render_manager(
|
|
337
|
+
render_manager,
|
|
338
|
+
renderer,
|
|
339
|
+
final_text,
|
|
340
|
+
stats_usage,
|
|
341
|
+
started_monotonic,
|
|
342
|
+
finished_monotonic,
|
|
343
|
+
)
|
|
231
344
|
|
|
232
345
|
def build_langgraph_agent(
|
|
233
346
|
self,
|
|
234
347
|
agent: Agent,
|
|
235
348
|
runtime_config: dict[str, Any] | None = None,
|
|
349
|
+
shared_tool_output_manager: Any | None = None,
|
|
350
|
+
*,
|
|
351
|
+
pause_resume_callback: Any | None = None,
|
|
236
352
|
) -> Any:
|
|
237
353
|
"""Build a LangGraphReactAgent from a glaip_sdk Agent definition.
|
|
238
354
|
|
|
@@ -240,6 +356,10 @@ class LangGraphRunner(BaseRunner):
|
|
|
240
356
|
agent: The glaip_sdk Agent to convert.
|
|
241
357
|
runtime_config: Optional runtime configuration with tool_configs,
|
|
242
358
|
mcp_configs, agent_config, and agent-specific overrides.
|
|
359
|
+
shared_tool_output_manager: Optional ToolOutputManager to reuse across
|
|
360
|
+
agents with tool_output_sharing enabled.
|
|
361
|
+
pause_resume_callback: Optional callback used to pause/resume the renderer
|
|
362
|
+
during interactive HITL prompts.
|
|
243
363
|
|
|
244
364
|
Returns:
|
|
245
365
|
A configured LangGraphReactAgent instance.
|
|
@@ -249,6 +369,7 @@ class LangGraphRunner(BaseRunner):
|
|
|
249
369
|
ValueError: If agent has unsupported tools, MCPs, or sub-agents for local mode.
|
|
250
370
|
"""
|
|
251
371
|
from aip_agents.agent import LangGraphReactAgent # noqa: PLC0415
|
|
372
|
+
|
|
252
373
|
from glaip_sdk.runner.tool_adapter import LangChainToolAdapter # noqa: PLC0415
|
|
253
374
|
|
|
254
375
|
# Adapt tools for local execution
|
|
@@ -260,9 +381,6 @@ class LangGraphRunner(BaseRunner):
|
|
|
260
381
|
adapter = LangChainToolAdapter()
|
|
261
382
|
langchain_tools = adapter.adapt_tools(agent.tools)
|
|
262
383
|
|
|
263
|
-
# Build sub-agents recursively
|
|
264
|
-
sub_agent_instances = self._build_sub_agents(agent.agents, runtime_config)
|
|
265
|
-
|
|
266
384
|
# Normalize runtime config: merge global and agent-specific configs
|
|
267
385
|
normalized_config = self._normalize_runtime_config(runtime_config, agent)
|
|
268
386
|
|
|
@@ -276,15 +394,42 @@ class LangGraphRunner(BaseRunner):
|
|
|
276
394
|
merged_agent_config = self._merge_agent_config(agent, normalized_config)
|
|
277
395
|
agent_config_params, agent_config_kwargs = self._apply_agent_config(merged_agent_config)
|
|
278
396
|
|
|
279
|
-
#
|
|
397
|
+
# Validate and normalize PTC configuration for local runs
|
|
398
|
+
ptc_config = validate_ptc_for_local_run(
|
|
399
|
+
agent_ptc=agent.ptc if hasattr(agent, "ptc") else None,
|
|
400
|
+
agent_config_ptc=None, # Already validated in _merge_agent_config
|
|
401
|
+
runtime_config_ptc=None, # Already validated in _normalize_runtime_config
|
|
402
|
+
)
|
|
403
|
+
normalized_ptc = normalize_ptc_for_aip_agents(ptc_config)
|
|
404
|
+
|
|
405
|
+
# Resolve model and merge its configuration into agent kwargs
|
|
406
|
+
model_string = self._resolve_local_model(agent, agent_config_kwargs)
|
|
407
|
+
|
|
408
|
+
tool_output_manager = self._resolve_tool_output_manager(
|
|
409
|
+
agent,
|
|
410
|
+
merged_agent_config,
|
|
411
|
+
shared_tool_output_manager,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Build sub-agents recursively, sharing tool output manager when enabled.
|
|
415
|
+
sub_agent_instances = self._build_sub_agents(
|
|
416
|
+
agent.agents,
|
|
417
|
+
runtime_config,
|
|
418
|
+
shared_tool_output_manager=tool_output_manager,
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Build the LangGraphReactAgent with tools, sub-agents, configs, and PTC
|
|
280
422
|
local_agent = LangGraphReactAgent(
|
|
281
423
|
name=agent.name,
|
|
282
424
|
instruction=agent.instruction,
|
|
283
425
|
description=agent.description,
|
|
284
|
-
model=
|
|
426
|
+
model=model_string,
|
|
285
427
|
tools=langchain_tools,
|
|
286
428
|
agents=sub_agent_instances if sub_agent_instances else None,
|
|
287
429
|
tool_configs=tool_configs if tool_configs else None,
|
|
430
|
+
tool_output_manager=tool_output_manager,
|
|
431
|
+
guardrail=agent.guardrail,
|
|
432
|
+
ptc_config=normalized_ptc,
|
|
288
433
|
**agent_config_params,
|
|
289
434
|
**agent_config_kwargs,
|
|
290
435
|
)
|
|
@@ -292,6 +437,11 @@ class LangGraphRunner(BaseRunner):
|
|
|
292
437
|
# Add MCP servers if configured
|
|
293
438
|
self._add_mcp_servers(local_agent, agent, mcp_configs)
|
|
294
439
|
|
|
440
|
+
# Inject local HITL manager only if hitl_enabled is True (master switch).
|
|
441
|
+
# This matches remote behavior: hitl_enabled gates the HITL plumbing.
|
|
442
|
+
# Tool-level HITL configs are only enforced when hitl_enabled=True.
|
|
443
|
+
self._inject_hitl_manager(local_agent, merged_agent_config, agent.name, pause_resume_callback)
|
|
444
|
+
|
|
295
445
|
logger.debug(
|
|
296
446
|
"Built local LangGraphReactAgent for agent '%s' with %d tools, %d sub-agents, and %d MCPs",
|
|
297
447
|
agent.name,
|
|
@@ -301,16 +451,72 @@ class LangGraphRunner(BaseRunner):
|
|
|
301
451
|
)
|
|
302
452
|
return local_agent
|
|
303
453
|
|
|
454
|
+
def _resolve_tool_output_manager(
|
|
455
|
+
self,
|
|
456
|
+
agent: Agent,
|
|
457
|
+
merged_agent_config: dict[str, Any],
|
|
458
|
+
shared_tool_output_manager: Any | None,
|
|
459
|
+
) -> Any | None:
|
|
460
|
+
"""Resolve tool output manager for local agent execution."""
|
|
461
|
+
tool_output_sharing_enabled = merged_agent_config.get("tool_output_sharing", False)
|
|
462
|
+
if not tool_output_sharing_enabled:
|
|
463
|
+
return None
|
|
464
|
+
if shared_tool_output_manager is not None:
|
|
465
|
+
return shared_tool_output_manager
|
|
466
|
+
return build_tool_output_manager(agent.name, merged_agent_config)
|
|
467
|
+
|
|
468
|
+
def _inject_hitl_manager(
|
|
469
|
+
self,
|
|
470
|
+
local_agent: Any,
|
|
471
|
+
merged_agent_config: dict[str, Any],
|
|
472
|
+
agent_name: str,
|
|
473
|
+
pause_resume_callback: Any | None,
|
|
474
|
+
) -> None:
|
|
475
|
+
"""Inject HITL manager when enabled, mirroring remote gating behavior."""
|
|
476
|
+
hitl_enabled = merged_agent_config.get("hitl_enabled", False)
|
|
477
|
+
if hitl_enabled:
|
|
478
|
+
try:
|
|
479
|
+
from aip_agents.agent.hitl.manager import ( # noqa: PLC0415
|
|
480
|
+
ApprovalManager,
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
from glaip_sdk.hitl import LocalPromptHandler # noqa: PLC0415
|
|
484
|
+
|
|
485
|
+
local_agent.hitl_manager = ApprovalManager(
|
|
486
|
+
prompt_handler=LocalPromptHandler(pause_resume_callback=pause_resume_callback)
|
|
487
|
+
)
|
|
488
|
+
# Store callback reference for setting renderer later
|
|
489
|
+
if pause_resume_callback:
|
|
490
|
+
local_agent._pause_resume_callback = pause_resume_callback
|
|
491
|
+
logger.debug(
|
|
492
|
+
"HITL manager injected for agent '%s' (hitl_enabled=True)",
|
|
493
|
+
agent_name,
|
|
494
|
+
)
|
|
495
|
+
except ImportError as e:
|
|
496
|
+
# Missing dependencies - fail fast
|
|
497
|
+
raise ImportError("Local HITL requires aip_agents. Install with: pip install 'glaip-sdk[local]'") from e
|
|
498
|
+
except Exception as e:
|
|
499
|
+
# Other errors during HITL setup - fail fast
|
|
500
|
+
raise RuntimeError(f"Failed to initialize HITL manager for agent '{agent_name}'") from e
|
|
501
|
+
else:
|
|
502
|
+
logger.debug(
|
|
503
|
+
"HITL manager not injected for agent '%s' (hitl_enabled=False)",
|
|
504
|
+
agent_name,
|
|
505
|
+
)
|
|
506
|
+
|
|
304
507
|
def _build_sub_agents(
|
|
305
508
|
self,
|
|
306
509
|
sub_agents: list[Any] | None,
|
|
307
510
|
runtime_config: dict[str, Any] | None,
|
|
511
|
+
shared_tool_output_manager: Any | None = None,
|
|
308
512
|
) -> list[Any]:
|
|
309
513
|
"""Build sub-agent instances recursively.
|
|
310
514
|
|
|
311
515
|
Args:
|
|
312
516
|
sub_agents: List of sub-agent definitions.
|
|
313
517
|
runtime_config: Runtime config to pass to sub-agents.
|
|
518
|
+
shared_tool_output_manager: Optional ToolOutputManager to reuse across
|
|
519
|
+
agents with tool_output_sharing enabled.
|
|
314
520
|
|
|
315
521
|
Returns:
|
|
316
522
|
List of built sub-agent instances.
|
|
@@ -324,7 +530,13 @@ class LangGraphRunner(BaseRunner):
|
|
|
324
530
|
sub_agent_instances = []
|
|
325
531
|
for sub_agent in sub_agents:
|
|
326
532
|
self._validate_sub_agent_for_local_mode(sub_agent)
|
|
327
|
-
sub_agent_instances.append(
|
|
533
|
+
sub_agent_instances.append(
|
|
534
|
+
self.build_langgraph_agent(
|
|
535
|
+
sub_agent,
|
|
536
|
+
runtime_config,
|
|
537
|
+
shared_tool_output_manager=shared_tool_output_manager,
|
|
538
|
+
)
|
|
539
|
+
)
|
|
328
540
|
return sub_agent_instances
|
|
329
541
|
|
|
330
542
|
def _add_mcp_servers(
|
|
@@ -385,6 +597,14 @@ class LangGraphRunner(BaseRunner):
|
|
|
385
597
|
if not runtime_config:
|
|
386
598
|
return {}
|
|
387
599
|
|
|
600
|
+
# Check for unsupported runtime_config.ptc (v1 constraint)
|
|
601
|
+
if "ptc" in runtime_config:
|
|
602
|
+
validate_ptc_for_local_run(
|
|
603
|
+
agent_ptc=None,
|
|
604
|
+
agent_config_ptc=None,
|
|
605
|
+
runtime_config_ptc=runtime_config["ptc"],
|
|
606
|
+
)
|
|
607
|
+
|
|
388
608
|
# 1. Extract global configs and normalize keys
|
|
389
609
|
global_tool_configs = normalize_local_config_keys(runtime_config.get("tool_configs", {}))
|
|
390
610
|
global_mcp_configs = normalize_local_config_keys(runtime_config.get("mcp_configs", {}))
|
|
@@ -544,6 +764,14 @@ class LangGraphRunner(BaseRunner):
|
|
|
544
764
|
# Get runtime agent_config
|
|
545
765
|
runtime_agent_config = normalized_config.get("agent_config", {})
|
|
546
766
|
|
|
767
|
+
# Check for unsupported agent_config.ptc (local runs constraint)
|
|
768
|
+
if "ptc" in agent_agent_config or "ptc" in runtime_agent_config:
|
|
769
|
+
validate_ptc_for_local_run(
|
|
770
|
+
agent_ptc=None,
|
|
771
|
+
agent_config_ptc=agent_agent_config.get("ptc") or runtime_agent_config.get("ptc"),
|
|
772
|
+
runtime_config_ptc=None,
|
|
773
|
+
)
|
|
774
|
+
|
|
547
775
|
# Merge: agent definition < runtime config
|
|
548
776
|
return merge_configs(agent_agent_config, runtime_agent_config)
|
|
549
777
|
|
|
@@ -566,12 +794,20 @@ class LangGraphRunner(BaseRunner):
|
|
|
566
794
|
"""
|
|
567
795
|
direct_params = {}
|
|
568
796
|
kwargs_params = {}
|
|
797
|
+
config_dict = {}
|
|
569
798
|
|
|
570
799
|
# Direct constructor parameters
|
|
571
800
|
if "planning" in agent_config:
|
|
572
801
|
direct_params["planning"] = agent_config["planning"]
|
|
573
802
|
|
|
803
|
+
if "enable_a2a_token_streaming" in agent_config:
|
|
804
|
+
direct_params["enable_a2a_token_streaming"] = agent_config["enable_a2a_token_streaming"]
|
|
805
|
+
|
|
574
806
|
# Kwargs parameters (passed through **kwargs to BaseAgent)
|
|
807
|
+
if "enable_pii" in agent_config:
|
|
808
|
+
kwargs_params["enable_pii"] = agent_config["enable_pii"]
|
|
809
|
+
config_dict["enable_pii"] = agent_config["enable_pii"]
|
|
810
|
+
|
|
575
811
|
if "memory" in agent_config:
|
|
576
812
|
# Map "memory" to "memory_backend" for aip-agents compatibility
|
|
577
813
|
kwargs_params["memory_backend"] = agent_config["memory"]
|
|
@@ -582,8 +818,73 @@ class LangGraphRunner(BaseRunner):
|
|
|
582
818
|
if key in agent_config:
|
|
583
819
|
kwargs_params[key] = agent_config[key]
|
|
584
820
|
|
|
821
|
+
# Ensure we pass a config dictionary to BaseAgent, which uses it for
|
|
822
|
+
# LM configuration (api keys, etc.). Memory settings are passed only
|
|
823
|
+
# via kwargs to avoid leaking into LM invoker config.
|
|
824
|
+
if config_dict:
|
|
825
|
+
kwargs_params["config"] = config_dict
|
|
826
|
+
|
|
585
827
|
return direct_params, kwargs_params
|
|
586
828
|
|
|
829
|
+
def _convert_model_for_local(self, model: Any) -> tuple[str, dict[str, Any]]:
|
|
830
|
+
"""Convert model to aip_agents format for local execution.
|
|
831
|
+
|
|
832
|
+
Args:
|
|
833
|
+
model: Model object or string identifier.
|
|
834
|
+
|
|
835
|
+
Returns:
|
|
836
|
+
Tuple of (model_string, config_dict).
|
|
837
|
+
"""
|
|
838
|
+
from glaip_sdk.models._validation import ( # noqa: PLC0415
|
|
839
|
+
convert_model_for_local_execution,
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
return convert_model_for_local_execution(model)
|
|
843
|
+
|
|
844
|
+
def _resolve_local_model(self, agent: Agent, agent_config_kwargs: dict[str, Any]) -> str:
|
|
845
|
+
"""Resolve model string and merge its configuration into agent kwargs.
|
|
846
|
+
|
|
847
|
+
This method extracts model-specific credentials and hyperparameters from a Model
|
|
848
|
+
object and merges them into the 'config' dictionary within agent_config_kwargs.
|
|
849
|
+
This is required because BaseAgent expects LM settings (api keys, etc.) to be
|
|
850
|
+
inside the 'config' parameter, not top-level kwargs.
|
|
851
|
+
|
|
852
|
+
Example:
|
|
853
|
+
If agent has:
|
|
854
|
+
- model = Model(id="deepinfra/model", credentials="key-123")
|
|
855
|
+
- agent_config_kwargs = {"enable_pii": True, "config": {"enable_pii": True}}
|
|
856
|
+
|
|
857
|
+
_resolve_local_model will:
|
|
858
|
+
1. Resolve model_string to "openai-compatible/model"
|
|
859
|
+
2. Extract model_config as {"lm_api_key": "key-123"}
|
|
860
|
+
3. Update agent_config_kwargs["config"] to:
|
|
861
|
+
{"enable_pii": True, "lm_api_key": "key-123"}
|
|
862
|
+
|
|
863
|
+
Args:
|
|
864
|
+
agent: The glaip_sdk Agent.
|
|
865
|
+
agent_config_kwargs: Agent config kwargs to update (modified in-place).
|
|
866
|
+
|
|
867
|
+
Returns:
|
|
868
|
+
The model identifier string for local execution.
|
|
869
|
+
"""
|
|
870
|
+
model_to_use = agent.model or self.default_model
|
|
871
|
+
model_string, model_config = self._convert_model_for_local(model_to_use)
|
|
872
|
+
|
|
873
|
+
if model_config:
|
|
874
|
+
# Normalize config to a dict early to simplify merging
|
|
875
|
+
config_val = agent_config_kwargs.get("config", {})
|
|
876
|
+
if hasattr(config_val, "model_dump"):
|
|
877
|
+
config_val = config_val.model_dump()
|
|
878
|
+
|
|
879
|
+
if not isinstance(config_val, dict):
|
|
880
|
+
config_val = {}
|
|
881
|
+
|
|
882
|
+
# Use a single merge path for model configuration
|
|
883
|
+
config_val.update(model_config)
|
|
884
|
+
agent_config_kwargs["config"] = config_val
|
|
885
|
+
|
|
886
|
+
return model_string
|
|
887
|
+
|
|
587
888
|
def _apply_runtime_mcp_configs(
|
|
588
889
|
self,
|
|
589
890
|
base_configs: dict[str, Any],
|
|
@@ -612,39 +913,126 @@ class LangGraphRunner(BaseRunner):
|
|
|
612
913
|
base_config: dict[str, Any],
|
|
613
914
|
override: dict[str, Any] | None,
|
|
614
915
|
) -> dict[str, Any]:
|
|
615
|
-
"""Merge a single MCP config with runtime override.
|
|
916
|
+
"""Merge a single MCP config with a runtime override, handling normalization and parity fixes.
|
|
917
|
+
|
|
918
|
+
This method orchestrates the merging of base MCP settings (from the object definition)
|
|
919
|
+
with runtime overrides. It enforces Platform parity by prioritizing the nested 'config'
|
|
920
|
+
block while maintaining robustness for local development by auto-fixing flat transport keys.
|
|
921
|
+
|
|
922
|
+
The merge follows these priority rules (highest to lowest):
|
|
923
|
+
1. Misplaced flat keys in the override (e.g., 'url' at top level) - Auto-fixed with warning.
|
|
924
|
+
2. Nested 'config' block in the override (Matches Platform/Constructor schema).
|
|
925
|
+
3. Authentication objects in the override (Converted to HTTP headers).
|
|
926
|
+
4. Structural settings in the override (e.g., 'allowed_tools').
|
|
927
|
+
5. Base configuration from the MCP object definition.
|
|
928
|
+
|
|
929
|
+
Examples:
|
|
930
|
+
>>> # 1. Strict Nested Style (Recommended)
|
|
931
|
+
>>> override = {"config": {"url": "https://new.api"}, "allowed_tools": ["t1"]}
|
|
932
|
+
>>> self._merge_single_mcp_config("mcp", base, override)
|
|
933
|
+
>>> # Result: {"url": "https://new.api", "allowed_tools": ["t1"], ...}
|
|
934
|
+
|
|
935
|
+
>>> # 2. Flat Legacy Style (Auto-fixed with warning)
|
|
936
|
+
>>> override = {"url": "https://new.api"}
|
|
937
|
+
>>> self._merge_single_mcp_config("mcp", base, override)
|
|
938
|
+
>>> # Result: {"url": "https://new.api", ...}
|
|
939
|
+
|
|
940
|
+
>>> # 3. Header Merging (Preserves Auth)
|
|
941
|
+
>>> base = {"headers": {"Authorization": "Bearer token"}}
|
|
942
|
+
>>> override = {"headers": {"X-Custom": "val"}}
|
|
943
|
+
>>> self._merge_single_mcp_config("mcp", base, override)
|
|
944
|
+
>>> # Result: {"headers": {"Authorization": "Bearer token", "X-Custom": "val"}, ...}
|
|
616
945
|
|
|
617
946
|
Args:
|
|
618
|
-
server_name: Name of the MCP server.
|
|
619
|
-
base_config: Base
|
|
620
|
-
override: Optional runtime
|
|
947
|
+
server_name: Name of the MCP server being configured.
|
|
948
|
+
base_config: Base configuration dictionary derived from the MCP object.
|
|
949
|
+
override: Optional dictionary of runtime overrides.
|
|
621
950
|
|
|
622
951
|
Returns:
|
|
623
|
-
|
|
952
|
+
A fully merged and normalized configuration dictionary ready for the local runner.
|
|
624
953
|
"""
|
|
625
954
|
merged = base_config.copy()
|
|
626
955
|
|
|
627
956
|
if not override:
|
|
628
957
|
return merged
|
|
629
958
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
)
|
|
959
|
+
# 1. Check for misplaced keys and warn (DX/Parity guidance)
|
|
960
|
+
self._warn_if_mcp_override_misplaced(server_name, override)
|
|
633
961
|
|
|
634
|
-
#
|
|
635
|
-
|
|
636
|
-
headers = MCPConfigBuilder.build_headers_from_auth(override["authentication"])
|
|
637
|
-
if headers:
|
|
638
|
-
merged["headers"] = headers
|
|
639
|
-
logger.debug("Applied runtime authentication headers for MCP '%s'", server_name)
|
|
962
|
+
# 2. Apply Authentication (Converted to headers)
|
|
963
|
+
self._apply_mcp_auth_override(server_name, merged, override)
|
|
640
964
|
|
|
641
|
-
#
|
|
965
|
+
# 3. Apply Transport Settings (Nested 'config')
|
|
966
|
+
if "config" in override and isinstance(override["config"], dict):
|
|
967
|
+
merged.update(override["config"])
|
|
968
|
+
|
|
969
|
+
# 4. Apply Structural Settings (e.g., allowed_tools)
|
|
970
|
+
if "allowed_tools" in override:
|
|
971
|
+
merged["allowed_tools"] = override["allowed_tools"]
|
|
972
|
+
|
|
973
|
+
# 5. Preserve unknown top-level keys (backward compatibility)
|
|
974
|
+
known_keys = _MCP_TRANSPORT_KEYS | {"config", "authentication", "allowed_tools"}
|
|
642
975
|
for key, value in override.items():
|
|
643
|
-
if key
|
|
976
|
+
if key not in known_keys:
|
|
644
977
|
merged[key] = value
|
|
645
978
|
|
|
979
|
+
# 6. Apply Auto-fix for misplaced keys (Local Success)
|
|
980
|
+
for key in [k for k in override if k in _MCP_TRANSPORT_KEYS]:
|
|
981
|
+
val = override[key]
|
|
982
|
+
# Special case: Merge headers instead of overwriting to preserve auth
|
|
983
|
+
if key == "headers" and isinstance(val, dict) and isinstance(merged.get("headers"), dict):
|
|
984
|
+
merged["headers"].update(val)
|
|
985
|
+
else:
|
|
986
|
+
merged[key] = val
|
|
987
|
+
|
|
646
988
|
return merged
|
|
647
989
|
|
|
990
|
+
def _warn_if_mcp_override_misplaced(self, server_name: str, override: dict[str, Any]) -> None:
|
|
991
|
+
"""Log a warning if transport keys are found at the top level of an override.
|
|
992
|
+
|
|
993
|
+
Args:
|
|
994
|
+
server_name: Name of the MCP server.
|
|
995
|
+
override: The raw override dictionary.
|
|
996
|
+
"""
|
|
997
|
+
misplaced = [k for k in override if k in _MCP_TRANSPORT_KEYS]
|
|
998
|
+
if misplaced:
|
|
999
|
+
logger.warning(
|
|
1000
|
+
"MCP '%s' override contains transport keys at the top level: %s. "
|
|
1001
|
+
"This structure is inconsistent with the Platform and MCP constructor. "
|
|
1002
|
+
"Transport settings should be nested within a 'config' dictionary. "
|
|
1003
|
+
"Example: mcp_configs={'%s': {'config': {'%s': '...'}}}. "
|
|
1004
|
+
"Automatically merging top-level keys for local execution parity.",
|
|
1005
|
+
server_name,
|
|
1006
|
+
misplaced,
|
|
1007
|
+
server_name,
|
|
1008
|
+
misplaced[0],
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
def _apply_mcp_auth_override(
|
|
1012
|
+
self,
|
|
1013
|
+
server_name: str,
|
|
1014
|
+
merged_config: dict[str, Any],
|
|
1015
|
+
override: dict[str, Any],
|
|
1016
|
+
) -> None:
|
|
1017
|
+
"""Convert authentication override to headers and apply to config.
|
|
1018
|
+
|
|
1019
|
+
Args:
|
|
1020
|
+
server_name: Name of the MCP server.
|
|
1021
|
+
merged_config: The configuration being built (mutated in place).
|
|
1022
|
+
override: The raw override dictionary.
|
|
1023
|
+
"""
|
|
1024
|
+
if "authentication" not in override:
|
|
1025
|
+
return
|
|
1026
|
+
|
|
1027
|
+
from glaip_sdk.runner.mcp_adapter.mcp_config_builder import ( # noqa: PLC0415
|
|
1028
|
+
MCPConfigBuilder,
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
headers = MCPConfigBuilder.build_headers_from_auth(override["authentication"])
|
|
1032
|
+
if headers:
|
|
1033
|
+
merged_config["headers"] = headers
|
|
1034
|
+
logger.debug("Applied runtime authentication headers for MCP '%s'", server_name)
|
|
1035
|
+
|
|
648
1036
|
def _validate_sub_agent_for_local_mode(self, sub_agent: Any) -> None:
|
|
649
1037
|
"""Validate that a sub-agent reference is supported for local execution.
|
|
650
1038
|
|