glaip-sdk 0.6.5b3__py3-none-any.whl → 0.7.17__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 +362 -39
- 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 +2 -3
- 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 +112 -32
- glaip_sdk/cli/slash/agent_session.py +5 -2
- glaip_sdk/cli/slash/prompt.py +11 -0
- glaip_sdk/cli/slash/remote_runs_controller.py +3 -1
- glaip_sdk/cli/slash/session.py +375 -25
- glaip_sdk/cli/slash/tui/__init__.py +28 -1
- glaip_sdk/cli/slash/tui/accounts.tcss +97 -6
- glaip_sdk/cli/slash/tui/accounts_app.py +1107 -126
- glaip_sdk/cli/slash/tui/clipboard.py +195 -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 +152 -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 +5 -3
- 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 +57 -26
- glaip_sdk/config/constants.py +22 -2
- glaip_sdk/guardrails/__init__.py +80 -0
- glaip_sdk/guardrails/serializer.py +89 -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/registry/tool.py +273 -66
- glaip_sdk/runner/__init__.py +76 -0
- glaip_sdk/runner/base.py +84 -0
- glaip_sdk/runner/deps.py +115 -0
- glaip_sdk/runner/langgraph.py +1055 -0
- glaip_sdk/runner/logging_config.py +77 -0
- glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
- glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
- glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
- glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +116 -0
- glaip_sdk/runner/tool_adapter/__init__.py +18 -0
- glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
- glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +242 -0
- 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/a2a/__init__.py +34 -0
- glaip_sdk/utils/a2a/event_processor.py +188 -0
- glaip_sdk/utils/agent_config.py +8 -2
- glaip_sdk/utils/bundler.py +138 -2
- glaip_sdk/utils/import_resolver.py +43 -11
- glaip_sdk/utils/rendering/renderer/base.py +58 -0
- glaip_sdk/utils/runtime_config.py +120 -0
- glaip_sdk/utils/sync.py +31 -11
- glaip_sdk/utils/tool_detection.py +301 -0
- glaip_sdk/utils/tool_storage_provider.py +140 -0
- {glaip_sdk-0.6.5b3.dist-info → glaip_sdk-0.7.17.dist-info}/METADATA +49 -38
- glaip_sdk-0.7.17.dist-info/RECORD +224 -0
- {glaip_sdk-0.6.5b3.dist-info → glaip_sdk-0.7.17.dist-info}/WHEEL +2 -1
- glaip_sdk-0.7.17.dist-info/entry_points.txt +2 -0
- glaip_sdk-0.7.17.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.5b3.dist-info/RECORD +0 -145
- glaip_sdk-0.6.5b3.dist-info/entry_points.txt +0 -3
glaip_sdk/agents/base.py
CHANGED
|
@@ -50,14 +50,17 @@ from pathlib import Path
|
|
|
50
50
|
from typing import TYPE_CHECKING, Any
|
|
51
51
|
|
|
52
52
|
from glaip_sdk.registry import get_agent_registry, get_mcp_registry, get_tool_registry
|
|
53
|
-
from glaip_sdk.utils.discovery import find_agent
|
|
54
53
|
from glaip_sdk.utils.resource_refs import is_uuid
|
|
55
|
-
from glaip_sdk.utils.runtime_config import normalize_runtime_config_keys
|
|
56
54
|
|
|
57
55
|
if TYPE_CHECKING:
|
|
58
|
-
from glaip_sdk.
|
|
56
|
+
from glaip_sdk.client.schedules import AgentScheduleManager
|
|
57
|
+
from glaip_sdk.models import AgentResponse, Model
|
|
58
|
+
from glaip_sdk.guardrails import GuardrailManager
|
|
59
59
|
from glaip_sdk.registry import AgentRegistry, MCPRegistry, ToolRegistry
|
|
60
60
|
|
|
61
|
+
# Import model validation utility
|
|
62
|
+
from glaip_sdk.models._validation import _validate_model
|
|
63
|
+
|
|
61
64
|
logger = logging.getLogger(__name__)
|
|
62
65
|
|
|
63
66
|
_AGENT_NOT_DEPLOYED_MSG = "Agent must be deployed before running. Call deploy() first."
|
|
@@ -99,11 +102,11 @@ class Agent:
|
|
|
99
102
|
- instruction: Agent instruction text (required)
|
|
100
103
|
- description: Agent description (default: "")
|
|
101
104
|
- tools: List of tools (default: [])
|
|
105
|
+
- model: Optional model override (default: None)
|
|
102
106
|
- agents: List of sub-agents (default: [])
|
|
103
107
|
- mcps: List of MCPs (default: [])
|
|
104
108
|
- timeout: Timeout in seconds (default: 300)
|
|
105
109
|
- metadata: Optional metadata dict (default: None)
|
|
106
|
-
- model: Optional model override (default: None)
|
|
107
110
|
- framework: Agent framework (default: "langchain")
|
|
108
111
|
- version: Agent version (default: "1.0.0")
|
|
109
112
|
- agent_type: Agent type (default: "config")
|
|
@@ -130,7 +133,8 @@ class Agent:
|
|
|
130
133
|
tools: list | None = None,
|
|
131
134
|
agents: list | None = None,
|
|
132
135
|
mcps: list | None = None,
|
|
133
|
-
model: str | None = _UNSET, # type: ignore[assignment]
|
|
136
|
+
model: str | Model | None = _UNSET, # type: ignore[assignment]
|
|
137
|
+
guardrail: GuardrailManager | None = None,
|
|
134
138
|
_client: Any = None,
|
|
135
139
|
**kwargs: Any,
|
|
136
140
|
) -> None:
|
|
@@ -147,8 +151,10 @@ class Agent:
|
|
|
147
151
|
tools: List of tools (Tool classes, SDK Tool objects, or strings).
|
|
148
152
|
agents: List of sub-agents (Agent classes, instances, or strings).
|
|
149
153
|
mcps: List of MCPs.
|
|
150
|
-
model: Model identifier.
|
|
154
|
+
model: Model identifier or Model configuration object.
|
|
155
|
+
guardrail: The guardrail manager for content safety.
|
|
151
156
|
_client: Internal client reference (set automatically).
|
|
157
|
+
|
|
152
158
|
**kwargs: Additional configuration parameters:
|
|
153
159
|
- timeout: Execution timeout in seconds.
|
|
154
160
|
- metadata: Optional metadata dictionary.
|
|
@@ -173,7 +179,8 @@ class Agent:
|
|
|
173
179
|
self._tools = tools
|
|
174
180
|
self._agents = agents
|
|
175
181
|
self._mcps = mcps
|
|
176
|
-
self._model = model
|
|
182
|
+
self._model = self._validate_and_set_model(model)
|
|
183
|
+
self._guardrail = guardrail
|
|
177
184
|
self._language_model_id: str | None = None
|
|
178
185
|
# Extract parameters from kwargs with _UNSET defaults
|
|
179
186
|
self._timeout = kwargs.pop("timeout", Agent._UNSET) # type: ignore[assignment]
|
|
@@ -181,9 +188,25 @@ class Agent:
|
|
|
181
188
|
self._framework = kwargs.pop("framework", Agent._UNSET) # type: ignore[assignment]
|
|
182
189
|
self._version = kwargs.pop("version", Agent._UNSET) # type: ignore[assignment]
|
|
183
190
|
self._agent_type = kwargs.pop("agent_type", Agent._UNSET) # type: ignore[assignment]
|
|
191
|
+
|
|
192
|
+
# Handle 'type' as a legacy alias for 'agent_type'
|
|
193
|
+
legacy_type = kwargs.pop("type", Agent._UNSET)
|
|
194
|
+
if legacy_type is not Agent._UNSET:
|
|
195
|
+
warnings.warn(
|
|
196
|
+
"The 'type' parameter is deprecated and will be removed in a future version. Use 'agent_type' instead.",
|
|
197
|
+
DeprecationWarning,
|
|
198
|
+
stacklevel=2,
|
|
199
|
+
)
|
|
200
|
+
if self._agent_type is Agent._UNSET:
|
|
201
|
+
self._agent_type = legacy_type
|
|
202
|
+
|
|
184
203
|
self._agent_config = kwargs.pop("agent_config", Agent._UNSET) # type: ignore[assignment]
|
|
185
204
|
self._tool_configs = kwargs.pop("tool_configs", Agent._UNSET) # type: ignore[assignment]
|
|
186
|
-
|
|
205
|
+
mcp_configs = kwargs.pop("mcp_configs", Agent._UNSET)
|
|
206
|
+
if mcp_configs is not Agent._UNSET and isinstance(mcp_configs, dict):
|
|
207
|
+
self._mcp_configs = self._normalize_mcp_configs(mcp_configs)
|
|
208
|
+
else:
|
|
209
|
+
self._mcp_configs = mcp_configs # type: ignore[assignment]
|
|
187
210
|
self._a2a_profile = kwargs.pop("a2a_profile", Agent._UNSET) # type: ignore[assignment]
|
|
188
211
|
|
|
189
212
|
# Warn about unexpected kwargs
|
|
@@ -194,6 +217,30 @@ class Agent:
|
|
|
194
217
|
stacklevel=2,
|
|
195
218
|
)
|
|
196
219
|
|
|
220
|
+
def _validate_and_set_model(self, model: str | Any) -> str | Any:
|
|
221
|
+
"""Validate and normalize model parameter.
|
|
222
|
+
|
|
223
|
+
Supports both string model identifiers and Model objects:
|
|
224
|
+
- String: Simple model identifier (e.g., "openai/gpt-4o" or OpenAI.GPT_4O)
|
|
225
|
+
- Model: Model object with credentials/hyperparameters for local execution
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
model: Model identifier (string) or Model object.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Validated model (string or Model object).
|
|
232
|
+
"""
|
|
233
|
+
if model is None or model is Agent._UNSET:
|
|
234
|
+
return model
|
|
235
|
+
|
|
236
|
+
from glaip_sdk.models import Model # noqa: PLC0415
|
|
237
|
+
|
|
238
|
+
if isinstance(model, str):
|
|
239
|
+
return _validate_model(model)
|
|
240
|
+
elif isinstance(model, Model):
|
|
241
|
+
return model
|
|
242
|
+
return model
|
|
243
|
+
|
|
197
244
|
# ─────────────────────────────────────────────────────────────────
|
|
198
245
|
# Properties (override in subclasses OR pass to __init__)
|
|
199
246
|
# ─────────────────────────────────────────────────────────────────
|
|
@@ -338,11 +385,11 @@ class Agent:
|
|
|
338
385
|
return None
|
|
339
386
|
|
|
340
387
|
@property
|
|
341
|
-
def model(self) -> str | None:
|
|
388
|
+
def model(self) -> str | Model | None:
|
|
342
389
|
"""Optional model override.
|
|
343
390
|
|
|
344
391
|
Returns:
|
|
345
|
-
Model identifier string or None to use default.
|
|
392
|
+
Model identifier string, Model object, or None to use default.
|
|
346
393
|
"""
|
|
347
394
|
if self._model is not self._UNSET:
|
|
348
395
|
return self._model
|
|
@@ -453,6 +500,11 @@ class Agent:
|
|
|
453
500
|
return self._mcp_configs
|
|
454
501
|
return None
|
|
455
502
|
|
|
503
|
+
@property
|
|
504
|
+
def guardrail(self) -> GuardrailManager | None:
|
|
505
|
+
"""The guardrail manager for content safety."""
|
|
506
|
+
return self._guardrail
|
|
507
|
+
|
|
456
508
|
@property
|
|
457
509
|
def a2a_profile(self) -> dict[str, Any] | None:
|
|
458
510
|
"""A2A (Agent-to-Agent) profile configuration.
|
|
@@ -513,6 +565,8 @@ class Agent:
|
|
|
513
565
|
from glaip_sdk.utils.client import get_client # noqa: PLC0415
|
|
514
566
|
|
|
515
567
|
client = get_client()
|
|
568
|
+
from glaip_sdk.utils.discovery import find_agent # noqa: PLC0415
|
|
569
|
+
|
|
516
570
|
response = self._create_or_update_agent(config, client, find_agent)
|
|
517
571
|
|
|
518
572
|
# Update self with deployed info
|
|
@@ -538,19 +592,33 @@ class Agent:
|
|
|
538
592
|
"framework": self.framework,
|
|
539
593
|
"version": self.version,
|
|
540
594
|
"agent_type": self.agent_type,
|
|
541
|
-
"model": self.model,
|
|
542
595
|
}
|
|
543
596
|
|
|
597
|
+
if self.model:
|
|
598
|
+
if isinstance(self.model, str):
|
|
599
|
+
config["model"] = self.model
|
|
600
|
+
else:
|
|
601
|
+
config["model"] = self.model.id
|
|
602
|
+
|
|
544
603
|
# Handle metadata (default to empty dict if None)
|
|
545
604
|
config["metadata"] = self.metadata or {}
|
|
546
605
|
|
|
547
606
|
# Handle agent_config with timeout
|
|
548
607
|
# The timeout property is a convenience that maps to agent_config.execution_timeout
|
|
549
|
-
|
|
608
|
+
raw_config = self.agent_config if self.agent_config is not self._UNSET else {}
|
|
609
|
+
agent_config = dict(raw_config) if raw_config else {}
|
|
610
|
+
|
|
550
611
|
if self.timeout and "execution_timeout" not in agent_config:
|
|
551
612
|
agent_config["execution_timeout"] = self.timeout
|
|
552
|
-
|
|
553
|
-
|
|
613
|
+
|
|
614
|
+
if self.guardrail:
|
|
615
|
+
from glaip_sdk.guardrails.serializer import ( # noqa: PLC0415
|
|
616
|
+
serialize_guardrail_manager,
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
agent_config["guardrails"] = serialize_guardrail_manager(self.guardrail)
|
|
620
|
+
|
|
621
|
+
config["agent_config"] = agent_config
|
|
554
622
|
|
|
555
623
|
# Handle tool_configs - resolve tool names/classes to IDs
|
|
556
624
|
if self.tool_configs:
|
|
@@ -582,11 +650,20 @@ class Agent:
|
|
|
582
650
|
|
|
583
651
|
Returns:
|
|
584
652
|
List of resolved MCP IDs for the API payload.
|
|
653
|
+
|
|
654
|
+
Raises:
|
|
655
|
+
ValueError: If an MCP fails to resolve to a valid ID.
|
|
585
656
|
"""
|
|
586
657
|
if not self.mcps:
|
|
587
658
|
return []
|
|
588
659
|
|
|
589
|
-
|
|
660
|
+
resolved_ids: list[str] = []
|
|
661
|
+
for mcp_ref in self.mcps:
|
|
662
|
+
mcp = registry.resolve(mcp_ref)
|
|
663
|
+
if not mcp.id:
|
|
664
|
+
raise ValueError(f"Failed to resolve ID for MCP: {mcp_ref}")
|
|
665
|
+
resolved_ids.append(mcp.id)
|
|
666
|
+
return resolved_ids
|
|
590
667
|
|
|
591
668
|
def _resolve_tools(self, registry: ToolRegistry) -> list[str]:
|
|
592
669
|
"""Resolve tool references to IDs using ToolRegistry.
|
|
@@ -600,12 +677,20 @@ class Agent:
|
|
|
600
677
|
|
|
601
678
|
Returns:
|
|
602
679
|
List of resolved tool IDs for the API payload.
|
|
680
|
+
|
|
681
|
+
Raises:
|
|
682
|
+
ValueError: If a tool fails to resolve to a valid ID.
|
|
603
683
|
"""
|
|
604
684
|
if not self.tools:
|
|
605
685
|
return []
|
|
606
686
|
|
|
607
|
-
|
|
608
|
-
|
|
687
|
+
resolved_ids: list[str] = []
|
|
688
|
+
for tool_ref in self.tools:
|
|
689
|
+
tool = registry.resolve(tool_ref)
|
|
690
|
+
if not tool.id:
|
|
691
|
+
raise ValueError(f"Failed to resolve ID for tool: {tool_ref}")
|
|
692
|
+
resolved_ids.append(tool.id)
|
|
693
|
+
return resolved_ids
|
|
609
694
|
|
|
610
695
|
def _resolve_tool_configs(self, registry: ToolRegistry) -> dict[str, Any]:
|
|
611
696
|
"""Resolve tool_configs keys from tool names/classes to tool IDs.
|
|
@@ -648,6 +733,8 @@ class Agent:
|
|
|
648
733
|
try:
|
|
649
734
|
# Resolve key (tool name/class) to Tool object, get ID
|
|
650
735
|
tool = registry.resolve(key)
|
|
736
|
+
if not tool.id:
|
|
737
|
+
raise ValueError(f"Resolved tool has no ID: {key}")
|
|
651
738
|
resolved[tool.id] = config
|
|
652
739
|
except (ValueError, KeyError) as e:
|
|
653
740
|
raise ValueError(f"Failed to resolve tool config key: {key}") from e
|
|
@@ -683,6 +770,8 @@ class Agent:
|
|
|
683
770
|
resolved_id = key
|
|
684
771
|
else:
|
|
685
772
|
mcp = registry.resolve(key)
|
|
773
|
+
if not mcp.id:
|
|
774
|
+
raise ValueError(f"Resolved MCP has no ID: {key}")
|
|
686
775
|
resolved_id = mcp.id
|
|
687
776
|
|
|
688
777
|
if resolved_id in resolved:
|
|
@@ -698,7 +787,48 @@ class Agent:
|
|
|
698
787
|
|
|
699
788
|
return resolved
|
|
700
789
|
|
|
701
|
-
def
|
|
790
|
+
def _normalize_mcp_configs(self, mcp_configs: dict[Any, Any]) -> dict[Any, Any]:
|
|
791
|
+
"""Normalize mcp_configs by wrapping misplaced transport keys in 'config'.
|
|
792
|
+
|
|
793
|
+
This ensures that flat transport settings (e.g. {'url': '...'}) provided
|
|
794
|
+
by the user are correctly moved into the 'config' block required by the
|
|
795
|
+
Platform, ensuring parity between local and remote execution.
|
|
796
|
+
|
|
797
|
+
Args:
|
|
798
|
+
mcp_configs: The raw mcp_configs dictionary.
|
|
799
|
+
|
|
800
|
+
Returns:
|
|
801
|
+
Normalized mcp_configs dictionary.
|
|
802
|
+
"""
|
|
803
|
+
from glaip_sdk.runner.langgraph import _MCP_TRANSPORT_KEYS # noqa: PLC0415
|
|
804
|
+
|
|
805
|
+
normalized = {}
|
|
806
|
+
for mcp_key, override in mcp_configs.items():
|
|
807
|
+
if not isinstance(override, dict):
|
|
808
|
+
normalized[mcp_key] = override
|
|
809
|
+
continue
|
|
810
|
+
|
|
811
|
+
misplaced = {k: v for k, v in override.items() if k in _MCP_TRANSPORT_KEYS}
|
|
812
|
+
|
|
813
|
+
if misplaced:
|
|
814
|
+
new_override = override.copy()
|
|
815
|
+
config_block = new_override.get("config", {})
|
|
816
|
+
if not isinstance(config_block, dict):
|
|
817
|
+
config_block = {}
|
|
818
|
+
|
|
819
|
+
config_block.update(misplaced)
|
|
820
|
+
new_override["config"] = config_block
|
|
821
|
+
|
|
822
|
+
for k in misplaced:
|
|
823
|
+
new_override.pop(k, None)
|
|
824
|
+
|
|
825
|
+
normalized[mcp_key] = new_override
|
|
826
|
+
else:
|
|
827
|
+
normalized[mcp_key] = override
|
|
828
|
+
|
|
829
|
+
return normalized
|
|
830
|
+
|
|
831
|
+
def _resolve_agents(self, registry: AgentRegistry) -> list[str]:
|
|
702
832
|
"""Resolve sub-agent references using AgentRegistry.
|
|
703
833
|
|
|
704
834
|
Uses the global AgentRegistry to cache Agent objects across deployments.
|
|
@@ -710,12 +840,20 @@ class Agent:
|
|
|
710
840
|
|
|
711
841
|
Returns:
|
|
712
842
|
List of resolved agent IDs for the API payload.
|
|
843
|
+
|
|
844
|
+
Raises:
|
|
845
|
+
ValueError: If an agent fails to resolve to a valid ID.
|
|
713
846
|
"""
|
|
714
847
|
if not self.agents:
|
|
715
848
|
return []
|
|
716
849
|
|
|
717
|
-
|
|
718
|
-
|
|
850
|
+
resolved_ids: list[str] = []
|
|
851
|
+
for agent_ref in self.agents:
|
|
852
|
+
agent = registry.resolve(agent_ref)
|
|
853
|
+
if not agent.id:
|
|
854
|
+
raise ValueError(f"Failed to resolve ID for agent: {agent_ref}")
|
|
855
|
+
resolved_ids.append(agent.id)
|
|
856
|
+
return resolved_ids
|
|
719
857
|
|
|
720
858
|
def _create_or_update_agent(
|
|
721
859
|
self,
|
|
@@ -799,19 +937,42 @@ class Agent:
|
|
|
799
937
|
"""Return a dict representation of the Agent.
|
|
800
938
|
|
|
801
939
|
Provides Pydantic-style serialization for backward compatibility.
|
|
940
|
+
This implementation avoids triggering external tool resolution to ensure
|
|
941
|
+
it remains robust even when the environment is not fully configured.
|
|
802
942
|
|
|
803
943
|
Args:
|
|
804
944
|
exclude_none: If True, exclude None values from the output.
|
|
805
945
|
|
|
806
946
|
Returns:
|
|
807
|
-
Dictionary containing Agent attributes.
|
|
947
|
+
Dictionary containing Agent attributes. Note: Mutable fields (dicts, lists)
|
|
948
|
+
are returned as references. Modify with caution or make a deep copy if needed.
|
|
808
949
|
"""
|
|
950
|
+
# Map convenience timeout to agent_config if not already present
|
|
951
|
+
agent_config = self.agent_config if self.agent_config is not self._UNSET else {}
|
|
952
|
+
agent_config = dict(agent_config) if agent_config else {}
|
|
953
|
+
|
|
954
|
+
if self.timeout and "execution_timeout" not in agent_config:
|
|
955
|
+
agent_config["execution_timeout"] = self.timeout
|
|
956
|
+
|
|
957
|
+
# Handle guardrail serialization without full config build
|
|
958
|
+
if self.guardrail:
|
|
959
|
+
try:
|
|
960
|
+
from glaip_sdk.guardrails.serializer import ( # noqa: PLC0415
|
|
961
|
+
serialize_guardrail_manager,
|
|
962
|
+
)
|
|
963
|
+
|
|
964
|
+
agent_config["guardrails"] = serialize_guardrail_manager(self.guardrail)
|
|
965
|
+
except ImportError: # pragma: no cover
|
|
966
|
+
# Serializer not available (optional dependency); skip guardrail data
|
|
967
|
+
pass
|
|
968
|
+
|
|
809
969
|
data = {
|
|
810
970
|
"id": self._id,
|
|
811
971
|
"name": self.name,
|
|
812
972
|
"instruction": self.instruction,
|
|
813
973
|
"description": self.description,
|
|
814
|
-
"
|
|
974
|
+
"agent_type": self.agent_type,
|
|
975
|
+
"type": self.agent_type, # Legacy key for backward compatibility
|
|
815
976
|
"framework": self.framework,
|
|
816
977
|
"version": self.version,
|
|
817
978
|
"tools": self.tools,
|
|
@@ -819,13 +980,16 @@ class Agent:
|
|
|
819
980
|
"mcps": self.mcps,
|
|
820
981
|
"timeout": self.timeout,
|
|
821
982
|
"metadata": self.metadata,
|
|
822
|
-
"
|
|
983
|
+
"model": self.model,
|
|
984
|
+
"agent_config": agent_config,
|
|
823
985
|
"tool_configs": self.tool_configs,
|
|
824
986
|
"mcp_configs": self.mcp_configs,
|
|
825
987
|
"a2a_profile": self.a2a_profile,
|
|
988
|
+
"guardrail": self.guardrail,
|
|
826
989
|
"created_at": self._created_at,
|
|
827
990
|
"updated_at": self._updated_at,
|
|
828
991
|
}
|
|
992
|
+
|
|
829
993
|
if exclude_none:
|
|
830
994
|
return {k: v for k, v in data.items() if v is not None}
|
|
831
995
|
return data
|
|
@@ -842,6 +1006,36 @@ class Agent:
|
|
|
842
1006
|
self._client = client
|
|
843
1007
|
return self
|
|
844
1008
|
|
|
1009
|
+
@property
|
|
1010
|
+
def schedule(self) -> AgentScheduleManager:
|
|
1011
|
+
"""Get the schedule manager for this agent.
|
|
1012
|
+
|
|
1013
|
+
Provides a convenient interface for managing schedules scoped to this agent.
|
|
1014
|
+
|
|
1015
|
+
Returns:
|
|
1016
|
+
AgentScheduleManager for schedule operations
|
|
1017
|
+
|
|
1018
|
+
Raises:
|
|
1019
|
+
ValueError: If agent is not deployed
|
|
1020
|
+
RuntimeError: If agent is not bound to a client
|
|
1021
|
+
|
|
1022
|
+
Example:
|
|
1023
|
+
>>> agent = client.get_agent_by_id("agent-id")
|
|
1024
|
+
>>> schedules = agent.schedule.list()
|
|
1025
|
+
>>> new_schedule = agent.schedule.create(
|
|
1026
|
+
... input="Daily task",
|
|
1027
|
+
... schedule="0 9 * * 1-5"
|
|
1028
|
+
... )
|
|
1029
|
+
"""
|
|
1030
|
+
if not self.id:
|
|
1031
|
+
raise ValueError(_AGENT_NOT_DEPLOYED_MSG)
|
|
1032
|
+
if not self._client:
|
|
1033
|
+
raise RuntimeError(_CLIENT_NOT_AVAILABLE_MSG)
|
|
1034
|
+
|
|
1035
|
+
from glaip_sdk.client.schedules import AgentScheduleManager # noqa: PLC0415
|
|
1036
|
+
|
|
1037
|
+
return AgentScheduleManager(self, self._client.schedules)
|
|
1038
|
+
|
|
845
1039
|
def _prepare_run_kwargs(
|
|
846
1040
|
self,
|
|
847
1041
|
message: str,
|
|
@@ -864,7 +1058,7 @@ class Agent:
|
|
|
864
1058
|
ValueError: If the agent hasn't been deployed yet.
|
|
865
1059
|
RuntimeError: If client is not available.
|
|
866
1060
|
"""
|
|
867
|
-
if not self.id:
|
|
1061
|
+
if not self.id: # pragma: no cover - defensive: called only when self.id is truthy
|
|
868
1062
|
raise ValueError(_AGENT_NOT_DEPLOYED_MSG)
|
|
869
1063
|
if not self._client:
|
|
870
1064
|
raise RuntimeError(_CLIENT_NOT_AVAILABLE_MSG)
|
|
@@ -878,6 +1072,10 @@ class Agent:
|
|
|
878
1072
|
}
|
|
879
1073
|
|
|
880
1074
|
if runtime_config is not None:
|
|
1075
|
+
from glaip_sdk.utils.runtime_config import ( # noqa: PLC0415
|
|
1076
|
+
normalize_runtime_config_keys,
|
|
1077
|
+
)
|
|
1078
|
+
|
|
881
1079
|
call_kwargs["runtime_config"] = normalize_runtime_config_keys(
|
|
882
1080
|
runtime_config,
|
|
883
1081
|
tool_registry=get_tool_registry(),
|
|
@@ -888,18 +1086,87 @@ class Agent:
|
|
|
888
1086
|
call_kwargs.update(kwargs)
|
|
889
1087
|
return agent_client, call_kwargs
|
|
890
1088
|
|
|
1089
|
+
def _get_local_runner_or_raise(self) -> Any:
|
|
1090
|
+
"""Get the local runner if available, otherwise raise ValueError.
|
|
1091
|
+
|
|
1092
|
+
Returns:
|
|
1093
|
+
The default local runner instance.
|
|
1094
|
+
|
|
1095
|
+
Raises:
|
|
1096
|
+
ValueError: If local runtime is not available.
|
|
1097
|
+
"""
|
|
1098
|
+
from glaip_sdk.runner import get_default_runner # noqa: PLC0415
|
|
1099
|
+
from glaip_sdk.runner.deps import ( # noqa: PLC0415
|
|
1100
|
+
check_local_runtime_available,
|
|
1101
|
+
get_local_runtime_missing_message,
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
if check_local_runtime_available():
|
|
1105
|
+
return get_default_runner()
|
|
1106
|
+
|
|
1107
|
+
# If agent is not deployed, it *must* use local runtime
|
|
1108
|
+
if not self.id:
|
|
1109
|
+
raise ValueError(f"{_AGENT_NOT_DEPLOYED_MSG}\n\n{get_local_runtime_missing_message()}")
|
|
1110
|
+
|
|
1111
|
+
# If agent IS deployed but local execution was forced (local=True)
|
|
1112
|
+
raise ValueError(
|
|
1113
|
+
f"Local execution override was requested, but local runtime is missing.\n\n"
|
|
1114
|
+
f"{get_local_runtime_missing_message()}"
|
|
1115
|
+
)
|
|
1116
|
+
|
|
1117
|
+
def _prepare_local_runner_kwargs(
|
|
1118
|
+
self,
|
|
1119
|
+
message: str,
|
|
1120
|
+
verbose: bool,
|
|
1121
|
+
runtime_config: dict[str, Any] | None,
|
|
1122
|
+
chat_history: list[dict[str, str]] | None,
|
|
1123
|
+
**kwargs: Any,
|
|
1124
|
+
) -> dict[str, Any]:
|
|
1125
|
+
"""Prepare kwargs for local runner execution.
|
|
1126
|
+
|
|
1127
|
+
Args:
|
|
1128
|
+
message: The message to send to the agent.
|
|
1129
|
+
verbose: If True, print streaming output to console.
|
|
1130
|
+
runtime_config: Optional runtime configuration.
|
|
1131
|
+
chat_history: Optional list of prior conversation messages.
|
|
1132
|
+
**kwargs: Additional arguments.
|
|
1133
|
+
|
|
1134
|
+
Returns:
|
|
1135
|
+
Dictionary of prepared kwargs for runner.run() or runner.arun().
|
|
1136
|
+
"""
|
|
1137
|
+
return {
|
|
1138
|
+
"agent": self,
|
|
1139
|
+
"message": message,
|
|
1140
|
+
"verbose": verbose,
|
|
1141
|
+
"runtime_config": runtime_config,
|
|
1142
|
+
"chat_history": chat_history,
|
|
1143
|
+
**kwargs,
|
|
1144
|
+
}
|
|
1145
|
+
|
|
891
1146
|
def run(
|
|
892
1147
|
self,
|
|
893
1148
|
message: str,
|
|
894
1149
|
verbose: bool = False,
|
|
1150
|
+
local: bool = False,
|
|
895
1151
|
runtime_config: dict[str, Any] | None = None,
|
|
1152
|
+
chat_history: list[dict[str, str]] | None = None,
|
|
896
1153
|
**kwargs: Any,
|
|
897
1154
|
) -> str:
|
|
898
1155
|
"""Run the agent synchronously with a message.
|
|
899
1156
|
|
|
1157
|
+
Supports two execution modes:
|
|
1158
|
+
- **Server-backed**: When the agent is deployed (has an ID), execution
|
|
1159
|
+
happens via the AIP backend server.
|
|
1160
|
+
- **Local**: When the agent is not deployed and glaip-sdk[local] is installed,
|
|
1161
|
+
execution happens locally via aip-agents (no server required).
|
|
1162
|
+
|
|
1163
|
+
You can force local execution for a deployed agent by passing `local=True`.
|
|
1164
|
+
|
|
900
1165
|
Args:
|
|
901
1166
|
message: The message to send to the agent.
|
|
902
|
-
verbose: If True, print streaming output to console.
|
|
1167
|
+
verbose: If True, print streaming output to console. Defaults to False.
|
|
1168
|
+
local: If True, force local execution even if the agent is deployed.
|
|
1169
|
+
Defaults to False.
|
|
903
1170
|
runtime_config: Optional runtime configuration for tools, MCPs, and agents.
|
|
904
1171
|
Keys can be SDK objects, UUIDs, or names. Example:
|
|
905
1172
|
{
|
|
@@ -907,32 +1174,61 @@ class Agent:
|
|
|
907
1174
|
"mcp_configs": {"mcp-id": {"setting": "on"}},
|
|
908
1175
|
"agent_config": {"planning": True},
|
|
909
1176
|
}
|
|
1177
|
+
Defaults to None.
|
|
1178
|
+
chat_history: Optional list of prior conversation messages for context.
|
|
1179
|
+
Each message is a dict with "role" and "content" keys.
|
|
1180
|
+
Defaults to None.
|
|
910
1181
|
**kwargs: Additional arguments to pass to the run API.
|
|
911
1182
|
|
|
912
1183
|
Returns:
|
|
913
1184
|
The agent's response as a string.
|
|
914
1185
|
|
|
915
1186
|
Raises:
|
|
916
|
-
ValueError: If the agent
|
|
917
|
-
RuntimeError: If
|
|
1187
|
+
ValueError: If the agent is not deployed and local runtime is not available.
|
|
1188
|
+
RuntimeError: If server-backed execution fails due to client issues.
|
|
918
1189
|
"""
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1190
|
+
# Backend routing: deployed agents use server, undeployed use local (if available)
|
|
1191
|
+
if self.id and not local:
|
|
1192
|
+
# Server-backed execution path (agent is deployed)
|
|
1193
|
+
agent_client, call_kwargs = self._prepare_run_kwargs(
|
|
1194
|
+
message,
|
|
1195
|
+
verbose,
|
|
1196
|
+
runtime_config or kwargs.get("runtime_config"),
|
|
1197
|
+
**kwargs,
|
|
1198
|
+
)
|
|
1199
|
+
if chat_history is not None:
|
|
1200
|
+
call_kwargs["chat_history"] = chat_history
|
|
1201
|
+
return agent_client.run_agent(**call_kwargs)
|
|
1202
|
+
|
|
1203
|
+
# Local execution path (agent is not deployed OR local=True)
|
|
1204
|
+
runner = self._get_local_runner_or_raise()
|
|
1205
|
+
local_kwargs = self._prepare_local_runner_kwargs(message, verbose, runtime_config, chat_history, **kwargs)
|
|
1206
|
+
return runner.run(**local_kwargs)
|
|
923
1207
|
|
|
924
1208
|
async def arun(
|
|
925
1209
|
self,
|
|
926
1210
|
message: str,
|
|
927
1211
|
verbose: bool = False,
|
|
1212
|
+
local: bool = False,
|
|
928
1213
|
runtime_config: dict[str, Any] | None = None,
|
|
1214
|
+
chat_history: list[dict[str, str]] | None = None,
|
|
929
1215
|
**kwargs: Any,
|
|
930
1216
|
) -> AsyncGenerator[dict, None]:
|
|
931
1217
|
"""Run the agent asynchronously with streaming output.
|
|
932
1218
|
|
|
1219
|
+
Supports two execution modes:
|
|
1220
|
+
- **Server-backed**: When the agent is deployed (has an ID), execution
|
|
1221
|
+
happens via the AIP backend server with streaming.
|
|
1222
|
+
- **Local**: When the agent is not deployed and glaip-sdk[local] is installed,
|
|
1223
|
+
execution happens locally via aip-agents (no server required).
|
|
1224
|
+
|
|
1225
|
+
You can force local execution for a deployed agent by passing `local=True`.
|
|
1226
|
+
|
|
933
1227
|
Args:
|
|
934
1228
|
message: The message to send to the agent.
|
|
935
|
-
verbose: If True, print streaming output to console.
|
|
1229
|
+
verbose: If True, print streaming output to console. Defaults to False.
|
|
1230
|
+
local: If True, force local execution even if the agent is deployed.
|
|
1231
|
+
Defaults to False.
|
|
936
1232
|
runtime_config: Optional runtime configuration for tools, MCPs, and agents.
|
|
937
1233
|
Keys can be SDK objects, UUIDs, or names. Example:
|
|
938
1234
|
{
|
|
@@ -940,20 +1236,47 @@ class Agent:
|
|
|
940
1236
|
"mcp_configs": {"mcp-id": {"setting": "on"}},
|
|
941
1237
|
"agent_config": {"planning": True},
|
|
942
1238
|
}
|
|
1239
|
+
Defaults to None.
|
|
1240
|
+
chat_history: Optional list of prior conversation messages for context.
|
|
1241
|
+
Each message is a dict with "role" and "content" keys.
|
|
1242
|
+
Defaults to None.
|
|
943
1243
|
**kwargs: Additional arguments to pass to the run API.
|
|
944
1244
|
|
|
945
1245
|
Yields:
|
|
946
1246
|
Streaming response chunks from the agent.
|
|
947
1247
|
|
|
948
1248
|
Raises:
|
|
949
|
-
ValueError: If the agent
|
|
950
|
-
RuntimeError: If
|
|
1249
|
+
ValueError: If the agent is not deployed and local runtime is not available.
|
|
1250
|
+
RuntimeError: If server-backed execution fails due to client issues.
|
|
951
1251
|
"""
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1252
|
+
# Backend routing: deployed agents use server, undeployed use local (if available)
|
|
1253
|
+
if self.id and not local:
|
|
1254
|
+
# Server-backed execution path (agent is deployed)
|
|
1255
|
+
agent_client, call_kwargs = self._prepare_run_kwargs(
|
|
1256
|
+
message,
|
|
1257
|
+
verbose,
|
|
1258
|
+
runtime_config or kwargs.get("runtime_config"),
|
|
1259
|
+
**kwargs,
|
|
1260
|
+
)
|
|
1261
|
+
if chat_history is not None:
|
|
1262
|
+
call_kwargs["chat_history"] = chat_history
|
|
1263
|
+
|
|
1264
|
+
async for chunk in agent_client.arun_agent(**call_kwargs):
|
|
1265
|
+
yield chunk
|
|
1266
|
+
return
|
|
1267
|
+
|
|
1268
|
+
# Local execution path (agent is not deployed OR local=True)
|
|
1269
|
+
runner = self._get_local_runner_or_raise()
|
|
1270
|
+
local_kwargs = self._prepare_local_runner_kwargs(message, verbose, runtime_config, chat_history, **kwargs)
|
|
1271
|
+
result = await runner.arun(**local_kwargs)
|
|
1272
|
+
# Yield a final_response event for consistency with server-backed execution
|
|
1273
|
+
# Include event_type for A2A event shape parity
|
|
1274
|
+
yield {
|
|
1275
|
+
"event_type": "final_response",
|
|
1276
|
+
"metadata": {"kind": "final_response"},
|
|
1277
|
+
"content": result,
|
|
1278
|
+
"is_final": True,
|
|
1279
|
+
}
|
|
957
1280
|
|
|
958
1281
|
def update(self, **kwargs: Any) -> Agent:
|
|
959
1282
|
"""Update the deployed agent with new configuration.
|