glaip-sdk 0.0.16__py3-none-any.whl → 0.0.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 +1 -1
- glaip_sdk/branding.py +1 -1
- glaip_sdk/cli/commands/agents.py +10 -10
- glaip_sdk/cli/commands/configure.py +7 -2
- glaip_sdk/cli/commands/mcps.py +18 -19
- glaip_sdk/cli/commands/tools.py +19 -13
- glaip_sdk/cli/display.py +5 -4
- glaip_sdk/cli/rich_helpers.py +29 -0
- glaip_sdk/cli/utils.py +6 -6
- glaip_sdk/client/agents.py +526 -95
- glaip_sdk/client/main.py +8 -0
- glaip_sdk/config/constants.py +1 -1
- {glaip_sdk-0.0.16.dist-info → glaip_sdk-0.0.17.dist-info}/METADATA +2 -2
- {glaip_sdk-0.0.16.dist-info → glaip_sdk-0.0.17.dist-info}/RECORD +16 -15
- {glaip_sdk-0.0.16.dist-info → glaip_sdk-0.0.17.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.0.16.dist-info → glaip_sdk-0.0.17.dist-info}/entry_points.txt +0 -0
glaip_sdk/__init__.py
CHANGED
glaip_sdk/branding.py
CHANGED
|
@@ -42,7 +42,7 @@ LABEL = "bold"
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class AIPBranding:
|
|
45
|
-
"""GL AIP
|
|
45
|
+
"""GL AIP branding utilities with ASCII banner and version display."""
|
|
46
46
|
|
|
47
47
|
# GL AIP ASCII art - Modern block style with enhanced visibility
|
|
48
48
|
AIP_LOGO = r"""
|
glaip_sdk/cli/commands/agents.py
CHANGED
|
@@ -12,7 +12,6 @@ from typing import Any
|
|
|
12
12
|
|
|
13
13
|
import click
|
|
14
14
|
from rich.console import Console
|
|
15
|
-
from rich.text import Text
|
|
16
15
|
|
|
17
16
|
from glaip_sdk.cli.agent_config import (
|
|
18
17
|
merge_agent_config_with_cli_args as merge_import_with_cli_args,
|
|
@@ -45,6 +44,7 @@ from glaip_sdk.cli.io import (
|
|
|
45
44
|
load_resource_from_file_with_validation as load_resource_from_file,
|
|
46
45
|
)
|
|
47
46
|
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
47
|
+
from glaip_sdk.cli.rich_helpers import markup_text, print_markup
|
|
48
48
|
from glaip_sdk.cli.utils import (
|
|
49
49
|
_fuzzy_pick_for_resources,
|
|
50
50
|
build_renderer,
|
|
@@ -275,7 +275,7 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
|
|
|
275
275
|
def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
276
276
|
"""Display full agent details using raw API data to preserve ALL fields."""
|
|
277
277
|
if agent is None:
|
|
278
|
-
handle_rich_output(ctx,
|
|
278
|
+
handle_rich_output(ctx, markup_text("[red]❌ No agent provided[/red]"))
|
|
279
279
|
return
|
|
280
280
|
|
|
281
281
|
# Try to fetch and format raw agent data first
|
|
@@ -297,7 +297,8 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
|
|
|
297
297
|
else:
|
|
298
298
|
# Fall back to Pydantic model data if raw fetch fails
|
|
299
299
|
handle_rich_output(
|
|
300
|
-
ctx,
|
|
300
|
+
ctx,
|
|
301
|
+
markup_text("[yellow]Falling back to Pydantic model data[/yellow]"),
|
|
301
302
|
)
|
|
302
303
|
|
|
303
304
|
with spinner_context(
|
|
@@ -495,18 +496,19 @@ def get(ctx: Any, agent_ref: str, select: int | None, export: str | None) -> Non
|
|
|
495
496
|
except Exception as e:
|
|
496
497
|
handle_rich_output(
|
|
497
498
|
ctx,
|
|
498
|
-
|
|
499
|
+
markup_text(
|
|
499
500
|
f"[yellow]⚠️ Could not fetch full agent details: {e}[/yellow]"
|
|
500
501
|
),
|
|
501
502
|
)
|
|
502
503
|
handle_rich_output(
|
|
503
|
-
ctx,
|
|
504
|
+
ctx,
|
|
505
|
+
markup_text("[yellow]⚠️ Proceeding with available data[/yellow]"),
|
|
504
506
|
)
|
|
505
507
|
|
|
506
508
|
export_resource_to_file(agent, export_path, detected_format)
|
|
507
509
|
handle_rich_output(
|
|
508
510
|
ctx,
|
|
509
|
-
|
|
511
|
+
markup_text(
|
|
510
512
|
f"[green]✅ Complete agent configuration exported to: {export_path} (format: {detected_format})[/green]"
|
|
511
513
|
),
|
|
512
514
|
)
|
|
@@ -619,7 +621,7 @@ def _save_run_transcript(save: str | None, result: Any, working_console: Any) ->
|
|
|
619
621
|
|
|
620
622
|
with open(save, "w", encoding="utf-8") as f:
|
|
621
623
|
f.write(content)
|
|
622
|
-
|
|
624
|
+
print_markup(f"[green]Full debug output saved to: {save}[/green]", console=console)
|
|
623
625
|
|
|
624
626
|
|
|
625
627
|
@agents_group.command()
|
|
@@ -1306,8 +1308,6 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1306
1308
|
|
|
1307
1309
|
# Show success message for non-JSON output
|
|
1308
1310
|
if get_ctx_value(ctx, "view") != "json":
|
|
1309
|
-
from rich.text import Text
|
|
1310
|
-
|
|
1311
1311
|
# Extract some useful info from the result
|
|
1312
1312
|
success_count = result.get("data", {}).get("created_count", 0) + result.get(
|
|
1313
1313
|
"data", {}
|
|
@@ -1316,7 +1316,7 @@ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
|
|
|
1316
1316
|
|
|
1317
1317
|
handle_rich_output(
|
|
1318
1318
|
ctx,
|
|
1319
|
-
|
|
1319
|
+
markup_text(
|
|
1320
1320
|
f"[green]✅ Successfully synced {success_count} LangFlow agents ({total_count} total processed)[/green]"
|
|
1321
1321
|
),
|
|
1322
1322
|
)
|
|
@@ -14,6 +14,7 @@ from glaip_sdk import Client
|
|
|
14
14
|
from glaip_sdk._version import __version__ as _SDK_VERSION
|
|
15
15
|
from glaip_sdk.branding import AIPBranding
|
|
16
16
|
from glaip_sdk.cli.config import CONFIG_FILE, load_config, save_config
|
|
17
|
+
from glaip_sdk.cli.rich_helpers import markup_text
|
|
17
18
|
from glaip_sdk.cli.utils import command_hint
|
|
18
19
|
from glaip_sdk.rich_components import AIPTable
|
|
19
20
|
|
|
@@ -88,7 +89,9 @@ def get_config(key: str) -> None:
|
|
|
88
89
|
config = load_config()
|
|
89
90
|
|
|
90
91
|
if key not in config:
|
|
91
|
-
console.print(
|
|
92
|
+
console.print(
|
|
93
|
+
markup_text(f"[yellow]Configuration key '{key}' not found.[/yellow]")
|
|
94
|
+
)
|
|
92
95
|
raise click.ClickException(f"Configuration key not found: {key}")
|
|
93
96
|
|
|
94
97
|
value = config[key]
|
|
@@ -108,7 +111,9 @@ def unset_config(key: str) -> None:
|
|
|
108
111
|
config = load_config()
|
|
109
112
|
|
|
110
113
|
if key not in config:
|
|
111
|
-
console.print(
|
|
114
|
+
console.print(
|
|
115
|
+
markup_text(f"[yellow]Configuration key '{key}' not found.[/yellow]")
|
|
116
|
+
)
|
|
112
117
|
return
|
|
113
118
|
|
|
114
119
|
del config[key]
|
glaip_sdk/cli/commands/mcps.py
CHANGED
|
@@ -11,7 +11,6 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
import click
|
|
13
13
|
from rich.console import Console
|
|
14
|
-
from rich.text import Text
|
|
15
14
|
|
|
16
15
|
from glaip_sdk.cli.context import detect_export_format, get_ctx_value, output_flags
|
|
17
16
|
from glaip_sdk.cli.display import (
|
|
@@ -33,6 +32,7 @@ from glaip_sdk.cli.mcp_validators import (
|
|
|
33
32
|
)
|
|
34
33
|
from glaip_sdk.cli.parsers.json_input import parse_json_input
|
|
35
34
|
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
35
|
+
from glaip_sdk.cli.rich_helpers import print_markup
|
|
36
36
|
from glaip_sdk.cli.utils import (
|
|
37
37
|
coerce_to_row,
|
|
38
38
|
get_client,
|
|
@@ -481,21 +481,23 @@ def _handle_mcp_export(
|
|
|
481
481
|
):
|
|
482
482
|
mcp = client.mcps.get_mcp_by_id(mcp.id)
|
|
483
483
|
except Exception as e:
|
|
484
|
-
|
|
485
|
-
|
|
484
|
+
print_markup(
|
|
485
|
+
f"[yellow]⚠️ Could not fetch full MCP details: {e}[/yellow]",
|
|
486
|
+
console=console,
|
|
487
|
+
)
|
|
488
|
+
print_markup(
|
|
489
|
+
"[yellow]⚠️ Proceeding with available data[/yellow]", console=console
|
|
486
490
|
)
|
|
487
|
-
console.print(Text("[yellow]⚠️ Proceeding with available data[/yellow]"))
|
|
488
491
|
|
|
489
492
|
# Determine if we should prompt for secrets
|
|
490
493
|
prompt_for_secrets = not no_auth_prompt and sys.stdin.isatty()
|
|
491
494
|
|
|
492
495
|
# Warn user if non-interactive mode forces placeholder usage
|
|
493
496
|
if not no_auth_prompt and not sys.stdin.isatty():
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
)
|
|
497
|
+
print_markup(
|
|
498
|
+
"[yellow]⚠️ Non-interactive mode detected. "
|
|
499
|
+
"Using placeholder values for secrets.[/yellow]",
|
|
500
|
+
console=console,
|
|
499
501
|
)
|
|
500
502
|
|
|
501
503
|
# Build and write export payload
|
|
@@ -528,11 +530,10 @@ def _handle_mcp_export(
|
|
|
528
530
|
)
|
|
529
531
|
write_resource_export(export_path, export_payload, detected_format)
|
|
530
532
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
)
|
|
533
|
+
print_markup(
|
|
534
|
+
f"[green]✅ Complete MCP configuration exported to: "
|
|
535
|
+
f"{export_path} (format: {detected_format})[/green]",
|
|
536
|
+
console=console,
|
|
536
537
|
)
|
|
537
538
|
|
|
538
539
|
|
|
@@ -732,11 +733,9 @@ def connect(ctx: Any, config_file: str) -> None:
|
|
|
732
733
|
|
|
733
734
|
view = get_ctx_value(ctx, "view", "rich")
|
|
734
735
|
if view != "json":
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
f"{config_file}...[/yellow]"
|
|
739
|
-
)
|
|
736
|
+
print_markup(
|
|
737
|
+
f"[yellow]Connecting to MCP with config from {config_file}...[/yellow]",
|
|
738
|
+
console=console,
|
|
740
739
|
)
|
|
741
740
|
|
|
742
741
|
# Test connection using config
|
glaip_sdk/cli/commands/tools.py
CHANGED
|
@@ -11,7 +11,6 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
import click
|
|
13
13
|
from rich.console import Console
|
|
14
|
-
from rich.text import Text
|
|
15
14
|
|
|
16
15
|
from glaip_sdk.cli.context import detect_export_format, get_ctx_value, output_flags
|
|
17
16
|
from glaip_sdk.cli.display import (
|
|
@@ -33,6 +32,7 @@ from glaip_sdk.cli.io import (
|
|
|
33
32
|
load_resource_from_file_with_validation as load_resource_from_file,
|
|
34
33
|
)
|
|
35
34
|
from glaip_sdk.cli.resolution import resolve_resource_reference
|
|
35
|
+
from glaip_sdk.cli.rich_helpers import markup_text, print_markup
|
|
36
36
|
from glaip_sdk.cli.utils import (
|
|
37
37
|
coerce_to_row,
|
|
38
38
|
get_client,
|
|
@@ -349,11 +349,13 @@ def get(ctx: Any, tool_ref: str, select: int | None, export: str | None) -> None
|
|
|
349
349
|
):
|
|
350
350
|
tool = client.get_tool_by_id(tool.id)
|
|
351
351
|
except Exception as e:
|
|
352
|
-
|
|
353
|
-
|
|
352
|
+
print_markup(
|
|
353
|
+
f"[yellow]⚠️ Could not fetch full tool details: {e}[/yellow]",
|
|
354
|
+
console=console,
|
|
354
355
|
)
|
|
355
|
-
|
|
356
|
-
|
|
356
|
+
print_markup(
|
|
357
|
+
"[yellow]⚠️ Proceeding with available data[/yellow]",
|
|
358
|
+
console=console,
|
|
357
359
|
)
|
|
358
360
|
|
|
359
361
|
with spinner_context(
|
|
@@ -362,10 +364,9 @@ def get(ctx: Any, tool_ref: str, select: int | None, export: str | None) -> None
|
|
|
362
364
|
console_override=console,
|
|
363
365
|
):
|
|
364
366
|
export_resource_to_file(tool, export_path, detected_format)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
)
|
|
367
|
+
print_markup(
|
|
368
|
+
f"[green]✅ Complete tool configuration exported to: {export_path} (format: {detected_format})[/green]",
|
|
369
|
+
console=console,
|
|
369
370
|
)
|
|
370
371
|
|
|
371
372
|
# Try to fetch raw API data first to preserve ALL fields
|
|
@@ -473,7 +474,8 @@ def update(
|
|
|
473
474
|
tool.id, file, framework=tool.framework
|
|
474
475
|
)
|
|
475
476
|
handle_rich_output(
|
|
476
|
-
ctx,
|
|
477
|
+
ctx,
|
|
478
|
+
markup_text(f"[green]✓[/green] Tool code updated from {file}"),
|
|
477
479
|
)
|
|
478
480
|
elif update_data:
|
|
479
481
|
# Update metadata only (native tools only)
|
|
@@ -487,9 +489,13 @@ def update(
|
|
|
487
489
|
console_override=console,
|
|
488
490
|
):
|
|
489
491
|
updated_tool = tool.update(**update_data)
|
|
490
|
-
handle_rich_output(
|
|
492
|
+
handle_rich_output(
|
|
493
|
+
ctx, markup_text("[green]✓[/green] Tool metadata updated")
|
|
494
|
+
)
|
|
491
495
|
else:
|
|
492
|
-
handle_rich_output(
|
|
496
|
+
handle_rich_output(
|
|
497
|
+
ctx, markup_text("[yellow]No updates specified[/yellow]")
|
|
498
|
+
)
|
|
493
499
|
return
|
|
494
500
|
|
|
495
501
|
handle_json_output(ctx, updated_tool.model_dump())
|
|
@@ -574,5 +580,5 @@ def script(ctx: Any, tool_id: str) -> None:
|
|
|
574
580
|
except Exception as e:
|
|
575
581
|
handle_json_output(ctx, error=e)
|
|
576
582
|
if get_ctx_value(ctx, "view") != "json":
|
|
577
|
-
|
|
583
|
+
print_markup(f"[red]Error getting tool script: {e}[/red]", console=console)
|
|
578
584
|
raise click.ClickException(str(e))
|
glaip_sdk/cli/display.py
CHANGED
|
@@ -15,6 +15,7 @@ from rich.console import Console
|
|
|
15
15
|
from rich.panel import Panel
|
|
16
16
|
from rich.text import Text
|
|
17
17
|
|
|
18
|
+
from glaip_sdk.cli.rich_helpers import markup_text, print_markup
|
|
18
19
|
from glaip_sdk.cli.utils import command_hint
|
|
19
20
|
from glaip_sdk.rich_components import AIPPanel
|
|
20
21
|
|
|
@@ -61,7 +62,7 @@ def display_update_success(resource_type: str, resource_name: str) -> Text:
|
|
|
61
62
|
Returns:
|
|
62
63
|
Rich Text object for display
|
|
63
64
|
"""
|
|
64
|
-
return
|
|
65
|
+
return markup_text(
|
|
65
66
|
f"[green]✅ {resource_type} '{resource_name}' updated successfully[/green]"
|
|
66
67
|
)
|
|
67
68
|
|
|
@@ -76,7 +77,7 @@ def display_deletion_success(resource_type: str, resource_name: str) -> Text:
|
|
|
76
77
|
Returns:
|
|
77
78
|
Rich Text object for display
|
|
78
79
|
"""
|
|
79
|
-
return
|
|
80
|
+
return markup_text(
|
|
80
81
|
f"[green]✅ {resource_type} '{resource_name}' deleted successfully[/green]"
|
|
81
82
|
)
|
|
82
83
|
|
|
@@ -89,8 +90,8 @@ def display_api_error(error: Exception, operation: str = "operation") -> None:
|
|
|
89
90
|
operation: Description of the operation that failed
|
|
90
91
|
"""
|
|
91
92
|
error_type = type(error).__name__
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
print_markup(f"[red]Error during {operation}: {error}[/red]", console=console)
|
|
94
|
+
print_markup(f"[dim]Error type: {error_type}[/dim]", console=console)
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
def print_api_error(e: Exception) -> None:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Shared helpers for creating and printing Rich markup content.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.markup import MarkupError
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def markup_text(message: str, **kwargs: Any) -> Text:
|
|
17
|
+
"""Create a Rich Text instance from markup with graceful fallback."""
|
|
18
|
+
try:
|
|
19
|
+
return Text.from_markup(message, **kwargs)
|
|
20
|
+
except MarkupError:
|
|
21
|
+
return Text(message, **kwargs)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def print_markup(
|
|
25
|
+
message: str, *, console: Console | None = None, **kwargs: Any
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Print markup-aware text to the provided console (default: new Console)."""
|
|
28
|
+
target_console = console or Console()
|
|
29
|
+
target_console.print(markup_text(message, **kwargs))
|
glaip_sdk/cli/utils.py
CHANGED
|
@@ -19,8 +19,8 @@ import click
|
|
|
19
19
|
from rich.console import Console, Group
|
|
20
20
|
from rich.markdown import Markdown
|
|
21
21
|
from rich.pretty import Pretty
|
|
22
|
-
from rich.text import Text
|
|
23
22
|
|
|
23
|
+
from glaip_sdk.cli.rich_helpers import markup_text
|
|
24
24
|
from glaip_sdk.rich_components import AIPPanel
|
|
25
25
|
|
|
26
26
|
# Optional interactive deps (fuzzy palette)
|
|
@@ -557,7 +557,7 @@ def output_result(
|
|
|
557
557
|
if panel_title:
|
|
558
558
|
console.print(AIPPanel(renderable, title=panel_title))
|
|
559
559
|
else:
|
|
560
|
-
console.print(
|
|
560
|
+
console.print(markup_text(f"[cyan]{title}:[/cyan]"))
|
|
561
561
|
console.print(renderable)
|
|
562
562
|
|
|
563
563
|
|
|
@@ -635,7 +635,7 @@ def _build_table_group(
|
|
|
635
635
|
table = _create_table(columns, title)
|
|
636
636
|
for row in rows:
|
|
637
637
|
table.add_row(*[str(row.get(key, "N/A")) for key, _, _, _ in columns])
|
|
638
|
-
footer =
|
|
638
|
+
footer = markup_text(f"\n[dim]Total {len(rows)} items[/dim]")
|
|
639
639
|
return Group(table, footer)
|
|
640
640
|
|
|
641
641
|
|
|
@@ -665,7 +665,7 @@ def _handle_markdown_output(
|
|
|
665
665
|
|
|
666
666
|
def _handle_empty_items(title: str) -> None:
|
|
667
667
|
"""Handle case when no items are found."""
|
|
668
|
-
console.print(
|
|
668
|
+
console.print(markup_text(f"[yellow]No {title.lower()} found.[/yellow]"))
|
|
669
669
|
|
|
670
670
|
|
|
671
671
|
def _should_use_fuzzy_picker() -> bool:
|
|
@@ -707,7 +707,7 @@ def _print_selection_tip(title: str) -> None:
|
|
|
707
707
|
tip_cmd = _resource_tip_command(title)
|
|
708
708
|
if tip_cmd:
|
|
709
709
|
console.print(
|
|
710
|
-
|
|
710
|
+
markup_text(f"\n[dim]Tip: use `{tip_cmd} <ID>` for details[/dim]")
|
|
711
711
|
)
|
|
712
712
|
|
|
713
713
|
|
|
@@ -1092,7 +1092,7 @@ def _handle_fallback_numeric_ambiguity(
|
|
|
1092
1092
|
safe_ref = ref.replace("{", "{{").replace("}", "}}")
|
|
1093
1093
|
|
|
1094
1094
|
console.print(
|
|
1095
|
-
|
|
1095
|
+
markup_text(
|
|
1096
1096
|
f"[yellow]Multiple {safe_resource_type}s found matching '{safe_ref}':[/yellow]"
|
|
1097
1097
|
)
|
|
1098
1098
|
)
|
glaip_sdk/client/agents.py
CHANGED
|
@@ -7,7 +7,9 @@ Authors:
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import logging
|
|
10
|
-
from collections.abc import AsyncGenerator, Mapping
|
|
10
|
+
from collections.abc import AsyncGenerator, Callable, Iterator, Mapping
|
|
11
|
+
from os import PathLike
|
|
12
|
+
from pathlib import Path
|
|
11
13
|
from typing import Any, BinaryIO
|
|
12
14
|
|
|
13
15
|
import httpx
|
|
@@ -19,10 +21,12 @@ from glaip_sdk.client._agent_payloads import (
|
|
|
19
21
|
AgentUpdateRequest,
|
|
20
22
|
)
|
|
21
23
|
from glaip_sdk.client.base import BaseClient
|
|
24
|
+
from glaip_sdk.client.mcps import MCPClient
|
|
22
25
|
from glaip_sdk.client.run_rendering import (
|
|
23
26
|
AgentRunRenderingManager,
|
|
24
27
|
compute_timeout_seconds,
|
|
25
28
|
)
|
|
29
|
+
from glaip_sdk.client.tools import ToolClient
|
|
26
30
|
from glaip_sdk.config.constants import (
|
|
27
31
|
DEFAULT_AGENT_FRAMEWORK,
|
|
28
32
|
DEFAULT_AGENT_RUN_TIMEOUT,
|
|
@@ -32,13 +36,21 @@ from glaip_sdk.config.constants import (
|
|
|
32
36
|
)
|
|
33
37
|
from glaip_sdk.exceptions import NotFoundError
|
|
34
38
|
from glaip_sdk.models import Agent
|
|
39
|
+
from glaip_sdk.payload_schemas.agent import list_server_only_fields
|
|
40
|
+
from glaip_sdk.utils.agent_config import normalize_agent_config_for_import
|
|
35
41
|
from glaip_sdk.utils.client_utils import (
|
|
36
42
|
aiter_sse_events,
|
|
37
43
|
create_model_instances,
|
|
38
44
|
find_by_name,
|
|
39
45
|
prepare_multipart_data,
|
|
40
46
|
)
|
|
47
|
+
from glaip_sdk.utils.import_export import (
|
|
48
|
+
convert_export_to_import_format,
|
|
49
|
+
merge_import_with_cli_args,
|
|
50
|
+
)
|
|
41
51
|
from glaip_sdk.utils.rendering.renderer import RichStreamRenderer
|
|
52
|
+
from glaip_sdk.utils.resource_refs import is_uuid
|
|
53
|
+
from glaip_sdk.utils.serialization import load_resource_from_file
|
|
42
54
|
from glaip_sdk.utils.validation import validate_agent_instruction
|
|
43
55
|
|
|
44
56
|
# API endpoints
|
|
@@ -50,6 +62,145 @@ SSE_CONTENT_TYPE = "text/event-stream"
|
|
|
50
62
|
# Set up module-level logger
|
|
51
63
|
logger = logging.getLogger("glaip_sdk.agents")
|
|
52
64
|
|
|
65
|
+
_SERVER_ONLY_IMPORT_FIELDS = set(list_server_only_fields()) | {"success", "message"}
|
|
66
|
+
_MERGED_SEQUENCE_FIELDS = ("tools", "agents", "mcps")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _normalise_sequence(value: Any) -> list[Any] | None:
|
|
70
|
+
"""Normalise optional sequence inputs to plain lists."""
|
|
71
|
+
if value is None:
|
|
72
|
+
return None
|
|
73
|
+
if isinstance(value, list):
|
|
74
|
+
return value
|
|
75
|
+
if isinstance(value, (tuple, set)):
|
|
76
|
+
return list(value)
|
|
77
|
+
return [value]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _normalise_sequence_fields(mapping: dict[str, Any]) -> None:
|
|
81
|
+
"""Normalise merged sequence fields in-place."""
|
|
82
|
+
for field in _MERGED_SEQUENCE_FIELDS:
|
|
83
|
+
if field in mapping:
|
|
84
|
+
normalised = _normalise_sequence(mapping[field])
|
|
85
|
+
if normalised is not None:
|
|
86
|
+
mapping[field] = normalised
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _merge_override_maps(
|
|
90
|
+
base_values: Mapping[str, Any],
|
|
91
|
+
extra_values: Mapping[str, Any],
|
|
92
|
+
) -> dict[str, Any]:
|
|
93
|
+
"""Merge override mappings while normalising sequence fields."""
|
|
94
|
+
merged: dict[str, Any] = {}
|
|
95
|
+
for source in (base_values, extra_values):
|
|
96
|
+
for key, value in source.items():
|
|
97
|
+
if value is None:
|
|
98
|
+
continue
|
|
99
|
+
merged[key] = (
|
|
100
|
+
_normalise_sequence(value) if key in _MERGED_SEQUENCE_FIELDS else value
|
|
101
|
+
)
|
|
102
|
+
return merged
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _split_known_and_extra(
|
|
106
|
+
payload: Mapping[str, Any],
|
|
107
|
+
known_fields: Mapping[str, Any],
|
|
108
|
+
) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
109
|
+
"""Split payload mapping into known request fields and extras."""
|
|
110
|
+
known: dict[str, Any] = {}
|
|
111
|
+
extras: dict[str, Any] = {}
|
|
112
|
+
for key, value in payload.items():
|
|
113
|
+
if value is None:
|
|
114
|
+
continue
|
|
115
|
+
if key in known_fields:
|
|
116
|
+
known[key] = value
|
|
117
|
+
else:
|
|
118
|
+
extras[key] = value
|
|
119
|
+
return known, extras
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _load_agent_file_payload(
|
|
123
|
+
file_path: Path, *, model_override: str | None
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
"""Load agent configuration from disk and normalise legacy fields."""
|
|
126
|
+
if not file_path.exists():
|
|
127
|
+
raise FileNotFoundError(f"Agent configuration file not found: {file_path}")
|
|
128
|
+
if not file_path.is_file():
|
|
129
|
+
raise ValueError(f"Agent configuration path must point to a file: {file_path}")
|
|
130
|
+
|
|
131
|
+
raw_data = load_resource_from_file(file_path)
|
|
132
|
+
if not isinstance(raw_data, Mapping):
|
|
133
|
+
raise ValueError("Agent configuration file must contain a mapping/object.")
|
|
134
|
+
|
|
135
|
+
payload = convert_export_to_import_format(dict(raw_data))
|
|
136
|
+
payload = normalize_agent_config_for_import(payload, model_override)
|
|
137
|
+
|
|
138
|
+
for field in _SERVER_ONLY_IMPORT_FIELDS:
|
|
139
|
+
payload.pop(field, None)
|
|
140
|
+
|
|
141
|
+
return payload
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _prepare_import_payload(
|
|
145
|
+
file_path: Path,
|
|
146
|
+
overrides: Mapping[str, Any],
|
|
147
|
+
*,
|
|
148
|
+
drop_model_fields: bool = False,
|
|
149
|
+
) -> dict[str, Any]:
|
|
150
|
+
"""Prepare merged payload from file contents and explicit overrides."""
|
|
151
|
+
overrides_dict = dict(overrides)
|
|
152
|
+
|
|
153
|
+
raw_definition = load_resource_from_file(file_path)
|
|
154
|
+
original_refs = {
|
|
155
|
+
"tools": list(raw_definition.get("tools") or []),
|
|
156
|
+
"agents": list(raw_definition.get("agents") or []),
|
|
157
|
+
"mcps": list(raw_definition.get("mcps") or []),
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
base_payload = _load_agent_file_payload(
|
|
161
|
+
file_path, model_override=overrides_dict.get("model")
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
cli_args = {
|
|
165
|
+
key: overrides_dict.get(key)
|
|
166
|
+
for key in (
|
|
167
|
+
"name",
|
|
168
|
+
"instruction",
|
|
169
|
+
"model",
|
|
170
|
+
"tools",
|
|
171
|
+
"agents",
|
|
172
|
+
"mcps",
|
|
173
|
+
"timeout",
|
|
174
|
+
)
|
|
175
|
+
if overrides_dict.get(key) is not None
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for field in _MERGED_SEQUENCE_FIELDS:
|
|
179
|
+
if field in cli_args:
|
|
180
|
+
cli_args[field] = tuple(_normalise_sequence(cli_args[field]) or [])
|
|
181
|
+
|
|
182
|
+
merged = merge_import_with_cli_args(base_payload, cli_args)
|
|
183
|
+
|
|
184
|
+
additional = {
|
|
185
|
+
key: value
|
|
186
|
+
for key, value in overrides_dict.items()
|
|
187
|
+
if value is not None and key not in cli_args
|
|
188
|
+
}
|
|
189
|
+
merged.update(additional)
|
|
190
|
+
|
|
191
|
+
if drop_model_fields:
|
|
192
|
+
if overrides_dict.get("language_model_id") is None:
|
|
193
|
+
merged.pop("language_model_id", None)
|
|
194
|
+
if overrides_dict.get("provider") is None:
|
|
195
|
+
merged.pop("provider", None)
|
|
196
|
+
|
|
197
|
+
merged.setdefault("_tool_refs", original_refs["tools"])
|
|
198
|
+
merged.setdefault("_agent_refs", original_refs["agents"])
|
|
199
|
+
merged.setdefault("_mcp_refs", original_refs["mcps"])
|
|
200
|
+
|
|
201
|
+
_normalise_sequence_fields(merged)
|
|
202
|
+
return merged
|
|
203
|
+
|
|
53
204
|
|
|
54
205
|
class AgentClient(BaseClient):
|
|
55
206
|
"""Client for agent operations."""
|
|
@@ -68,6 +219,8 @@ class AgentClient(BaseClient):
|
|
|
68
219
|
"""
|
|
69
220
|
super().__init__(parent_client=parent_client, **kwargs)
|
|
70
221
|
self._renderer_manager = AgentRunRenderingManager(logger)
|
|
222
|
+
self._tool_client: ToolClient | None = None
|
|
223
|
+
self._mcp_client: MCPClient | None = None
|
|
71
224
|
|
|
72
225
|
def list_agents(
|
|
73
226
|
self,
|
|
@@ -219,129 +372,407 @@ class AgentClient(BaseClient):
|
|
|
219
372
|
finished_monotonic,
|
|
220
373
|
)
|
|
221
374
|
|
|
222
|
-
def
|
|
375
|
+
def _get_tool_client(self) -> ToolClient:
|
|
376
|
+
if self._tool_client is None:
|
|
377
|
+
self._tool_client = ToolClient(parent_client=self)
|
|
378
|
+
return self._tool_client
|
|
379
|
+
|
|
380
|
+
def _get_mcp_client(self) -> MCPClient:
|
|
381
|
+
if self._mcp_client is None:
|
|
382
|
+
self._mcp_client = MCPClient(parent_client=self)
|
|
383
|
+
return self._mcp_client
|
|
384
|
+
|
|
385
|
+
def _normalise_reference_entry(
|
|
223
386
|
self,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
387
|
+
entry: Any,
|
|
388
|
+
fallback_iter: Iterator[Any] | None,
|
|
389
|
+
) -> tuple[str | None, str | None]:
|
|
390
|
+
entry_id: str | None = None
|
|
391
|
+
entry_name: str | None = None
|
|
392
|
+
|
|
393
|
+
if isinstance(entry, str):
|
|
394
|
+
if is_uuid(entry):
|
|
395
|
+
entry_id = entry
|
|
396
|
+
else:
|
|
397
|
+
entry_name = entry
|
|
398
|
+
elif isinstance(entry, dict):
|
|
399
|
+
entry_id = entry.get("id")
|
|
400
|
+
entry_name = entry.get("name")
|
|
401
|
+
else:
|
|
402
|
+
entry_name = str(entry)
|
|
403
|
+
|
|
404
|
+
if entry_name or fallback_iter is None:
|
|
405
|
+
return entry_id, entry_name
|
|
406
|
+
|
|
407
|
+
try:
|
|
408
|
+
ref = next(fallback_iter)
|
|
409
|
+
except StopIteration:
|
|
410
|
+
ref = None
|
|
411
|
+
if isinstance(ref, dict):
|
|
412
|
+
entry_name = ref.get("name") or entry_name
|
|
413
|
+
|
|
414
|
+
return entry_id, entry_name
|
|
415
|
+
|
|
416
|
+
def _resolve_resource_ids(
|
|
417
|
+
self,
|
|
418
|
+
items: list[Any] | None,
|
|
419
|
+
references: list[Any] | None,
|
|
230
420
|
*,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if not
|
|
237
|
-
|
|
421
|
+
fetch_by_id: Callable[[str], Any],
|
|
422
|
+
find_by_name: Callable[[str], list[Any]],
|
|
423
|
+
label: str,
|
|
424
|
+
plural_label: str | None = None,
|
|
425
|
+
) -> list[str] | None:
|
|
426
|
+
if not items:
|
|
427
|
+
return None
|
|
428
|
+
|
|
429
|
+
if references is None:
|
|
430
|
+
return [self._coerce_reference_value(entry) for entry in items]
|
|
431
|
+
|
|
432
|
+
singular = label
|
|
433
|
+
plural = plural_label or f"{label}s"
|
|
434
|
+
fallback_iter = iter(references or [])
|
|
435
|
+
|
|
436
|
+
return [
|
|
437
|
+
self._resolve_single_resource(
|
|
438
|
+
entry,
|
|
439
|
+
fallback_iter,
|
|
440
|
+
fetch_by_id,
|
|
441
|
+
find_by_name,
|
|
442
|
+
singular,
|
|
443
|
+
plural,
|
|
444
|
+
)
|
|
445
|
+
for entry in items
|
|
446
|
+
]
|
|
447
|
+
|
|
448
|
+
def _resolve_single_resource(
|
|
449
|
+
self,
|
|
450
|
+
entry: Any,
|
|
451
|
+
fallback_iter: Iterator[Any] | None,
|
|
452
|
+
fetch_by_id: Callable[[str], Any],
|
|
453
|
+
find_by_name: Callable[[str], list[Any]],
|
|
454
|
+
singular: str,
|
|
455
|
+
plural: str,
|
|
456
|
+
) -> str:
|
|
457
|
+
entry_id, entry_name = self._normalise_reference_entry(entry, fallback_iter)
|
|
458
|
+
|
|
459
|
+
validated_id = self._validate_resource_id(fetch_by_id, entry_id)
|
|
460
|
+
if validated_id:
|
|
461
|
+
return validated_id
|
|
462
|
+
if entry_id and entry_name is None:
|
|
463
|
+
return entry_id
|
|
464
|
+
|
|
465
|
+
if entry_name:
|
|
466
|
+
resolved, success = self._resolve_resource_by_name(
|
|
467
|
+
find_by_name, entry_name, singular, plural
|
|
468
|
+
)
|
|
469
|
+
return resolved if success else entry_name
|
|
470
|
+
|
|
471
|
+
raise ValueError(f"{singular} references must include a valid ID or name.")
|
|
472
|
+
|
|
473
|
+
@staticmethod
|
|
474
|
+
def _coerce_reference_value(entry: Any) -> str:
|
|
475
|
+
if isinstance(entry, dict):
|
|
476
|
+
if entry.get("id"):
|
|
477
|
+
return str(entry["id"])
|
|
478
|
+
if entry.get("name"):
|
|
479
|
+
return str(entry["name"])
|
|
480
|
+
return str(entry)
|
|
481
|
+
|
|
482
|
+
@staticmethod
|
|
483
|
+
def _validate_resource_id(
|
|
484
|
+
fetch_by_id: Callable[[str], Any], candidate_id: str | None
|
|
485
|
+
) -> str | None:
|
|
486
|
+
if not candidate_id:
|
|
487
|
+
return None
|
|
488
|
+
try:
|
|
489
|
+
fetch_by_id(candidate_id)
|
|
490
|
+
except Exception:
|
|
491
|
+
return None
|
|
492
|
+
return candidate_id
|
|
493
|
+
|
|
494
|
+
@staticmethod
|
|
495
|
+
def _resolve_resource_by_name(
|
|
496
|
+
find_by_name: Callable[[str], list[Any]],
|
|
497
|
+
entry_name: str,
|
|
498
|
+
singular: str,
|
|
499
|
+
plural: str,
|
|
500
|
+
) -> tuple[str, bool]:
|
|
501
|
+
try:
|
|
502
|
+
matches = find_by_name(entry_name)
|
|
503
|
+
except Exception:
|
|
504
|
+
return entry_name, False
|
|
505
|
+
|
|
506
|
+
if not matches:
|
|
507
|
+
raise ValueError(
|
|
508
|
+
f"{singular} '{entry_name}' not found in current workspace."
|
|
509
|
+
)
|
|
510
|
+
if len(matches) > 1:
|
|
511
|
+
exact = [
|
|
512
|
+
m
|
|
513
|
+
for m in matches
|
|
514
|
+
if getattr(m, "name", "").lower() == entry_name.lower()
|
|
515
|
+
]
|
|
516
|
+
if len(exact) == 1:
|
|
517
|
+
matches = exact
|
|
518
|
+
else:
|
|
519
|
+
raise ValueError(
|
|
520
|
+
f"Multiple {plural} named '{entry_name}'. Please disambiguate."
|
|
521
|
+
)
|
|
522
|
+
return str(matches[0].id), True
|
|
238
523
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
524
|
+
def _resolve_tool_ids(
|
|
525
|
+
self,
|
|
526
|
+
tools: list[Any] | None,
|
|
527
|
+
references: list[Any] | None = None,
|
|
528
|
+
) -> list[str] | None:
|
|
529
|
+
tool_client = self._get_tool_client()
|
|
530
|
+
return self._resolve_resource_ids(
|
|
531
|
+
tools,
|
|
532
|
+
references,
|
|
533
|
+
fetch_by_id=tool_client.get_tool_by_id,
|
|
534
|
+
find_by_name=tool_client.find_tools,
|
|
535
|
+
label="Tool",
|
|
536
|
+
plural_label="tools",
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
def _resolve_agent_ids(
|
|
540
|
+
self,
|
|
541
|
+
agents: list[Any] | None,
|
|
542
|
+
references: list[Any] | None = None,
|
|
543
|
+
) -> list[str] | None:
|
|
544
|
+
return self._resolve_resource_ids(
|
|
545
|
+
agents,
|
|
546
|
+
references,
|
|
547
|
+
fetch_by_id=self.get_agent_by_id,
|
|
548
|
+
find_by_name=self.find_agents,
|
|
549
|
+
label="Agent",
|
|
550
|
+
plural_label="agents",
|
|
257
551
|
)
|
|
258
552
|
|
|
553
|
+
def _resolve_mcp_ids(
|
|
554
|
+
self,
|
|
555
|
+
mcps: list[Any] | None,
|
|
556
|
+
references: list[Any] | None = None,
|
|
557
|
+
) -> list[str] | None:
|
|
558
|
+
mcp_client = self._get_mcp_client()
|
|
559
|
+
return self._resolve_resource_ids(
|
|
560
|
+
mcps,
|
|
561
|
+
references,
|
|
562
|
+
fetch_by_id=mcp_client.get_mcp_by_id,
|
|
563
|
+
find_by_name=mcp_client.find_mcps,
|
|
564
|
+
label="MCP",
|
|
565
|
+
plural_label="MCPs",
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
def _create_agent_from_payload(self, payload: Mapping[str, Any]) -> "Agent":
|
|
569
|
+
"""Create an agent using a fully prepared payload mapping."""
|
|
570
|
+
known, extras = _split_known_and_extra(
|
|
571
|
+
payload, AgentCreateRequest.__dataclass_fields__
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
name = known.pop("name", None)
|
|
575
|
+
instruction = known.pop("instruction", None)
|
|
576
|
+
if not name or not str(name).strip():
|
|
577
|
+
raise ValueError("Agent name cannot be empty or whitespace")
|
|
578
|
+
if not instruction or not str(instruction).strip():
|
|
579
|
+
raise ValueError("Agent instruction cannot be empty or whitespace")
|
|
580
|
+
|
|
581
|
+
validated_instruction = validate_agent_instruction(str(instruction))
|
|
582
|
+
_normalise_sequence_fields(known)
|
|
583
|
+
|
|
584
|
+
resolved_model = known.pop("model", None) or DEFAULT_MODEL
|
|
585
|
+
tool_refs = extras.pop("_tool_refs", None)
|
|
586
|
+
agent_refs = extras.pop("_agent_refs", None)
|
|
587
|
+
mcp_refs = extras.pop("_mcp_refs", None)
|
|
588
|
+
|
|
589
|
+
tools_raw = known.pop("tools", None)
|
|
590
|
+
agents_raw = known.pop("agents", None)
|
|
591
|
+
mcps_raw = known.pop("mcps", None)
|
|
592
|
+
|
|
593
|
+
resolved_tools = self._resolve_tool_ids(tools_raw, tool_refs)
|
|
594
|
+
resolved_agents = self._resolve_agent_ids(agents_raw, agent_refs)
|
|
595
|
+
resolved_mcps = self._resolve_mcp_ids(mcps_raw, mcp_refs)
|
|
596
|
+
|
|
597
|
+
final_extras = {**known, **extras}
|
|
598
|
+
final_extras.setdefault("model", resolved_model)
|
|
599
|
+
|
|
259
600
|
request = AgentCreateRequest(
|
|
260
|
-
name=name,
|
|
261
|
-
instruction=
|
|
262
|
-
model=
|
|
263
|
-
language_model_id=language_model_id,
|
|
264
|
-
provider=
|
|
265
|
-
model_name=
|
|
266
|
-
agent_type=agent_type,
|
|
267
|
-
framework=framework,
|
|
268
|
-
version=version,
|
|
269
|
-
account_id=account_id,
|
|
270
|
-
description=description,
|
|
271
|
-
metadata=metadata,
|
|
272
|
-
tools=
|
|
273
|
-
agents=
|
|
274
|
-
mcps=
|
|
275
|
-
tool_configs=tool_configs,
|
|
276
|
-
agent_config=agent_config,
|
|
277
|
-
timeout=timeout,
|
|
278
|
-
a2a_profile=a2a_profile,
|
|
279
|
-
extras=
|
|
601
|
+
name=str(name).strip(),
|
|
602
|
+
instruction=validated_instruction,
|
|
603
|
+
model=resolved_model,
|
|
604
|
+
language_model_id=known.pop("language_model_id", None),
|
|
605
|
+
provider=known.pop("provider", None),
|
|
606
|
+
model_name=known.pop("model_name", None),
|
|
607
|
+
agent_type=known.pop("agent_type", known.pop("type", DEFAULT_AGENT_TYPE)),
|
|
608
|
+
framework=known.pop("framework", None) or DEFAULT_AGENT_FRAMEWORK,
|
|
609
|
+
version=known.pop("version", None) or DEFAULT_AGENT_VERSION,
|
|
610
|
+
account_id=known.pop("account_id", None),
|
|
611
|
+
description=known.pop("description", None),
|
|
612
|
+
metadata=known.pop("metadata", None),
|
|
613
|
+
tools=resolved_tools,
|
|
614
|
+
agents=resolved_agents,
|
|
615
|
+
mcps=resolved_mcps,
|
|
616
|
+
tool_configs=known.pop("tool_configs", None),
|
|
617
|
+
agent_config=known.pop("agent_config", None),
|
|
618
|
+
timeout=known.pop("timeout", None) or DEFAULT_AGENT_RUN_TIMEOUT,
|
|
619
|
+
a2a_profile=known.pop("a2a_profile", None),
|
|
620
|
+
extras=final_extras,
|
|
280
621
|
)
|
|
281
622
|
|
|
282
|
-
|
|
623
|
+
payload_dict = request.to_payload()
|
|
624
|
+
payload_dict.setdefault("model", resolved_model)
|
|
283
625
|
|
|
284
626
|
full_agent_data = self._post_then_fetch(
|
|
285
627
|
id_key="id",
|
|
286
628
|
post_endpoint=AGENTS_ENDPOINT,
|
|
287
629
|
get_endpoint_fmt=f"{AGENTS_ENDPOINT}{{id}}",
|
|
288
|
-
json=
|
|
630
|
+
json=payload_dict,
|
|
289
631
|
)
|
|
290
632
|
return Agent(**full_agent_data)._set_client(self)
|
|
291
633
|
|
|
292
|
-
def
|
|
634
|
+
def create_agent(
|
|
293
635
|
self,
|
|
294
|
-
agent_id: str,
|
|
295
636
|
name: str | None = None,
|
|
296
637
|
instruction: str | None = None,
|
|
297
638
|
model: str | None = None,
|
|
639
|
+
tools: list[str | Any] | None = None,
|
|
640
|
+
agents: list[str | Any] | None = None,
|
|
641
|
+
timeout: int | None = None,
|
|
642
|
+
*,
|
|
643
|
+
file: str | PathLike[str] | None = None,
|
|
644
|
+
mcps: list[str | Any] | None = None,
|
|
645
|
+
tool_configs: Mapping[str, Any] | None = None,
|
|
298
646
|
**kwargs: Any,
|
|
299
647
|
) -> "Agent":
|
|
300
|
-
"""
|
|
301
|
-
|
|
648
|
+
"""Create a new agent, optionally loading configuration from a file."""
|
|
649
|
+
base_overrides = {
|
|
650
|
+
"name": name,
|
|
651
|
+
"instruction": instruction,
|
|
652
|
+
"model": model,
|
|
653
|
+
"tools": tools,
|
|
654
|
+
"agents": agents,
|
|
655
|
+
"timeout": timeout,
|
|
656
|
+
"mcps": mcps,
|
|
657
|
+
"tool_configs": tool_configs,
|
|
658
|
+
}
|
|
659
|
+
overrides = _merge_override_maps(base_overrides, kwargs)
|
|
660
|
+
|
|
661
|
+
if file is not None:
|
|
662
|
+
payload = _prepare_import_payload(
|
|
663
|
+
Path(file).expanduser(), overrides, drop_model_fields=True
|
|
664
|
+
)
|
|
665
|
+
if overrides.get("model") is None:
|
|
666
|
+
payload.pop("model", None)
|
|
667
|
+
else:
|
|
668
|
+
payload = overrides
|
|
669
|
+
|
|
670
|
+
return self._create_agent_from_payload(payload)
|
|
302
671
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
672
|
+
def create_agent_from_file( # pragma: no cover - thin compatibility wrapper
|
|
673
|
+
self,
|
|
674
|
+
file_path: str | PathLike[str],
|
|
675
|
+
**overrides: Any,
|
|
676
|
+
) -> "Agent":
|
|
677
|
+
"""Backward-compatible helper to create an agent from a configuration file."""
|
|
678
|
+
return self.create_agent(file=file_path, **overrides)
|
|
679
|
+
|
|
680
|
+
def _update_agent_from_payload(
|
|
681
|
+
self,
|
|
682
|
+
agent_id: str,
|
|
683
|
+
current_agent: Agent,
|
|
684
|
+
payload: Mapping[str, Any],
|
|
685
|
+
) -> "Agent":
|
|
686
|
+
"""Update an agent using a prepared payload mapping."""
|
|
687
|
+
known, extras = _split_known_and_extra(
|
|
688
|
+
payload, AgentUpdateRequest.__dataclass_fields__
|
|
689
|
+
)
|
|
690
|
+
_normalise_sequence_fields(known)
|
|
691
|
+
|
|
692
|
+
tool_refs = extras.pop("_tool_refs", None)
|
|
693
|
+
agent_refs = extras.pop("_agent_refs", None)
|
|
694
|
+
mcp_refs = extras.pop("_mcp_refs", None)
|
|
695
|
+
|
|
696
|
+
tools_value = known.pop("tools", None)
|
|
697
|
+
agents_value = known.pop("agents", None)
|
|
698
|
+
mcps_value = known.pop("mcps", None)
|
|
699
|
+
|
|
700
|
+
if tools_value is not None:
|
|
701
|
+
tools_value = self._resolve_tool_ids(tools_value, tool_refs)
|
|
702
|
+
if agents_value is not None:
|
|
703
|
+
agents_value = self._resolve_agent_ids(agents_value, agent_refs)
|
|
704
|
+
if mcps_value is not None:
|
|
705
|
+
mcps_value = self._resolve_mcp_ids(mcps_value, mcp_refs) # pragma: no cover
|
|
318
706
|
|
|
319
707
|
request = AgentUpdateRequest(
|
|
320
|
-
name=name,
|
|
321
|
-
instruction=instruction,
|
|
322
|
-
description=description,
|
|
323
|
-
model=model,
|
|
324
|
-
language_model_id=language_model_id,
|
|
325
|
-
provider=
|
|
326
|
-
model_name=
|
|
327
|
-
agent_type=
|
|
328
|
-
framework=
|
|
329
|
-
version=
|
|
330
|
-
account_id=account_id,
|
|
331
|
-
metadata=metadata,
|
|
332
|
-
tools=
|
|
333
|
-
tool_configs=tool_configs,
|
|
708
|
+
name=known.pop("name", None),
|
|
709
|
+
instruction=known.pop("instruction", None),
|
|
710
|
+
description=known.pop("description", None),
|
|
711
|
+
model=known.pop("model", None),
|
|
712
|
+
language_model_id=known.pop("language_model_id", None),
|
|
713
|
+
provider=known.pop("provider", None),
|
|
714
|
+
model_name=known.pop("model_name", None),
|
|
715
|
+
agent_type=known.pop("agent_type", known.pop("type", None)),
|
|
716
|
+
framework=known.pop("framework", None),
|
|
717
|
+
version=known.pop("version", None),
|
|
718
|
+
account_id=known.pop("account_id", None),
|
|
719
|
+
metadata=known.pop("metadata", None),
|
|
720
|
+
tools=tools_value,
|
|
721
|
+
tool_configs=known.pop("tool_configs", None),
|
|
334
722
|
agents=agents_value,
|
|
335
|
-
mcps=
|
|
336
|
-
agent_config=agent_config,
|
|
337
|
-
a2a_profile=a2a_profile,
|
|
338
|
-
extras=
|
|
723
|
+
mcps=mcps_value,
|
|
724
|
+
agent_config=known.pop("agent_config", None),
|
|
725
|
+
a2a_profile=known.pop("a2a_profile", None),
|
|
726
|
+
extras={**known, **extras},
|
|
339
727
|
)
|
|
340
728
|
|
|
341
|
-
|
|
729
|
+
payload_dict = request.to_payload(current_agent)
|
|
342
730
|
|
|
343
|
-
|
|
344
|
-
return Agent(**
|
|
731
|
+
response = self._request("PUT", f"/agents/{agent_id}", json=payload_dict)
|
|
732
|
+
return Agent(**response)._set_client(self)
|
|
733
|
+
|
|
734
|
+
def update_agent(
|
|
735
|
+
self,
|
|
736
|
+
agent_id: str,
|
|
737
|
+
name: str | None = None,
|
|
738
|
+
instruction: str | None = None,
|
|
739
|
+
model: str | None = None,
|
|
740
|
+
*,
|
|
741
|
+
file: str | PathLike[str] | None = None,
|
|
742
|
+
tools: list[str | Any] | None = None,
|
|
743
|
+
agents: list[str | Any] | None = None,
|
|
744
|
+
mcps: list[str | Any] | None = None,
|
|
745
|
+
**kwargs: Any,
|
|
746
|
+
) -> "Agent":
|
|
747
|
+
"""Update an existing agent."""
|
|
748
|
+
base_overrides = {
|
|
749
|
+
"name": name,
|
|
750
|
+
"instruction": instruction,
|
|
751
|
+
"model": model,
|
|
752
|
+
"tools": tools,
|
|
753
|
+
"agents": agents,
|
|
754
|
+
"mcps": mcps,
|
|
755
|
+
}
|
|
756
|
+
overrides = _merge_override_maps(base_overrides, kwargs)
|
|
757
|
+
|
|
758
|
+
if file is not None:
|
|
759
|
+
payload = _prepare_import_payload(
|
|
760
|
+
Path(file).expanduser(), overrides, drop_model_fields=True
|
|
761
|
+
)
|
|
762
|
+
else:
|
|
763
|
+
payload = overrides
|
|
764
|
+
|
|
765
|
+
current_agent = self.get_agent_by_id(agent_id)
|
|
766
|
+
return self._update_agent_from_payload(agent_id, current_agent, payload)
|
|
767
|
+
|
|
768
|
+
def update_agent_from_file( # pragma: no cover - thin compatibility wrapper
|
|
769
|
+
self,
|
|
770
|
+
agent_id: str,
|
|
771
|
+
file_path: str | PathLike[str],
|
|
772
|
+
**overrides: Any,
|
|
773
|
+
) -> "Agent":
|
|
774
|
+
"""Backward-compatible helper to update an agent from a configuration file."""
|
|
775
|
+
return self.update_agent(agent_id, file=file_path, **overrides)
|
|
345
776
|
|
|
346
777
|
def delete_agent(self, agent_id: str) -> None:
|
|
347
778
|
"""Delete an agent."""
|
glaip_sdk/client/main.py
CHANGED
|
@@ -42,6 +42,10 @@ class Client(BaseClient):
|
|
|
42
42
|
"""Create a new agent."""
|
|
43
43
|
return self.agents.create_agent(**kwargs)
|
|
44
44
|
|
|
45
|
+
def create_agent_from_file(self, *args, **kwargs) -> Agent:
|
|
46
|
+
"""Create a new agent from a JSON or YAML configuration file."""
|
|
47
|
+
return self.agents.create_agent_from_file(*args, **kwargs)
|
|
48
|
+
|
|
45
49
|
def list_agents(
|
|
46
50
|
self,
|
|
47
51
|
agent_type: str | None = None,
|
|
@@ -86,6 +90,10 @@ class Client(BaseClient):
|
|
|
86
90
|
"""Update an existing agent."""
|
|
87
91
|
return self.agents.update_agent(agent_id, **kwargs)
|
|
88
92
|
|
|
93
|
+
def update_agent_from_file(self, agent_id: str, *args, **kwargs) -> Agent:
|
|
94
|
+
"""Update an existing agent using a JSON or YAML configuration file."""
|
|
95
|
+
return self.agents.update_agent_from_file(agent_id, *args, **kwargs)
|
|
96
|
+
|
|
89
97
|
def delete_agent(self, agent_id: str) -> bool:
|
|
90
98
|
"""Delete an agent."""
|
|
91
99
|
return self.agents.delete_agent(agent_id)
|
glaip_sdk/config/constants.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: glaip-sdk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.17
|
|
4
4
|
Summary: Python SDK for GL AIP (GDP Labs AI Agent Package) - Simplified CLI Design
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Raymond Christopher
|
|
@@ -22,7 +22,7 @@ Requires-Dist: readchar (>=4.2.1,<5.0.0)
|
|
|
22
22
|
Requires-Dist: rich (>=13.0.0)
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
|
|
25
|
-
# GL AIP
|
|
25
|
+
# GL AIP — GDP Labs AI Agents Package
|
|
26
26
|
|
|
27
27
|
[](https://www.python.org/downloads/)
|
|
28
28
|
[](https://github.com/psf/black)
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
glaip_sdk/__init__.py,sha256
|
|
1
|
+
glaip_sdk/__init__.py,sha256=-Itm_1dz4QwhIUmqagvCwwvSjKYKYJI3lZSHD2bwtko,366
|
|
2
2
|
glaip_sdk/_version.py,sha256=tGkFWAVu2ry4Hy7j-u7ophGbPRX8y-ngBbXDhN1VBIQ,2007
|
|
3
|
-
glaip_sdk/branding.py,sha256=
|
|
3
|
+
glaip_sdk/branding.py,sha256=RwXVIc_gMDayKsNN4HBqo8c1fodTMvGFNhPnEZxpRIQ,6410
|
|
4
4
|
glaip_sdk/cli/__init__.py,sha256=xCCfuF1Yc7mpCDcfhHZTX0vizvtrDSLeT8MJ3V7m5A0,156
|
|
5
5
|
glaip_sdk/cli/agent_config.py,sha256=VHjebw68wAdhGUzYdPH8qz10oADZPRgUQcPW6F7iHIU,2421
|
|
6
6
|
glaip_sdk/cli/auth.py,sha256=eYdtGmJ3XgiO96hq_69GF6b3W-aRWZrDQ-6bHuaRX4M,13517
|
|
7
7
|
glaip_sdk/cli/commands/__init__.py,sha256=x0CZlZbZHoHvuzfoTWIyEch6WmNnbPzxajrox6riYp0,173
|
|
8
|
-
glaip_sdk/cli/commands/agents.py,sha256=
|
|
9
|
-
glaip_sdk/cli/commands/configure.py,sha256=
|
|
10
|
-
glaip_sdk/cli/commands/mcps.py,sha256=
|
|
8
|
+
glaip_sdk/cli/commands/agents.py,sha256=FtWGhbl4QRlqxXFNMEnZUpw5mjQ0KPjY0_6o0hYyoaU,41251
|
|
9
|
+
glaip_sdk/cli/commands/configure.py,sha256=h2GgBpnBWYHAhp3zqkAiy8QNgwPD_7pToZBZRpuMoNM,7869
|
|
10
|
+
glaip_sdk/cli/commands/mcps.py,sha256=V41Te3IG8jNFRMb1v1XQV1t461uxpPsrmDFIO6cZPOk,28185
|
|
11
11
|
glaip_sdk/cli/commands/models.py,sha256=G1ce-wZOfvMP6SMnIVuSQ89CF444Kz8Ja6nrNOQXCqU,1729
|
|
12
|
-
glaip_sdk/cli/commands/tools.py,sha256=
|
|
12
|
+
glaip_sdk/cli/commands/tools.py,sha256=YfkB7HRBGcAOC6N-wXTV5Ch5XTXqjKTtyq-Cfb0-18c,18908
|
|
13
13
|
glaip_sdk/cli/config.py,sha256=jCLJxTBAnOU6EJI6JjcpwUTEAWCJRoALbMrhOvvAofc,946
|
|
14
14
|
glaip_sdk/cli/context.py,sha256=M4weRf8dmp5bMtPLRF3w1StnRB7Lo8FPFq2GQMv3Rv8,3617
|
|
15
|
-
glaip_sdk/cli/display.py,sha256=
|
|
15
|
+
glaip_sdk/cli/display.py,sha256=fNb2aBEV4V76TaRQ-L6EO-1fwq71VlzekDzZRTYKEPA,10758
|
|
16
16
|
glaip_sdk/cli/io.py,sha256=GPkw3pQMLBGoD5GH-KlbKpNRlVWFZOXHE17F7V3kQsI,3343
|
|
17
17
|
glaip_sdk/cli/main.py,sha256=gIF4b2mtLrWktsVdYcoHO91S07twtfn1w4D4RCqAMfc,13499
|
|
18
18
|
glaip_sdk/cli/masking.py,sha256=BOZjwUqxQf3LQlYgUMwq7UYgve8x4_1Qk04ixiJJPZ8,4399
|
|
@@ -21,23 +21,24 @@ glaip_sdk/cli/pager.py,sha256=n9ypOGPPSaseJlwPG1X38qSz1yV3pjRWunzA4xx5E7M,8052
|
|
|
21
21
|
glaip_sdk/cli/parsers/__init__.py,sha256=Ycd4HDfYmA7GUGFt0ndBPBo5uTbv15XsXnYUj-a89ug,183
|
|
22
22
|
glaip_sdk/cli/parsers/json_input.py,sha256=6NqzVM5l8g0pwCNLKeTVL9SeLM9W_Fl4H__X0dfaQEA,4039
|
|
23
23
|
glaip_sdk/cli/resolution.py,sha256=jXUNpKKhs30n7Ke0uz1Hbny5DTo2_sxvchIhTbeBubE,2393
|
|
24
|
+
glaip_sdk/cli/rich_helpers.py,sha256=ByUOmK16IisoXWE7nEiI55BF1KWDrm6KCYAxqHu0XOU,825
|
|
24
25
|
glaip_sdk/cli/slash/__init__.py,sha256=Vdv6Y8bu-pA8dxDlyP4XrhudBPivztUozhLAz9vaLig,682
|
|
25
26
|
glaip_sdk/cli/slash/agent_session.py,sha256=-woZkqH70YUSaEHDF9XpxP-cbh36Jx7yuJW7aA3JszI,7078
|
|
26
27
|
glaip_sdk/cli/slash/prompt.py,sha256=2CLAfdmX6yQedcNLnwZ4g6QFoV9TVv8il9OF8iaJwdc,7977
|
|
27
28
|
glaip_sdk/cli/slash/session.py,sha256=JsTHZB8gPFFdcp_bhtw-nig37Z61OWK_okPMx47H86c,31484
|
|
28
29
|
glaip_sdk/cli/update_notifier.py,sha256=nfQ-jRQKn-nZyt7EhxNfZq9Z7nBrYjZJKAgAtuHffnw,3410
|
|
29
|
-
glaip_sdk/cli/utils.py,sha256=
|
|
30
|
+
glaip_sdk/cli/utils.py,sha256=pgbV0f5rdjAHeZ-ULCntH7HUG6FdFB9kODv0a9puB40,35503
|
|
30
31
|
glaip_sdk/cli/validators.py,sha256=USbBgY86AwuDHO-Q_g8g7hu-ot4NgITBsWjTWIl62ms,5569
|
|
31
32
|
glaip_sdk/client/__init__.py,sha256=nYLXfBVTTWwKjP0e63iumPYO4k5FifwWaELQPaPIKIg,188
|
|
32
33
|
glaip_sdk/client/_agent_payloads.py,sha256=sYlMzrfAdd8KC37qxokLy2uDd3aOhzQirnv7UYlvwYc,16385
|
|
33
|
-
glaip_sdk/client/agents.py,sha256=
|
|
34
|
+
glaip_sdk/client/agents.py,sha256=GpDlxzhUQaMn9IUq_kCsbZo5TBfwWXFYs1bbpPLiSok,35492
|
|
34
35
|
glaip_sdk/client/base.py,sha256=OPRlAWhZ77rUK0MRGA83-zW5NVhxJ1RgdfcfGOYr8rI,16267
|
|
35
|
-
glaip_sdk/client/main.py,sha256=
|
|
36
|
+
glaip_sdk/client/main.py,sha256=tfyyx9utReq7nXdtHKOCXQIUXXeZ1-D6u4OGTu6h8Es,8632
|
|
36
37
|
glaip_sdk/client/mcps.py,sha256=-O-I15qjbwfSA69mouHY6g5_qgPWC4rM98VJLpOkh1A,8975
|
|
37
38
|
glaip_sdk/client/run_rendering.py,sha256=fXUj1FBw8n-nAzjI_zaG7-Ap_UXXe0z4tMdL7m2R7Ek,9213
|
|
38
39
|
glaip_sdk/client/tools.py,sha256=n8DIiOOf1YU_j9JK3Bx2-rDnkpckPi0MI9Ok2s1kwa4,16634
|
|
39
40
|
glaip_sdk/client/validators.py,sha256=NtPsWjQLjj25LiUnmR-WuS8lL5p4MVRaYT9UVRmj9bo,8809
|
|
40
|
-
glaip_sdk/config/constants.py,sha256=
|
|
41
|
+
glaip_sdk/config/constants.py,sha256=B9CSlYG8LYjQuo_vNpqy-eSks3ej37FMcvJMy6d_F4U,888
|
|
41
42
|
glaip_sdk/exceptions.py,sha256=ILquxC4QGPFR9eY6RpeXzkQsblfsvZMGFqz38ZjeW3E,2345
|
|
42
43
|
glaip_sdk/models.py,sha256=ofhnNOaKK0UK1mDiot73z8qS0-X2IKJXmm7YyOifGzg,8876
|
|
43
44
|
glaip_sdk/payload_schemas/__init__.py,sha256=fJamlkpS3IfS9xyKAQaUbnalvrtG5Ied69OUVAA3xvs,395
|
|
@@ -66,7 +67,7 @@ glaip_sdk/utils/rich_utils.py,sha256=-Ij-1bIJvnVAi6DrfftchIlMcvOTjVmSE0Qqax0EY_s
|
|
|
66
67
|
glaip_sdk/utils/run_renderer.py,sha256=d_VMI6LbvHPUUeRmGqh5wK_lHqDEIAcym2iqpbtDad0,1365
|
|
67
68
|
glaip_sdk/utils/serialization.py,sha256=AFbucakFaCtQDfcgsm2gHZ1iZDA8OaJSZUsS6FhWFR0,12820
|
|
68
69
|
glaip_sdk/utils/validation.py,sha256=QNORcdyvuliEs4EH2_mkDgmoyT9utgl7YNhaf45SEf8,6992
|
|
69
|
-
glaip_sdk-0.0.
|
|
70
|
-
glaip_sdk-0.0.
|
|
71
|
-
glaip_sdk-0.0.
|
|
72
|
-
glaip_sdk-0.0.
|
|
70
|
+
glaip_sdk-0.0.17.dist-info/METADATA,sha256=z1GiwKAgE5eXkCbBfUXrQuMZBUQ14XWuOZhgj5ugyAw,5164
|
|
71
|
+
glaip_sdk-0.0.17.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
72
|
+
glaip_sdk-0.0.17.dist-info/entry_points.txt,sha256=EGs8NO8J1fdFMWA3CsF7sKBEvtHb_fujdCoNPhfMouE,47
|
|
73
|
+
glaip_sdk-0.0.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|