glaip-sdk 0.7.13__py3-none-any.whl → 0.7.15__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/agents/base.py +86 -9
- glaip_sdk/cli/commands/agents/__init__.py +14 -17
- glaip_sdk/cli/commands/agents/_common.py +6 -5
- glaip_sdk/cli/commands/agents/create.py +9 -5
- glaip_sdk/client/agents.py +247 -15
- glaip_sdk/client/base.py +25 -0
- glaip_sdk/client/main.py +0 -4
- glaip_sdk/client/payloads/agent/requests.py +6 -1
- glaip_sdk/config/constants.py +22 -2
- glaip_sdk/models/__init__.py +30 -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/constants.py +141 -0
- glaip_sdk/models/model.py +170 -0
- glaip_sdk/runner/langgraph.py +212 -30
- glaip_sdk/utils/agent_config.py +8 -2
- glaip_sdk/utils/runtime_config.py +3 -2
- {glaip_sdk-0.7.13.dist-info → glaip_sdk-0.7.15.dist-info}/METADATA +1 -1
- {glaip_sdk-0.7.13.dist-info → glaip_sdk-0.7.15.dist-info}/RECORD +23 -19
- {glaip_sdk-0.7.13.dist-info → glaip_sdk-0.7.15.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.7.13.dist-info → glaip_sdk-0.7.15.dist-info}/entry_points.txt +0 -0
- {glaip_sdk-0.7.13.dist-info → glaip_sdk-0.7.15.dist-info}/top_level.txt +0 -0
glaip_sdk/agents/base.py
CHANGED
|
@@ -54,10 +54,13 @@ from glaip_sdk.utils.resource_refs import is_uuid
|
|
|
54
54
|
|
|
55
55
|
if TYPE_CHECKING:
|
|
56
56
|
from glaip_sdk.client.schedules import AgentScheduleManager
|
|
57
|
+
from glaip_sdk.models import AgentResponse, Model
|
|
57
58
|
from glaip_sdk.guardrails import GuardrailManager
|
|
58
|
-
from glaip_sdk.models import AgentResponse
|
|
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,7 @@ 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]
|
|
134
137
|
guardrail: GuardrailManager | None = None,
|
|
135
138
|
_client: Any = None,
|
|
136
139
|
**kwargs: Any,
|
|
@@ -148,7 +151,7 @@ class Agent:
|
|
|
148
151
|
tools: List of tools (Tool classes, SDK Tool objects, or strings).
|
|
149
152
|
agents: List of sub-agents (Agent classes, instances, or strings).
|
|
150
153
|
mcps: List of MCPs.
|
|
151
|
-
model: Model identifier.
|
|
154
|
+
model: Model identifier or Model configuration object.
|
|
152
155
|
guardrail: The guardrail manager for content safety.
|
|
153
156
|
_client: Internal client reference (set automatically).
|
|
154
157
|
|
|
@@ -176,7 +179,7 @@ class Agent:
|
|
|
176
179
|
self._tools = tools
|
|
177
180
|
self._agents = agents
|
|
178
181
|
self._mcps = mcps
|
|
179
|
-
self._model = model
|
|
182
|
+
self._model = self._validate_and_set_model(model)
|
|
180
183
|
self._guardrail = guardrail
|
|
181
184
|
self._language_model_id: str | None = None
|
|
182
185
|
# Extract parameters from kwargs with _UNSET defaults
|
|
@@ -199,7 +202,11 @@ class Agent:
|
|
|
199
202
|
|
|
200
203
|
self._agent_config = kwargs.pop("agent_config", Agent._UNSET) # type: ignore[assignment]
|
|
201
204
|
self._tool_configs = kwargs.pop("tool_configs", Agent._UNSET) # type: ignore[assignment]
|
|
202
|
-
|
|
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]
|
|
203
210
|
self._a2a_profile = kwargs.pop("a2a_profile", Agent._UNSET) # type: ignore[assignment]
|
|
204
211
|
|
|
205
212
|
# Warn about unexpected kwargs
|
|
@@ -210,6 +217,30 @@ class Agent:
|
|
|
210
217
|
stacklevel=2,
|
|
211
218
|
)
|
|
212
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
|
+
|
|
213
244
|
# ─────────────────────────────────────────────────────────────────
|
|
214
245
|
# Properties (override in subclasses OR pass to __init__)
|
|
215
246
|
# ─────────────────────────────────────────────────────────────────
|
|
@@ -354,11 +385,11 @@ class Agent:
|
|
|
354
385
|
return None
|
|
355
386
|
|
|
356
387
|
@property
|
|
357
|
-
def model(self) -> str | None:
|
|
388
|
+
def model(self) -> str | Model | None:
|
|
358
389
|
"""Optional model override.
|
|
359
390
|
|
|
360
391
|
Returns:
|
|
361
|
-
Model identifier string or None to use default.
|
|
392
|
+
Model identifier string, Model object, or None to use default.
|
|
362
393
|
"""
|
|
363
394
|
if self._model is not self._UNSET:
|
|
364
395
|
return self._model
|
|
@@ -561,9 +592,14 @@ class Agent:
|
|
|
561
592
|
"framework": self.framework,
|
|
562
593
|
"version": self.version,
|
|
563
594
|
"agent_type": self.agent_type,
|
|
564
|
-
"model": self.model,
|
|
565
595
|
}
|
|
566
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
|
+
|
|
567
603
|
# Handle metadata (default to empty dict if None)
|
|
568
604
|
config["metadata"] = self.metadata or {}
|
|
569
605
|
|
|
@@ -751,6 +787,47 @@ class Agent:
|
|
|
751
787
|
|
|
752
788
|
return resolved
|
|
753
789
|
|
|
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
|
+
|
|
754
831
|
def _resolve_agents(self, registry: AgentRegistry) -> list[str]:
|
|
755
832
|
"""Resolve sub-agent references using AgentRegistry.
|
|
756
833
|
|
|
@@ -11,37 +11,30 @@ Authors:
|
|
|
11
11
|
# Import from submodules
|
|
12
12
|
from glaip_sdk.cli.commands.agents._common import ( # noqa: E402
|
|
13
13
|
AGENT_NOT_FOUND_ERROR,
|
|
14
|
-
_get_agent_for_update,
|
|
15
|
-
_resolve_agent,
|
|
16
|
-
agents_group,
|
|
17
14
|
_coerce_mapping_candidate,
|
|
18
15
|
_display_agent_details,
|
|
19
16
|
_emit_verbose_guidance,
|
|
20
17
|
_fetch_full_agent_details,
|
|
18
|
+
_get_agent_for_update,
|
|
21
19
|
_get_agent_model_name,
|
|
22
20
|
_get_language_model_display_name,
|
|
23
21
|
_model_from_config,
|
|
24
22
|
_prepare_agent_output,
|
|
23
|
+
_resolve_agent,
|
|
25
24
|
_resolve_resources_by_name,
|
|
25
|
+
agents_group,
|
|
26
26
|
console,
|
|
27
27
|
)
|
|
28
28
|
from glaip_sdk.cli.commands.agents.create import create # noqa: E402
|
|
29
29
|
from glaip_sdk.cli.commands.agents.delete import delete # noqa: E402
|
|
30
30
|
from glaip_sdk.cli.commands.agents.get import get # noqa: E402
|
|
31
31
|
from glaip_sdk.cli.commands.agents.list import list_agents # noqa: E402
|
|
32
|
-
from glaip_sdk.cli.commands.agents.run import
|
|
32
|
+
from glaip_sdk.cli.commands.agents.run import _maybe_attach_transcript_toggle, run # noqa: E402
|
|
33
33
|
from glaip_sdk.cli.commands.agents.sync_langflow import sync_langflow # noqa: E402
|
|
34
34
|
from glaip_sdk.cli.commands.agents.update import update # noqa: E402
|
|
35
35
|
|
|
36
36
|
# Import core functions for test compatibility
|
|
37
37
|
from glaip_sdk.cli.core.context import get_client # noqa: E402
|
|
38
|
-
from glaip_sdk.cli.core.rendering import with_client_and_spinner # noqa: E402
|
|
39
|
-
|
|
40
|
-
# Import display functions for test compatibility
|
|
41
|
-
from glaip_sdk.cli.display import ( # noqa: E402
|
|
42
|
-
handle_json_output,
|
|
43
|
-
handle_rich_output,
|
|
44
|
-
)
|
|
45
38
|
|
|
46
39
|
# Import core output functions for test compatibility
|
|
47
40
|
from glaip_sdk.cli.core.output import ( # noqa: E402
|
|
@@ -52,11 +45,20 @@ from glaip_sdk.cli.core.output import ( # noqa: E402
|
|
|
52
45
|
# Import rendering functions for test compatibility
|
|
53
46
|
from glaip_sdk.cli.core.rendering import ( # noqa: E402
|
|
54
47
|
build_renderer,
|
|
48
|
+
with_client_and_spinner, # noqa: E402
|
|
55
49
|
)
|
|
56
50
|
|
|
57
51
|
# Import display functions for test compatibility
|
|
58
|
-
|
|
52
|
+
# Import display functions for test compatibility
|
|
53
|
+
from glaip_sdk.cli.display import ( # noqa: E402 # noqa: E402
|
|
59
54
|
display_agent_run_suggestions,
|
|
55
|
+
handle_json_output,
|
|
56
|
+
handle_rich_output,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Import IO functions for test compatibility
|
|
60
|
+
from glaip_sdk.cli.io import ( # noqa: E402
|
|
61
|
+
fetch_raw_resource_details,
|
|
60
62
|
)
|
|
61
63
|
|
|
62
64
|
# Import rich helpers for test compatibility
|
|
@@ -70,11 +72,6 @@ from glaip_sdk.cli.transcript import ( # noqa: E402
|
|
|
70
72
|
store_transcript_for_session,
|
|
71
73
|
)
|
|
72
74
|
|
|
73
|
-
# Import IO functions for test compatibility
|
|
74
|
-
from glaip_sdk.cli.io import ( # noqa: E402
|
|
75
|
-
fetch_raw_resource_details,
|
|
76
|
-
)
|
|
77
|
-
|
|
78
75
|
# Import utils for test compatibility
|
|
79
76
|
from glaip_sdk.utils import ( # noqa: E402
|
|
80
77
|
is_uuid,
|
|
@@ -19,8 +19,11 @@ from glaip_sdk.branding import (
|
|
|
19
19
|
WARNING_STYLE,
|
|
20
20
|
)
|
|
21
21
|
from glaip_sdk.cli.constants import DEFAULT_AGENT_INSTRUCTION_PREVIEW_LIMIT
|
|
22
|
-
from glaip_sdk.config.constants import AGENT_CONFIG_FIELDS, DEFAULT_MODEL
|
|
23
22
|
from glaip_sdk.cli.context import get_ctx_value
|
|
23
|
+
from glaip_sdk.cli.core.output import (
|
|
24
|
+
output_result,
|
|
25
|
+
)
|
|
26
|
+
from glaip_sdk.cli.core.rendering import spinner_context
|
|
24
27
|
from glaip_sdk.cli.display import (
|
|
25
28
|
build_resource_result_data,
|
|
26
29
|
handle_json_output,
|
|
@@ -31,10 +34,8 @@ from glaip_sdk.cli.hints import in_slash_mode
|
|
|
31
34
|
from glaip_sdk.cli.io import fetch_raw_resource_details
|
|
32
35
|
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
33
36
|
from glaip_sdk.cli.rich_helpers import markup_text
|
|
34
|
-
from glaip_sdk.
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
from glaip_sdk.cli.core.rendering import spinner_context
|
|
37
|
+
from glaip_sdk.config.constants import AGENT_CONFIG_FIELDS
|
|
38
|
+
from glaip_sdk.models.constants import DEFAULT_MODEL
|
|
38
39
|
from glaip_sdk.icons import ICON_AGENT
|
|
39
40
|
from glaip_sdk.utils import format_datetime, is_uuid
|
|
40
41
|
|
|
@@ -11,19 +11,19 @@ from typing import Any
|
|
|
11
11
|
import click
|
|
12
12
|
|
|
13
13
|
from glaip_sdk.cli.context import output_flags
|
|
14
|
+
from glaip_sdk.cli.core.context import get_client
|
|
14
15
|
from glaip_sdk.cli.display import (
|
|
15
16
|
display_agent_run_suggestions,
|
|
16
17
|
display_creation_success,
|
|
17
18
|
handle_json_output,
|
|
18
19
|
handle_rich_output,
|
|
19
20
|
)
|
|
20
|
-
from glaip_sdk.cli.core.context import get_client
|
|
21
|
-
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT, DEFAULT_MODEL
|
|
22
21
|
from glaip_sdk.cli.validators import (
|
|
23
22
|
validate_agent_instruction_cli as validate_agent_instruction,
|
|
24
|
-
validate_agent_name_cli as validate_agent_name,
|
|
25
|
-
validate_timeout_cli as validate_timeout,
|
|
26
23
|
)
|
|
24
|
+
from glaip_sdk.cli.validators import validate_agent_name_cli as validate_agent_name
|
|
25
|
+
from glaip_sdk.cli.validators import validate_timeout_cli as validate_timeout
|
|
26
|
+
from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
|
|
27
27
|
from glaip_sdk.utils.validation import coerce_timeout
|
|
28
28
|
|
|
29
29
|
from ._common import (
|
|
@@ -67,7 +67,11 @@ def _handle_creation_exception(ctx: Any, e: Exception) -> None:
|
|
|
67
67
|
@click.option("--instruction", help="Agent instruction (prompt)")
|
|
68
68
|
@click.option(
|
|
69
69
|
"--model",
|
|
70
|
-
help=
|
|
70
|
+
help=(
|
|
71
|
+
"Language model in 'provider/model' format "
|
|
72
|
+
"(e.g., openai/gpt-4o-mini, bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0). "
|
|
73
|
+
"Use 'aip models list' to see available models."
|
|
74
|
+
),
|
|
71
75
|
)
|
|
72
76
|
@click.option("--tools", multiple=True, help="Tool names or IDs to attach")
|
|
73
77
|
@click.option("--agents", multiple=True, help="Sub-agent names or IDs to attach")
|
glaip_sdk/client/agents.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# pylint: disable=duplicate-code
|
|
2
2
|
"""Agent client for AIP SDK.
|
|
3
3
|
|
|
4
4
|
Authors:
|
|
@@ -9,6 +9,7 @@ Authors:
|
|
|
9
9
|
import asyncio
|
|
10
10
|
import json
|
|
11
11
|
import logging
|
|
12
|
+
import warnings
|
|
12
13
|
from collections.abc import AsyncGenerator, Callable, Iterator, Mapping
|
|
13
14
|
from contextlib import asynccontextmanager
|
|
14
15
|
from os import PathLike
|
|
@@ -21,15 +22,15 @@ if TYPE_CHECKING:
|
|
|
21
22
|
|
|
22
23
|
import httpx
|
|
23
24
|
from glaip_sdk.agents import Agent
|
|
25
|
+
from glaip_sdk.client.agent_runs import AgentRunsClient
|
|
26
|
+
from glaip_sdk.client.base import BaseClient
|
|
27
|
+
from glaip_sdk.client.mcps import MCPClient
|
|
24
28
|
from glaip_sdk.client.payloads.agent import (
|
|
25
29
|
AgentCreateRequest,
|
|
26
30
|
AgentListParams,
|
|
27
31
|
AgentListResult,
|
|
28
32
|
AgentUpdateRequest,
|
|
29
33
|
)
|
|
30
|
-
from glaip_sdk.client.agent_runs import AgentRunsClient
|
|
31
|
-
from glaip_sdk.client.base import BaseClient
|
|
32
|
-
from glaip_sdk.client.mcps import MCPClient
|
|
33
34
|
from glaip_sdk.client.run_rendering import (
|
|
34
35
|
AgentRunRenderingManager,
|
|
35
36
|
compute_timeout_seconds,
|
|
@@ -42,10 +43,10 @@ from glaip_sdk.config.constants import (
|
|
|
42
43
|
DEFAULT_AGENT_RUN_TIMEOUT,
|
|
43
44
|
DEFAULT_AGENT_TYPE,
|
|
44
45
|
DEFAULT_AGENT_VERSION,
|
|
45
|
-
DEFAULT_MODEL,
|
|
46
46
|
)
|
|
47
47
|
from glaip_sdk.exceptions import NotFoundError, ValidationError
|
|
48
48
|
from glaip_sdk.models import AgentResponse
|
|
49
|
+
from glaip_sdk.models.constants import DEFAULT_MODEL
|
|
49
50
|
from glaip_sdk.payload_schemas.agent import list_server_only_fields
|
|
50
51
|
from glaip_sdk.utils.agent_config import normalize_agent_config_for_import
|
|
51
52
|
from glaip_sdk.utils.client_utils import (
|
|
@@ -255,13 +256,16 @@ class AgentClient(BaseClient):
|
|
|
255
256
|
self,
|
|
256
257
|
*,
|
|
257
258
|
parent_client: BaseClient | None = None,
|
|
259
|
+
lm_cache_ttl: float = 3600.0,
|
|
258
260
|
**kwargs: Any,
|
|
259
261
|
) -> None:
|
|
260
262
|
"""Initialize the agent client.
|
|
261
263
|
|
|
262
264
|
Args:
|
|
263
|
-
parent_client: Parent client to adopt session/config from
|
|
264
|
-
|
|
265
|
+
parent_client: Parent client to adopt session/config from.
|
|
266
|
+
lm_cache_ttl: TTL for the language model list cache in seconds.
|
|
267
|
+
Defaults to 3600 (1 hour).
|
|
268
|
+
**kwargs: Additional arguments for standalone initialization.
|
|
265
269
|
"""
|
|
266
270
|
super().__init__(parent_client=parent_client, **kwargs)
|
|
267
271
|
self._renderer_manager = AgentRunRenderingManager(logger)
|
|
@@ -270,6 +274,117 @@ class AgentClient(BaseClient):
|
|
|
270
274
|
self._runs_client: AgentRunsClient | None = None
|
|
271
275
|
self._schedule_client: ScheduleClient | None = None
|
|
272
276
|
|
|
277
|
+
self._lm_cache: list[dict[str, Any]] | None = None
|
|
278
|
+
self._lm_cache_time: float = 0.0
|
|
279
|
+
self._lm_cache_ttl: float = lm_cache_ttl
|
|
280
|
+
|
|
281
|
+
def clear_language_model_cache(self) -> None:
|
|
282
|
+
"""Invalidate the language model list cache.
|
|
283
|
+
|
|
284
|
+
Forces the next call to list_language_models() to fetch a fresh list
|
|
285
|
+
from the server.
|
|
286
|
+
"""
|
|
287
|
+
self._lm_cache = None
|
|
288
|
+
self._lm_cache_time = 0.0
|
|
289
|
+
logger.debug("Language model cache invalidated.")
|
|
290
|
+
|
|
291
|
+
def _resolve_language_model_id(self, model_str: str | None) -> str | None:
|
|
292
|
+
"""Resolve a friendly model name to a server language model ID.
|
|
293
|
+
|
|
294
|
+
Handles provider name mapping (e.g., 'deepinfra/model' → 'openai-compatible/model')
|
|
295
|
+
by checking both the original provider name and its driver equivalent.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
model_str: The model string to resolve (e.g., 'openai/gpt-4o', 'deepinfra/Qwen3-30B').
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
The resolved server model ID (UUID), or None if not found.
|
|
302
|
+
|
|
303
|
+
Examples:
|
|
304
|
+
>>> _resolve_language_model_id("openai/gpt-4o")
|
|
305
|
+
"uuid-1234-..."
|
|
306
|
+
>>> _resolve_language_model_id("deepinfra/Qwen3-30B") # Maps to openai-compatible
|
|
307
|
+
"uuid-5678-..."
|
|
308
|
+
"""
|
|
309
|
+
if not model_str:
|
|
310
|
+
return None
|
|
311
|
+
|
|
312
|
+
# If resolution is explicitly disabled (e.g. in unit tests to avoid extra API calls), skip it
|
|
313
|
+
if getattr(self, "_skip_model_resolution", False):
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
models = self.list_language_models()
|
|
318
|
+
|
|
319
|
+
# Try exact match first
|
|
320
|
+
model_id = self._find_exact_model_match(model_str, models)
|
|
321
|
+
if model_id:
|
|
322
|
+
return model_id
|
|
323
|
+
|
|
324
|
+
# Try with provider-to-driver mapping
|
|
325
|
+
return self._try_resolve_with_driver_mapping(model_str, models)
|
|
326
|
+
except Exception:
|
|
327
|
+
pass
|
|
328
|
+
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
def _find_exact_model_match(self, model_str: str, models: list[dict[str, Any]]) -> str | None:
|
|
332
|
+
"""Find exact model match in models list.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
model_str: Model string to match.
|
|
336
|
+
models: List of language model dictionaries from server.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
Model ID (UUID) if found, None otherwise.
|
|
340
|
+
"""
|
|
341
|
+
for model_info in models:
|
|
342
|
+
provider = model_info.get("provider")
|
|
343
|
+
name = model_info.get("name")
|
|
344
|
+
if provider and name:
|
|
345
|
+
full_name = f"{provider}/{name}"
|
|
346
|
+
if full_name == model_str:
|
|
347
|
+
return model_info.get("id")
|
|
348
|
+
if name == model_str:
|
|
349
|
+
return model_info.get("id")
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
def _try_resolve_with_driver_mapping(self, model_str: str, models: list[dict[str, Any]]) -> str | None:
|
|
353
|
+
"""Try to resolve model using provider-to-driver mapping.
|
|
354
|
+
|
|
355
|
+
Maps provider names to their driver implementations (e.g., deepinfra → openai-compatible)
|
|
356
|
+
and searches the models list with the driver name.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
model_str: Model string in provider/model format (e.g., "deepinfra/Qwen3-30B").
|
|
360
|
+
models: List of language model dictionaries from server.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
Model ID (UUID) if found, None otherwise.
|
|
364
|
+
"""
|
|
365
|
+
if "/" not in model_str:
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
from glaip_sdk.models._provider_mappings import get_driver # noqa: PLC0415
|
|
369
|
+
|
|
370
|
+
provider, model_name = model_str.split("/", 1)
|
|
371
|
+
driver = get_driver(provider)
|
|
372
|
+
|
|
373
|
+
# Only try with driver if it's different from provider
|
|
374
|
+
if driver == provider:
|
|
375
|
+
return None
|
|
376
|
+
|
|
377
|
+
driver_model_str = f"{driver}/{model_name}"
|
|
378
|
+
for model_info in models:
|
|
379
|
+
provider_field = model_info.get("provider")
|
|
380
|
+
name_field = model_info.get("name")
|
|
381
|
+
if provider_field and name_field:
|
|
382
|
+
full_name = f"{provider_field}/{name_field}"
|
|
383
|
+
if full_name == driver_model_str:
|
|
384
|
+
return model_info.get("id")
|
|
385
|
+
|
|
386
|
+
return None
|
|
387
|
+
|
|
273
388
|
def list_agents(
|
|
274
389
|
self,
|
|
275
390
|
query: AgentListParams | None = None,
|
|
@@ -461,11 +576,18 @@ class AgentClient(BaseClient):
|
|
|
461
576
|
Returns:
|
|
462
577
|
Final text string.
|
|
463
578
|
"""
|
|
464
|
-
from glaip_sdk.client.run_rendering import
|
|
579
|
+
from glaip_sdk.client.run_rendering import ( # noqa: PLC0415
|
|
580
|
+
finalize_render_manager,
|
|
581
|
+
)
|
|
465
582
|
|
|
466
583
|
manager = self._get_renderer_manager()
|
|
467
584
|
return finalize_render_manager(
|
|
468
|
-
manager,
|
|
585
|
+
manager,
|
|
586
|
+
renderer,
|
|
587
|
+
final_text,
|
|
588
|
+
stats_usage,
|
|
589
|
+
started_monotonic,
|
|
590
|
+
finished_monotonic,
|
|
469
591
|
)
|
|
470
592
|
|
|
471
593
|
def _get_tool_client(self) -> ToolClient:
|
|
@@ -769,10 +891,18 @@ class AgentClient(BaseClient):
|
|
|
769
891
|
plural_label="MCPs",
|
|
770
892
|
)
|
|
771
893
|
|
|
772
|
-
def
|
|
773
|
-
"""
|
|
774
|
-
|
|
894
|
+
def _validate_agent_basics(self, known: dict[str, Any]) -> tuple[str, str]:
|
|
895
|
+
"""Validate and extract basic agent fields.
|
|
896
|
+
|
|
897
|
+
Args:
|
|
898
|
+
known: Known fields dictionary.
|
|
899
|
+
|
|
900
|
+
Returns:
|
|
901
|
+
Tuple of (name, validated_instruction).
|
|
775
902
|
|
|
903
|
+
Raises:
|
|
904
|
+
ValueError: If name or instruction is empty/whitespace.
|
|
905
|
+
"""
|
|
776
906
|
name = known.pop("name", None)
|
|
777
907
|
instruction = known.pop("instruction", None)
|
|
778
908
|
if not name or not str(name).strip():
|
|
@@ -781,9 +911,20 @@ class AgentClient(BaseClient):
|
|
|
781
911
|
raise ValueError("Agent instruction cannot be empty or whitespace")
|
|
782
912
|
|
|
783
913
|
validated_instruction = validate_agent_instruction(str(instruction))
|
|
784
|
-
|
|
914
|
+
return str(name).strip(), validated_instruction
|
|
785
915
|
|
|
786
|
-
|
|
916
|
+
def _resolve_all_resources(
|
|
917
|
+
self, known: dict[str, Any], extras: dict[str, Any]
|
|
918
|
+
) -> tuple[list[str] | None, list[str] | None, list[str] | None]:
|
|
919
|
+
"""Resolve all resource IDs (tools, agents, mcps).
|
|
920
|
+
|
|
921
|
+
Args:
|
|
922
|
+
known: Known fields dictionary.
|
|
923
|
+
extras: Extra fields dictionary.
|
|
924
|
+
|
|
925
|
+
Returns:
|
|
926
|
+
Tuple of (resolved_tools, resolved_agents, resolved_mcps).
|
|
927
|
+
"""
|
|
787
928
|
tool_refs = extras.pop("_tool_refs", None)
|
|
788
929
|
agent_refs = extras.pop("_agent_refs", None)
|
|
789
930
|
mcp_refs = extras.pop("_mcp_refs", None)
|
|
@@ -796,10 +937,56 @@ class AgentClient(BaseClient):
|
|
|
796
937
|
resolved_agents = self._resolve_agent_ids(agents_raw, agent_refs)
|
|
797
938
|
resolved_mcps = self._resolve_mcp_ids(mcps_raw, mcp_refs)
|
|
798
939
|
|
|
940
|
+
return resolved_tools, resolved_agents, resolved_mcps
|
|
941
|
+
|
|
942
|
+
def _process_model_fields(
|
|
943
|
+
self, resolved_model: Any, known: dict[str, Any]
|
|
944
|
+
) -> tuple[str, str | None, str | None, str | None]:
|
|
945
|
+
"""Process model fields and extract language model ID.
|
|
946
|
+
|
|
947
|
+
Args:
|
|
948
|
+
resolved_model: Resolved model (string or Model object).
|
|
949
|
+
known: Known fields dictionary.
|
|
950
|
+
|
|
951
|
+
Returns:
|
|
952
|
+
Tuple of (resolved_model_str, language_model_id, provider, model_name).
|
|
953
|
+
"""
|
|
954
|
+
from glaip_sdk.models import Model # noqa: PLC0415
|
|
955
|
+
|
|
956
|
+
if isinstance(resolved_model, Model):
|
|
957
|
+
if resolved_model.credentials or resolved_model.hyperparameters or resolved_model.base_url:
|
|
958
|
+
warnings.warn(
|
|
959
|
+
"Model object contains local configuration (credentials, hyperparameters, or base_url) "
|
|
960
|
+
"which is ignored for remote deployment. These fields are only used for local execution.",
|
|
961
|
+
UserWarning,
|
|
962
|
+
stacklevel=2,
|
|
963
|
+
)
|
|
964
|
+
resolved_model = resolved_model.id
|
|
965
|
+
|
|
966
|
+
# Validate and normalize string models (handles bare name deprecation)
|
|
967
|
+
if isinstance(resolved_model, str):
|
|
968
|
+
from glaip_sdk.models._validation import _validate_model # noqa: PLC0415
|
|
969
|
+
|
|
970
|
+
resolved_model = _validate_model(resolved_model)
|
|
971
|
+
|
|
799
972
|
language_model_id = known.pop("language_model_id", None)
|
|
973
|
+
if not language_model_id and isinstance(resolved_model, str):
|
|
974
|
+
language_model_id = self._resolve_language_model_id(resolved_model)
|
|
975
|
+
|
|
800
976
|
provider = known.pop("provider", None)
|
|
801
977
|
model_name = known.pop("model_name", None)
|
|
802
978
|
|
|
979
|
+
return resolved_model, language_model_id, provider, model_name
|
|
980
|
+
|
|
981
|
+
def _extract_agent_metadata(self, known: dict[str, Any]) -> tuple[str, str, str]:
|
|
982
|
+
"""Extract agent type, framework, and version.
|
|
983
|
+
|
|
984
|
+
Args:
|
|
985
|
+
known: Known fields dictionary.
|
|
986
|
+
|
|
987
|
+
Returns:
|
|
988
|
+
Tuple of (agent_type, framework, version).
|
|
989
|
+
"""
|
|
803
990
|
agent_type_value = known.pop("agent_type", None)
|
|
804
991
|
fallback_type_value = known.pop("type", None)
|
|
805
992
|
if agent_type_value is None:
|
|
@@ -807,6 +994,26 @@ class AgentClient(BaseClient):
|
|
|
807
994
|
|
|
808
995
|
framework_value = known.pop("framework", None) or DEFAULT_AGENT_FRAMEWORK
|
|
809
996
|
version_value = known.pop("version", None) or DEFAULT_AGENT_VERSION
|
|
997
|
+
|
|
998
|
+
return agent_type_value, framework_value, version_value
|
|
999
|
+
|
|
1000
|
+
def _create_agent_from_payload(self, payload: Mapping[str, Any]) -> "Agent":
|
|
1001
|
+
"""Create an agent using a fully prepared payload mapping."""
|
|
1002
|
+
known, extras = _split_known_and_extra(payload, AgentCreateRequest.__dataclass_fields__)
|
|
1003
|
+
|
|
1004
|
+
# Validate and extract basic fields
|
|
1005
|
+
name, validated_instruction = self._validate_agent_basics(known)
|
|
1006
|
+
_normalise_sequence_fields(known)
|
|
1007
|
+
|
|
1008
|
+
# Resolve model and resources
|
|
1009
|
+
resolved_model = known.pop("model", None) or DEFAULT_MODEL
|
|
1010
|
+
resolved_tools, resolved_agents, resolved_mcps = self._resolve_all_resources(known, extras)
|
|
1011
|
+
|
|
1012
|
+
# Process model and language model ID
|
|
1013
|
+
resolved_model, language_model_id, provider, model_name = self._process_model_fields(resolved_model, known)
|
|
1014
|
+
|
|
1015
|
+
# Extract agent type, framework, version
|
|
1016
|
+
agent_type_value, framework_value, version_value = self._extract_agent_metadata(known)
|
|
810
1017
|
account_id = known.pop("account_id", None)
|
|
811
1018
|
description = known.pop("description", None)
|
|
812
1019
|
metadata = _prepare_agent_metadata(known.pop("metadata", None))
|
|
@@ -819,7 +1026,7 @@ class AgentClient(BaseClient):
|
|
|
819
1026
|
final_extras.setdefault("model", resolved_model)
|
|
820
1027
|
|
|
821
1028
|
request = AgentCreateRequest(
|
|
822
|
-
name=
|
|
1029
|
+
name=name,
|
|
823
1030
|
instruction=validated_instruction,
|
|
824
1031
|
model=resolved_model,
|
|
825
1032
|
language_model_id=language_model_id,
|
|
@@ -897,6 +1104,29 @@ class AgentClient(BaseClient):
|
|
|
897
1104
|
"""Backward-compatible helper to create an agent from a configuration file."""
|
|
898
1105
|
return self.create_agent(file=file_path, **overrides)
|
|
899
1106
|
|
|
1107
|
+
def _resolve_update_model_fields(self, known: dict[str, Any]) -> None:
|
|
1108
|
+
"""Resolve model fields in-place for update payload if present.
|
|
1109
|
+
|
|
1110
|
+
If 'model' or 'language_model_id' keys exist in the known fields dict,
|
|
1111
|
+
this method resolves them into 'language_model_id', 'provider', and 'model_name'
|
|
1112
|
+
using the standard resolution logic, ensuring consistency with create_agent.
|
|
1113
|
+
|
|
1114
|
+
Args:
|
|
1115
|
+
known: The dictionary of known fields to check and update in-place.
|
|
1116
|
+
"""
|
|
1117
|
+
if "model" in known or "language_model_id" in known:
|
|
1118
|
+
model_val = known.pop("model", None)
|
|
1119
|
+
r_model, r_id, r_prov, r_name = self._process_model_fields(model_val, known)
|
|
1120
|
+
|
|
1121
|
+
if r_model is not None:
|
|
1122
|
+
known["model"] = r_model
|
|
1123
|
+
if r_id is not None:
|
|
1124
|
+
known["language_model_id"] = r_id
|
|
1125
|
+
if r_prov is not None:
|
|
1126
|
+
known["provider"] = r_prov
|
|
1127
|
+
if r_name is not None:
|
|
1128
|
+
known["model_name"] = r_name
|
|
1129
|
+
|
|
900
1130
|
def _update_agent_from_payload(
|
|
901
1131
|
self,
|
|
902
1132
|
agent_id: str,
|
|
@@ -922,6 +1152,8 @@ class AgentClient(BaseClient):
|
|
|
922
1152
|
if mcps_value is not None:
|
|
923
1153
|
mcps_value = self._resolve_mcp_ids(mcps_value, mcp_refs) # pragma: no cover
|
|
924
1154
|
|
|
1155
|
+
self._resolve_update_model_fields(known)
|
|
1156
|
+
|
|
925
1157
|
request = AgentUpdateRequest(
|
|
926
1158
|
name=known.pop("name", None),
|
|
927
1159
|
instruction=known.pop("instruction", None),
|