glaip-sdk 0.3.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glaip_sdk/cli/account_store.py +522 -0
- glaip_sdk/cli/auth.py +224 -8
- glaip_sdk/cli/commands/accounts.py +414 -0
- glaip_sdk/cli/commands/agents.py +2 -2
- glaip_sdk/cli/commands/common_config.py +65 -0
- glaip_sdk/cli/commands/configure.py +153 -87
- glaip_sdk/cli/commands/mcps.py +191 -44
- glaip_sdk/cli/commands/transcripts.py +1 -1
- glaip_sdk/cli/config.py +31 -3
- glaip_sdk/cli/display.py +1 -1
- glaip_sdk/cli/hints.py +57 -0
- glaip_sdk/cli/io.py +6 -3
- glaip_sdk/cli/main.py +181 -79
- glaip_sdk/cli/masking.py +14 -1
- glaip_sdk/cli/slash/agent_session.py +2 -1
- glaip_sdk/cli/slash/remote_runs_controller.py +1 -1
- glaip_sdk/cli/slash/session.py +11 -9
- glaip_sdk/cli/slash/tui/remote_runs_app.py +2 -3
- glaip_sdk/cli/transcript/capture.py +12 -18
- glaip_sdk/cli/transcript/viewer.py +13 -646
- glaip_sdk/cli/update_notifier.py +2 -1
- glaip_sdk/cli/utils.py +95 -139
- glaip_sdk/client/agents.py +2 -4
- glaip_sdk/client/main.py +2 -18
- glaip_sdk/client/mcps.py +11 -1
- glaip_sdk/client/run_rendering.py +90 -111
- glaip_sdk/client/shared.py +21 -0
- glaip_sdk/models.py +8 -7
- glaip_sdk/utils/display.py +23 -15
- glaip_sdk/utils/rendering/__init__.py +6 -13
- glaip_sdk/utils/rendering/formatting.py +5 -30
- glaip_sdk/utils/rendering/layout/__init__.py +64 -0
- glaip_sdk/utils/rendering/{renderer → layout}/panels.py +9 -0
- glaip_sdk/utils/rendering/{renderer → layout}/progress.py +70 -1
- glaip_sdk/utils/rendering/layout/summary.py +74 -0
- glaip_sdk/utils/rendering/layout/transcript.py +606 -0
- glaip_sdk/utils/rendering/models.py +1 -0
- glaip_sdk/utils/rendering/renderer/__init__.py +10 -28
- glaip_sdk/utils/rendering/renderer/base.py +214 -1469
- glaip_sdk/utils/rendering/renderer/debug.py +24 -0
- glaip_sdk/utils/rendering/renderer/factory.py +138 -0
- glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
- glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
- glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
- glaip_sdk/utils/rendering/state.py +204 -0
- glaip_sdk/utils/rendering/steps/__init__.py +34 -0
- glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +53 -440
- glaip_sdk/utils/rendering/steps/format.py +176 -0
- glaip_sdk/utils/rendering/steps/manager.py +387 -0
- glaip_sdk/utils/rendering/timing.py +36 -0
- glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
- glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
- glaip_sdk/utils/validation.py +13 -21
- {glaip_sdk-0.3.0.dist-info → glaip_sdk-0.5.0.dist-info}/METADATA +1 -1
- glaip_sdk-0.5.0.dist-info/RECORD +113 -0
- glaip_sdk-0.3.0.dist-info/RECORD +0 -94
- {glaip_sdk-0.3.0.dist-info → glaip_sdk-0.5.0.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.3.0.dist-info → glaip_sdk-0.5.0.dist-info}/entry_points.txt +0 -0
glaip_sdk/cli/update_notifier.py
CHANGED
|
@@ -26,7 +26,8 @@ from glaip_sdk.branding import (
|
|
|
26
26
|
)
|
|
27
27
|
from glaip_sdk.cli.commands.update import update_command
|
|
28
28
|
from glaip_sdk.cli.constants import UPDATE_CHECK_ENABLED
|
|
29
|
-
from glaip_sdk.cli.
|
|
29
|
+
from glaip_sdk.cli.hints import format_command_hint
|
|
30
|
+
from glaip_sdk.cli.utils import command_hint
|
|
30
31
|
from glaip_sdk.rich_components import AIPPanel
|
|
31
32
|
|
|
32
33
|
FetchLatestVersion = Callable[[], str | None]
|
glaip_sdk/cli/utils.py
CHANGED
|
@@ -7,12 +7,12 @@ Authors:
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import asyncio
|
|
10
11
|
import importlib
|
|
11
12
|
import json
|
|
12
13
|
import logging
|
|
13
14
|
import os
|
|
14
15
|
import sys
|
|
15
|
-
import asyncio
|
|
16
16
|
from collections.abc import Callable, Iterable
|
|
17
17
|
from contextlib import AbstractContextManager, contextmanager, nullcontext
|
|
18
18
|
from pathlib import Path
|
|
@@ -27,19 +27,21 @@ from rich.syntax import Syntax
|
|
|
27
27
|
from glaip_sdk import _version as _version_module
|
|
28
28
|
from glaip_sdk.branding import (
|
|
29
29
|
ACCENT_STYLE,
|
|
30
|
-
HINT_COMMAND_STYLE,
|
|
31
|
-
HINT_DESCRIPTION_COLOR,
|
|
32
30
|
SUCCESS_STYLE,
|
|
33
31
|
WARNING_STYLE,
|
|
34
32
|
)
|
|
33
|
+
from glaip_sdk.cli import display as cli_display
|
|
35
34
|
from glaip_sdk.cli import masking, pager
|
|
36
|
-
from glaip_sdk.cli.constants import LITERAL_STRING_THRESHOLD, TABLE_SORT_ENABLED
|
|
37
35
|
from glaip_sdk.cli.config import load_config
|
|
36
|
+
from glaip_sdk.cli.constants import LITERAL_STRING_THRESHOLD, TABLE_SORT_ENABLED
|
|
38
37
|
from glaip_sdk.cli.context import (
|
|
39
38
|
_get_view,
|
|
40
|
-
detect_export_format as _detect_export_format,
|
|
41
39
|
get_ctx_value,
|
|
42
40
|
)
|
|
41
|
+
from glaip_sdk.cli.context import (
|
|
42
|
+
detect_export_format as _detect_export_format,
|
|
43
|
+
)
|
|
44
|
+
from glaip_sdk.cli.hints import command_hint
|
|
43
45
|
from glaip_sdk.cli.io import export_resource_to_file_with_validation
|
|
44
46
|
from glaip_sdk.cli.rich_helpers import markup_text, print_markup
|
|
45
47
|
from glaip_sdk.icons import ICON_AGENT
|
|
@@ -47,10 +49,35 @@ from glaip_sdk.rich_components import AIPPanel, AIPTable
|
|
|
47
49
|
from glaip_sdk.utils import format_datetime, is_uuid
|
|
48
50
|
from glaip_sdk.utils.rendering.renderer import (
|
|
49
51
|
CapturingConsole,
|
|
50
|
-
|
|
52
|
+
RendererFactoryOptions,
|
|
51
53
|
RichStreamRenderer,
|
|
54
|
+
make_default_renderer,
|
|
55
|
+
make_verbose_renderer,
|
|
52
56
|
)
|
|
53
57
|
|
|
58
|
+
questionary = None # type: ignore[assignment]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _load_questionary_module() -> tuple[Any | None, Any | None]:
|
|
62
|
+
"""Return the questionary module and Choice class if available."""
|
|
63
|
+
module = questionary
|
|
64
|
+
if module is not None:
|
|
65
|
+
return module, getattr(module, "Choice", None)
|
|
66
|
+
|
|
67
|
+
try: # pragma: no cover - optional dependency
|
|
68
|
+
module = __import__("questionary")
|
|
69
|
+
except ImportError:
|
|
70
|
+
return None, None
|
|
71
|
+
|
|
72
|
+
return module, getattr(module, "Choice", None)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _make_questionary_choice(choice_cls: Any | None, **kwargs: Any) -> Any:
|
|
76
|
+
"""Create a questionary Choice instance or lightweight fallback."""
|
|
77
|
+
if choice_cls is None:
|
|
78
|
+
return kwargs
|
|
79
|
+
return choice_cls(**kwargs)
|
|
80
|
+
|
|
54
81
|
|
|
55
82
|
@contextmanager
|
|
56
83
|
def bind_slash_session_context(ctx: Any, session: Any) -> Any:
|
|
@@ -122,31 +149,28 @@ def prompt_export_choice_questionary(
|
|
|
122
149
|
Tuple of (choice, path) or None if cancelled/unavailable.
|
|
123
150
|
Choice can be "default", "custom", or "cancel".
|
|
124
151
|
"""
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
import questionary
|
|
128
|
-
from questionary import Choice
|
|
129
|
-
except Exception: # pragma: no cover - optional dependency
|
|
130
|
-
return None
|
|
131
|
-
|
|
132
|
-
if questionary is None or Choice is None:
|
|
152
|
+
questionary_module, choice_cls = _load_questionary_module()
|
|
153
|
+
if questionary_module is None or choice_cls is None:
|
|
133
154
|
return None
|
|
134
155
|
|
|
135
156
|
try:
|
|
136
|
-
question =
|
|
157
|
+
question = questionary_module.select(
|
|
137
158
|
"Export transcript",
|
|
138
159
|
choices=[
|
|
139
|
-
|
|
160
|
+
_make_questionary_choice(
|
|
161
|
+
choice_cls,
|
|
140
162
|
title=f"Save to default ({default_display})",
|
|
141
163
|
value=("default", default_path),
|
|
142
164
|
shortcut_key="1",
|
|
143
165
|
),
|
|
144
|
-
|
|
166
|
+
_make_questionary_choice(
|
|
167
|
+
choice_cls,
|
|
145
168
|
title="Choose a different path",
|
|
146
169
|
value=("custom", None),
|
|
147
170
|
shortcut_key="2",
|
|
148
171
|
),
|
|
149
|
-
|
|
172
|
+
_make_questionary_choice(
|
|
173
|
+
choice_cls,
|
|
150
174
|
title="Cancel",
|
|
151
175
|
value=("cancel", None),
|
|
152
176
|
shortcut_key="3",
|
|
@@ -194,9 +218,7 @@ def _run_questionary_in_thread(question: Any, *, patch_stdout: bool = False) ->
|
|
|
194
218
|
run_callable = getattr(application, "run", None) if application is not None else None
|
|
195
219
|
if callable(run_callable):
|
|
196
220
|
try:
|
|
197
|
-
if patch_stdout:
|
|
198
|
-
from prompt_toolkit.patch_stdout import patch_stdout as pt_patch_stdout
|
|
199
|
-
|
|
221
|
+
if patch_stdout and pt_patch_stdout is not None:
|
|
200
222
|
with pt_patch_stdout():
|
|
201
223
|
return run_callable(in_thread=True)
|
|
202
224
|
return run_callable(in_thread=True)
|
|
@@ -226,6 +248,7 @@ _LiteralYamlDumper.add_representer(str, _literal_str_representer)
|
|
|
226
248
|
try:
|
|
227
249
|
from prompt_toolkit.buffer import Buffer
|
|
228
250
|
from prompt_toolkit.completion import Completion
|
|
251
|
+
from prompt_toolkit.patch_stdout import patch_stdout as pt_patch_stdout
|
|
229
252
|
from prompt_toolkit.selection import SelectionType
|
|
230
253
|
from prompt_toolkit.shortcuts import PromptSession, prompt
|
|
231
254
|
|
|
@@ -235,13 +258,9 @@ except Exception: # pragma: no cover - optional dependency
|
|
|
235
258
|
SelectionType = None # type: ignore[assignment]
|
|
236
259
|
PromptSession = None # type: ignore[assignment]
|
|
237
260
|
prompt = None # type: ignore[assignment]
|
|
261
|
+
pt_patch_stdout = None # type: ignore[assignment]
|
|
238
262
|
_HAS_PTK = False
|
|
239
263
|
|
|
240
|
-
try:
|
|
241
|
-
import questionary
|
|
242
|
-
except Exception: # pragma: no cover - optional dependency
|
|
243
|
-
questionary = None
|
|
244
|
-
|
|
245
264
|
if TYPE_CHECKING: # pragma: no cover - import-only during type checking
|
|
246
265
|
from glaip_sdk import Client
|
|
247
266
|
|
|
@@ -401,9 +420,7 @@ def handle_resource_export(
|
|
|
401
420
|
):
|
|
402
421
|
export_resource_to_file_with_validation(full_resource, export_path, detected_format)
|
|
403
422
|
except Exception:
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
handle_rich_output(
|
|
423
|
+
cli_display.handle_rich_output(
|
|
407
424
|
ctx,
|
|
408
425
|
markup_text(f"[{WARNING_STYLE}]⚠️ Failed to fetch full details, using available data[/]"),
|
|
409
426
|
)
|
|
@@ -416,73 +433,6 @@ def handle_resource_export(
|
|
|
416
433
|
)
|
|
417
434
|
|
|
418
435
|
|
|
419
|
-
def in_slash_mode(ctx: click.Context | None = None) -> bool:
|
|
420
|
-
"""Return True when running inside the slash command palette."""
|
|
421
|
-
if ctx is None:
|
|
422
|
-
try:
|
|
423
|
-
ctx = click.get_current_context(silent=True)
|
|
424
|
-
except RuntimeError:
|
|
425
|
-
ctx = None
|
|
426
|
-
|
|
427
|
-
if ctx is None:
|
|
428
|
-
return False
|
|
429
|
-
|
|
430
|
-
obj = getattr(ctx, "obj", None)
|
|
431
|
-
if isinstance(obj, dict):
|
|
432
|
-
return bool(obj.get("_slash_session"))
|
|
433
|
-
|
|
434
|
-
return bool(getattr(obj, "_slash_session", False))
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
def command_hint(
|
|
438
|
-
cli_command: str | None,
|
|
439
|
-
slash_command: str | None = None,
|
|
440
|
-
*,
|
|
441
|
-
ctx: click.Context | None = None,
|
|
442
|
-
) -> str | None:
|
|
443
|
-
"""Return the appropriate command string for the current mode.
|
|
444
|
-
|
|
445
|
-
Args:
|
|
446
|
-
cli_command: Command string without the ``aip`` prefix (e.g., ``"status"``).
|
|
447
|
-
slash_command: Slash command counterpart (e.g., ``"status"`` or ``"/status"``).
|
|
448
|
-
ctx: Optional Click context override.
|
|
449
|
-
|
|
450
|
-
Returns:
|
|
451
|
-
The formatted command string for the active mode, or ``None`` when no
|
|
452
|
-
equivalent command exists in that mode.
|
|
453
|
-
"""
|
|
454
|
-
if in_slash_mode(ctx):
|
|
455
|
-
if not slash_command:
|
|
456
|
-
return None
|
|
457
|
-
return slash_command if slash_command.startswith("/") else f"/{slash_command}"
|
|
458
|
-
|
|
459
|
-
if not cli_command:
|
|
460
|
-
return None
|
|
461
|
-
return f"aip {cli_command}"
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
def format_command_hint(
|
|
465
|
-
command: str | None,
|
|
466
|
-
description: str | None = None,
|
|
467
|
-
) -> str | None:
|
|
468
|
-
"""Return a Rich markup string that highlights a command hint.
|
|
469
|
-
|
|
470
|
-
Args:
|
|
471
|
-
command: Command text to highlight (already formatted for the active mode).
|
|
472
|
-
description: Optional short description to display alongside the command.
|
|
473
|
-
|
|
474
|
-
Returns:
|
|
475
|
-
Markup string suitable for Rich rendering, or ``None`` when ``command`` is falsy.
|
|
476
|
-
"""
|
|
477
|
-
if not command:
|
|
478
|
-
return None
|
|
479
|
-
|
|
480
|
-
highlighted = f"[{HINT_COMMAND_STYLE}]{command}[/]"
|
|
481
|
-
if description:
|
|
482
|
-
highlighted += f" [{HINT_DESCRIPTION_COLOR}]{description}[/{HINT_DESCRIPTION_COLOR}]"
|
|
483
|
-
return highlighted
|
|
484
|
-
|
|
485
|
-
|
|
486
436
|
def sdk_version() -> str:
|
|
487
437
|
"""Return the current SDK version, warning if metadata is unavailable."""
|
|
488
438
|
version = getattr(_version_module, "__version__", None)
|
|
@@ -598,45 +548,48 @@ _spinner_stop = stop_spinner
|
|
|
598
548
|
|
|
599
549
|
|
|
600
550
|
def get_client(ctx: Any) -> Client: # pragma: no cover
|
|
601
|
-
"""Get configured client from context
|
|
551
|
+
"""Get configured client from context and account store (ctx > account)."""
|
|
552
|
+
# Import here to avoid circular import
|
|
553
|
+
from glaip_sdk.cli.auth import resolve_credentials # noqa: PLC0415
|
|
554
|
+
|
|
602
555
|
module = importlib.import_module("glaip_sdk")
|
|
603
556
|
client_class = cast("type[Client]", module.Client)
|
|
604
|
-
file_config = load_config() or {}
|
|
605
557
|
context_config_obj = getattr(ctx, "obj", None)
|
|
606
558
|
context_config = context_config_obj or {}
|
|
607
559
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
env_config = {
|
|
615
|
-
"api_url": os.getenv("AIP_API_URL"),
|
|
616
|
-
"api_key": os.getenv("AIP_API_KEY"),
|
|
617
|
-
"timeout": timeout_value if timeout_value else None,
|
|
618
|
-
}
|
|
619
|
-
env_config = {k: v for k, v in env_config.items() if v not in (None, "", 0)}
|
|
620
|
-
|
|
621
|
-
# Merge config sources: context > env > file
|
|
622
|
-
config = {
|
|
623
|
-
**file_config,
|
|
624
|
-
**env_config,
|
|
625
|
-
**{k: v for k, v in context_config.items() if v is not None},
|
|
626
|
-
}
|
|
560
|
+
account_name = context_config.get("account_name")
|
|
561
|
+
api_url, api_key, _ = resolve_credentials(
|
|
562
|
+
account_name=account_name,
|
|
563
|
+
api_url=context_config.get("api_url"),
|
|
564
|
+
api_key=context_config.get("api_key"),
|
|
565
|
+
)
|
|
627
566
|
|
|
628
|
-
if not
|
|
629
|
-
configure_hint = command_hint("
|
|
630
|
-
actions = []
|
|
567
|
+
if not api_url or not api_key:
|
|
568
|
+
configure_hint = command_hint("accounts add", slash_command="login", ctx=ctx)
|
|
569
|
+
actions: list[str] = []
|
|
631
570
|
if configure_hint:
|
|
632
|
-
actions.append(f"Run `{configure_hint}`")
|
|
633
|
-
|
|
571
|
+
actions.append(f"Run `{configure_hint}` to add an account profile")
|
|
572
|
+
else:
|
|
573
|
+
actions.append("add an account with 'aip accounts add'")
|
|
634
574
|
raise click.ClickException(f"Missing api_url/api_key. {' or '.join(actions)}.")
|
|
635
575
|
|
|
576
|
+
# Get timeout from context or config
|
|
577
|
+
timeout = context_config.get("timeout")
|
|
578
|
+
if timeout is None:
|
|
579
|
+
raw_timeout = os.getenv("AIP_TIMEOUT", "0") or "0"
|
|
580
|
+
try:
|
|
581
|
+
timeout = float(raw_timeout) if raw_timeout != "0" else None
|
|
582
|
+
except ValueError:
|
|
583
|
+
timeout = None
|
|
584
|
+
if timeout is None:
|
|
585
|
+
# Fallback to legacy config
|
|
586
|
+
file_config = load_config() or {}
|
|
587
|
+
timeout = file_config.get("timeout")
|
|
588
|
+
|
|
636
589
|
return client_class(
|
|
637
|
-
api_url=
|
|
638
|
-
api_key=
|
|
639
|
-
timeout=float(
|
|
590
|
+
api_url=api_url,
|
|
591
|
+
api_key=api_key,
|
|
592
|
+
timeout=float(timeout or 30.0),
|
|
640
593
|
)
|
|
641
594
|
|
|
642
595
|
|
|
@@ -1358,19 +1311,20 @@ def build_renderer(
|
|
|
1358
1311
|
|
|
1359
1312
|
# Configure renderer based on verbose mode and explicit overrides
|
|
1360
1313
|
live_enabled = bool(live) if live is not None else not verbose
|
|
1361
|
-
|
|
1362
|
-
live
|
|
1363
|
-
append_finished_snapshots
|
|
1364
|
-
|
|
1365
|
-
|
|
1314
|
+
cfg_overrides = {
|
|
1315
|
+
"live": live_enabled,
|
|
1316
|
+
"append_finished_snapshots": bool(snapshots) if snapshots is not None else False,
|
|
1317
|
+
}
|
|
1318
|
+
renderer_console = (
|
|
1319
|
+
working_console.original_console if isinstance(working_console, CapturingConsole) else working_console
|
|
1366
1320
|
)
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
verbose=verbose,
|
|
1321
|
+
factory = make_verbose_renderer if verbose else make_default_renderer
|
|
1322
|
+
factory_options = RendererFactoryOptions(
|
|
1323
|
+
console=renderer_console,
|
|
1324
|
+
cfg_overrides=cfg_overrides,
|
|
1325
|
+
verbose=verbose if factory is make_default_renderer else None,
|
|
1373
1326
|
)
|
|
1327
|
+
renderer = factory_options.build(factory)
|
|
1374
1328
|
|
|
1375
1329
|
# Link the renderer back to the slash session when running from the palette.
|
|
1376
1330
|
_register_renderer_with_session(_ctx, renderer)
|
|
@@ -1540,17 +1494,19 @@ def _handle_json_view_ambiguity(matches: list[Any]) -> Any:
|
|
|
1540
1494
|
|
|
1541
1495
|
def _handle_questionary_ambiguity(resource_type: str, ref: str, matches: list[Any]) -> Any:
|
|
1542
1496
|
"""Handle ambiguity using questionary interactive interface."""
|
|
1543
|
-
|
|
1497
|
+
questionary_module, choice_cls = _load_questionary_module()
|
|
1498
|
+
if not (questionary_module and os.getenv("TERM") and os.isatty(0) and os.isatty(1)):
|
|
1544
1499
|
raise click.ClickException("Interactive selection not available")
|
|
1545
1500
|
|
|
1546
1501
|
# Escape special characters for questionary
|
|
1547
1502
|
safe_resource_type = resource_type.replace("{", "{{").replace("}", "}}")
|
|
1548
1503
|
safe_ref = ref.replace("{", "{{").replace("}", "}}")
|
|
1549
1504
|
|
|
1550
|
-
picked_idx =
|
|
1505
|
+
picked_idx = questionary_module.select(
|
|
1551
1506
|
f"Multiple {safe_resource_type}s match '{safe_ref}'. Pick one:",
|
|
1552
1507
|
choices=[
|
|
1553
|
-
|
|
1508
|
+
_make_questionary_choice(
|
|
1509
|
+
choice_cls,
|
|
1554
1510
|
title=(
|
|
1555
1511
|
f"{getattr(m, 'name', '—').replace('{', '{{').replace('}', '}}')} — "
|
|
1556
1512
|
f"{getattr(m, 'id', '').replace('{', '{{').replace('}', '}}')}"
|
glaip_sdk/client/agents.py
CHANGED
|
@@ -29,6 +29,7 @@ from glaip_sdk.client.run_rendering import (
|
|
|
29
29
|
AgentRunRenderingManager,
|
|
30
30
|
compute_timeout_seconds,
|
|
31
31
|
)
|
|
32
|
+
from glaip_sdk.client.shared import build_shared_config
|
|
32
33
|
from glaip_sdk.client.tools import ToolClient
|
|
33
34
|
from glaip_sdk.config.constants import (
|
|
34
35
|
AGENT_CONFIG_FIELDS,
|
|
@@ -1235,9 +1236,6 @@ class AgentClient(BaseClient):
|
|
|
1235
1236
|
def runs(self) -> "AgentRunsClient":
|
|
1236
1237
|
"""Get the agent runs client."""
|
|
1237
1238
|
if self._runs_client is None:
|
|
1238
|
-
|
|
1239
|
-
from glaip_sdk.client.main import _build_shared_config
|
|
1240
|
-
|
|
1241
|
-
shared_config = _build_shared_config(self)
|
|
1239
|
+
shared_config = build_shared_config(self)
|
|
1242
1240
|
self._runs_client = AgentRunsClient(**shared_config)
|
|
1243
1241
|
return self._runs_client
|
glaip_sdk/client/main.py
CHANGED
|
@@ -10,27 +10,11 @@ from typing import Any
|
|
|
10
10
|
from glaip_sdk.client.agents import AgentClient
|
|
11
11
|
from glaip_sdk.client.base import BaseClient
|
|
12
12
|
from glaip_sdk.client.mcps import MCPClient
|
|
13
|
+
from glaip_sdk.client.shared import build_shared_config
|
|
13
14
|
from glaip_sdk.client.tools import ToolClient
|
|
14
15
|
from glaip_sdk.models import MCP, Agent, Tool
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def _build_shared_config(client: BaseClient) -> dict[str, Any]:
|
|
18
|
-
"""Build shared configuration dictionary for sub-clients.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
client: Base client instance.
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
Dictionary with shared configuration.
|
|
25
|
-
"""
|
|
26
|
-
return {
|
|
27
|
-
"parent_client": client,
|
|
28
|
-
"api_url": client.api_url,
|
|
29
|
-
"api_key": client.api_key,
|
|
30
|
-
"timeout": client._timeout,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
18
|
class Client(BaseClient):
|
|
35
19
|
"""Main client that composes all specialized clients and shares one HTTP session."""
|
|
36
20
|
|
|
@@ -42,7 +26,7 @@ class Client(BaseClient):
|
|
|
42
26
|
"""
|
|
43
27
|
super().__init__(**kwargs)
|
|
44
28
|
# Share the single httpx.Client + config with sub-clients
|
|
45
|
-
shared_config =
|
|
29
|
+
shared_config = build_shared_config(self)
|
|
46
30
|
self.agents = AgentClient(**shared_config)
|
|
47
31
|
self.tools = ToolClient(**shared_config)
|
|
48
32
|
self.mcps = MCPClient(**shared_config)
|
glaip_sdk/client/mcps.py
CHANGED
|
@@ -204,7 +204,17 @@ class MCPClient(BaseClient):
|
|
|
204
204
|
def get_mcp_tools(self, mcp_id: str) -> list[dict[str, Any]]:
|
|
205
205
|
"""Get tools available from an MCP."""
|
|
206
206
|
data = self._request("GET", f"{MCPS_ENDPOINT}{mcp_id}/tools")
|
|
207
|
-
|
|
207
|
+
if data is None:
|
|
208
|
+
return []
|
|
209
|
+
if isinstance(data, list):
|
|
210
|
+
return data
|
|
211
|
+
if isinstance(data, dict):
|
|
212
|
+
if "tools" in data:
|
|
213
|
+
return data.get("tools", []) or []
|
|
214
|
+
logger.warning("Unexpected MCP tools response keys %s; returning empty list", list(data.keys()))
|
|
215
|
+
return []
|
|
216
|
+
logger.warning("Unexpected MCP tools response type %s; returning empty list", type(data).__name__)
|
|
217
|
+
return []
|
|
208
218
|
|
|
209
219
|
def test_mcp_connection(self, config: dict[str, Any]) -> dict[str, Any]:
|
|
210
220
|
"""Test MCP connection using configuration.
|