glaip-sdk 0.6.5b6__py3-none-any.whl → 0.7.12__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.
Files changed (127) hide show
  1. glaip_sdk/__init__.py +42 -5
  2. glaip_sdk/agents/base.py +217 -42
  3. glaip_sdk/branding.py +113 -2
  4. glaip_sdk/cli/account_store.py +15 -0
  5. glaip_sdk/cli/auth.py +14 -8
  6. glaip_sdk/cli/commands/accounts.py +1 -1
  7. glaip_sdk/cli/commands/agents/__init__.py +119 -0
  8. glaip_sdk/cli/commands/agents/_common.py +561 -0
  9. glaip_sdk/cli/commands/agents/create.py +151 -0
  10. glaip_sdk/cli/commands/agents/delete.py +64 -0
  11. glaip_sdk/cli/commands/agents/get.py +89 -0
  12. glaip_sdk/cli/commands/agents/list.py +129 -0
  13. glaip_sdk/cli/commands/agents/run.py +264 -0
  14. glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
  15. glaip_sdk/cli/commands/agents/update.py +112 -0
  16. glaip_sdk/cli/commands/common_config.py +15 -12
  17. glaip_sdk/cli/commands/configure.py +2 -3
  18. glaip_sdk/cli/commands/mcps/__init__.py +94 -0
  19. glaip_sdk/cli/commands/mcps/_common.py +459 -0
  20. glaip_sdk/cli/commands/mcps/connect.py +82 -0
  21. glaip_sdk/cli/commands/mcps/create.py +152 -0
  22. glaip_sdk/cli/commands/mcps/delete.py +73 -0
  23. glaip_sdk/cli/commands/mcps/get.py +212 -0
  24. glaip_sdk/cli/commands/mcps/list.py +69 -0
  25. glaip_sdk/cli/commands/mcps/tools.py +235 -0
  26. glaip_sdk/cli/commands/mcps/update.py +190 -0
  27. glaip_sdk/cli/commands/models.py +2 -4
  28. glaip_sdk/cli/commands/shared/__init__.py +21 -0
  29. glaip_sdk/cli/commands/shared/formatters.py +91 -0
  30. glaip_sdk/cli/commands/tools/__init__.py +69 -0
  31. glaip_sdk/cli/commands/tools/_common.py +80 -0
  32. glaip_sdk/cli/commands/tools/create.py +228 -0
  33. glaip_sdk/cli/commands/tools/delete.py +61 -0
  34. glaip_sdk/cli/commands/tools/get.py +103 -0
  35. glaip_sdk/cli/commands/tools/list.py +69 -0
  36. glaip_sdk/cli/commands/tools/script.py +49 -0
  37. glaip_sdk/cli/commands/tools/update.py +102 -0
  38. glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
  39. glaip_sdk/cli/commands/transcripts/_common.py +9 -0
  40. glaip_sdk/cli/commands/transcripts/clear.py +5 -0
  41. glaip_sdk/cli/commands/transcripts/detail.py +5 -0
  42. glaip_sdk/cli/commands/{transcripts.py → transcripts_original.py} +2 -1
  43. glaip_sdk/cli/commands/update.py +163 -17
  44. glaip_sdk/cli/config.py +1 -0
  45. glaip_sdk/cli/core/output.py +12 -7
  46. glaip_sdk/cli/entrypoint.py +20 -0
  47. glaip_sdk/cli/main.py +127 -39
  48. glaip_sdk/cli/pager.py +3 -3
  49. glaip_sdk/cli/resolution.py +2 -1
  50. glaip_sdk/cli/slash/accounts_controller.py +112 -32
  51. glaip_sdk/cli/slash/agent_session.py +5 -2
  52. glaip_sdk/cli/slash/prompt.py +11 -0
  53. glaip_sdk/cli/slash/remote_runs_controller.py +3 -1
  54. glaip_sdk/cli/slash/session.py +369 -23
  55. glaip_sdk/cli/slash/tui/__init__.py +26 -1
  56. glaip_sdk/cli/slash/tui/accounts.tcss +79 -5
  57. glaip_sdk/cli/slash/tui/accounts_app.py +1027 -88
  58. glaip_sdk/cli/slash/tui/clipboard.py +195 -0
  59. glaip_sdk/cli/slash/tui/context.py +87 -0
  60. glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
  61. glaip_sdk/cli/slash/tui/layouts/__init__.py +14 -0
  62. glaip_sdk/cli/slash/tui/layouts/harlequin.py +160 -0
  63. glaip_sdk/cli/slash/tui/remote_runs_app.py +119 -12
  64. glaip_sdk/cli/slash/tui/terminal.py +407 -0
  65. glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
  66. glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
  67. glaip_sdk/cli/slash/tui/theme/manager.py +112 -0
  68. glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
  69. glaip_sdk/cli/slash/tui/toast.py +374 -0
  70. glaip_sdk/cli/transcript/history.py +1 -1
  71. glaip_sdk/cli/transcript/viewer.py +5 -3
  72. glaip_sdk/cli/tui_settings.py +125 -0
  73. glaip_sdk/cli/update_notifier.py +215 -7
  74. glaip_sdk/cli/validators.py +1 -1
  75. glaip_sdk/client/__init__.py +2 -1
  76. glaip_sdk/client/_schedule_payloads.py +89 -0
  77. glaip_sdk/client/agents.py +50 -8
  78. glaip_sdk/client/hitl.py +136 -0
  79. glaip_sdk/client/main.py +7 -1
  80. glaip_sdk/client/mcps.py +44 -13
  81. glaip_sdk/client/payloads/agent/__init__.py +23 -0
  82. glaip_sdk/client/{_agent_payloads.py → payloads/agent/requests.py} +22 -47
  83. glaip_sdk/client/payloads/agent/responses.py +43 -0
  84. glaip_sdk/client/run_rendering.py +414 -3
  85. glaip_sdk/client/schedules.py +439 -0
  86. glaip_sdk/client/tools.py +57 -26
  87. glaip_sdk/guardrails/__init__.py +80 -0
  88. glaip_sdk/guardrails/serializer.py +89 -0
  89. glaip_sdk/hitl/__init__.py +48 -0
  90. glaip_sdk/hitl/base.py +64 -0
  91. glaip_sdk/hitl/callback.py +43 -0
  92. glaip_sdk/hitl/local.py +121 -0
  93. glaip_sdk/hitl/remote.py +523 -0
  94. glaip_sdk/models/__init__.py +17 -0
  95. glaip_sdk/models/agent_runs.py +2 -1
  96. glaip_sdk/models/schedule.py +224 -0
  97. glaip_sdk/payload_schemas/agent.py +1 -0
  98. glaip_sdk/payload_schemas/guardrails.py +34 -0
  99. glaip_sdk/registry/tool.py +273 -59
  100. glaip_sdk/runner/__init__.py +20 -3
  101. glaip_sdk/runner/deps.py +5 -8
  102. glaip_sdk/runner/langgraph.py +318 -42
  103. glaip_sdk/runner/logging_config.py +77 -0
  104. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +104 -5
  105. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +72 -7
  106. glaip_sdk/schedules/__init__.py +22 -0
  107. glaip_sdk/schedules/base.py +291 -0
  108. glaip_sdk/tools/base.py +67 -14
  109. glaip_sdk/utils/__init__.py +1 -0
  110. glaip_sdk/utils/bundler.py +138 -2
  111. glaip_sdk/utils/import_resolver.py +43 -11
  112. glaip_sdk/utils/rendering/renderer/base.py +58 -0
  113. glaip_sdk/utils/runtime_config.py +15 -12
  114. glaip_sdk/utils/sync.py +31 -11
  115. glaip_sdk/utils/tool_detection.py +274 -6
  116. glaip_sdk/utils/tool_storage_provider.py +140 -0
  117. {glaip_sdk-0.6.5b6.dist-info → glaip_sdk-0.7.12.dist-info}/METADATA +49 -37
  118. glaip_sdk-0.7.12.dist-info/RECORD +219 -0
  119. {glaip_sdk-0.6.5b6.dist-info → glaip_sdk-0.7.12.dist-info}/WHEEL +2 -1
  120. glaip_sdk-0.7.12.dist-info/entry_points.txt +2 -0
  121. glaip_sdk-0.7.12.dist-info/top_level.txt +1 -0
  122. glaip_sdk/cli/commands/agents.py +0 -1509
  123. glaip_sdk/cli/commands/mcps.py +0 -1356
  124. glaip_sdk/cli/commands/tools.py +0 -576
  125. glaip_sdk/cli/utils.py +0 -263
  126. glaip_sdk-0.6.5b6.dist-info/RECORD +0 -159
  127. glaip_sdk-0.6.5b6.dist-info/entry_points.txt +0 -3
@@ -0,0 +1,64 @@
1
+ """Delete agent command.
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
+ import click
12
+
13
+ from glaip_sdk.cli.context import output_flags
14
+ from glaip_sdk.cli.display import (
15
+ display_confirmation_prompt,
16
+ display_deletion_success,
17
+ handle_json_output,
18
+ handle_rich_output,
19
+ )
20
+ from glaip_sdk.cli.core.context import get_client
21
+
22
+ from ._common import (
23
+ _handle_click_exception_for_json,
24
+ _handle_command_exception,
25
+ agents_group,
26
+ )
27
+
28
+
29
+ @agents_group.command()
30
+ @click.argument("agent_id")
31
+ @click.option("-y", "--yes", is_flag=True, help="Skip confirmation")
32
+ @output_flags()
33
+ @click.pass_context
34
+ def delete(ctx: Any, agent_id: str, yes: bool) -> None:
35
+ """Delete an agent."""
36
+ try:
37
+ client = get_client(ctx)
38
+
39
+ # Get agent by ID (no ambiguity handling needed)
40
+ try:
41
+ agent = client.agents.get_agent_by_id(agent_id)
42
+ except Exception as e:
43
+ raise click.ClickException(f"Agent with ID '{agent_id}' not found: {e}") from e
44
+
45
+ # Confirm deletion when not forced
46
+ if not yes:
47
+ if not display_confirmation_prompt("Agent", agent.name):
48
+ return
49
+
50
+ client.agents.delete_agent(agent.id)
51
+
52
+ handle_json_output(
53
+ ctx,
54
+ {
55
+ "success": True,
56
+ "message": f"Agent '{agent.name}' deleted",
57
+ },
58
+ )
59
+ handle_rich_output(ctx, display_deletion_success("Agent", agent.name))
60
+
61
+ except click.ClickException as e:
62
+ _handle_click_exception_for_json(ctx, e)
63
+ except Exception as e:
64
+ _handle_command_exception(ctx, e)
@@ -0,0 +1,89 @@
1
+ """Get agent command.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ import click
13
+
14
+ from glaip_sdk.cli.context import output_flags
15
+ from glaip_sdk.cli.display import display_agent_run_suggestions, handle_rich_output
16
+ from glaip_sdk.cli.core.context import get_client
17
+ from glaip_sdk.cli.core.output import handle_resource_export
18
+
19
+ from ._common import _display_agent_details, _resolve_agent, agents_group, console
20
+
21
+
22
+ @agents_group.command()
23
+ @click.argument("agent_ref")
24
+ @click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
25
+ @click.option(
26
+ "--export",
27
+ type=click.Path(dir_okay=False, writable=True),
28
+ help="Export complete agent configuration to file (format auto-detected from .json/.yaml extension)",
29
+ )
30
+ @click.option(
31
+ "--instruction-preview",
32
+ type=int,
33
+ default=0,
34
+ show_default=True,
35
+ help="Instruction preview length when printing instructions (0 shows full prompt).",
36
+ )
37
+ @output_flags()
38
+ @click.pass_context
39
+ def get(
40
+ ctx: Any,
41
+ agent_ref: str,
42
+ select: int | None,
43
+ export: str | None,
44
+ instruction_preview: int,
45
+ ) -> None:
46
+ r"""Get agent details.
47
+
48
+ \b
49
+ Examples:
50
+ aip agents get my-agent
51
+ aip agents get my-agent --export agent.json # Exports complete configuration as JSON
52
+ aip agents get my-agent --export agent.yaml # Exports complete configuration as YAML
53
+ """
54
+ try:
55
+ # Initialize API client for agent retrieval
56
+ api_client = get_client(ctx)
57
+
58
+ # Resolve agent reference using questionary interface for better UX
59
+ agent = _resolve_agent(ctx, api_client, agent_ref, select, interface_preference="questionary")
60
+
61
+ if not agent:
62
+ raise click.ClickException(f"Agent '{agent_ref}' not found")
63
+
64
+ # Handle export option if requested
65
+ if export:
66
+ handle_resource_export(
67
+ ctx,
68
+ agent,
69
+ Path(export),
70
+ resource_type="agent",
71
+ get_by_id_func=api_client.agents.get_agent_by_id,
72
+ console_override=console,
73
+ )
74
+
75
+ # Display full agent details using the standardized helper
76
+ _display_agent_details(
77
+ ctx,
78
+ api_client,
79
+ agent,
80
+ instruction_preview_limit=instruction_preview,
81
+ )
82
+
83
+ # Show run suggestions via centralized display helper
84
+ handle_rich_output(ctx, display_agent_run_suggestions(agent))
85
+
86
+ except click.ClickException:
87
+ raise
88
+ except Exception as e:
89
+ raise click.ClickException(str(e)) from e
@@ -0,0 +1,129 @@
1
+ """List agents command.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ from typing import Any
11
+
12
+ import click
13
+
14
+ from glaip_sdk.branding import ACCENT_STYLE, INFO, SUCCESS, WARNING_STYLE
15
+ from glaip_sdk.cli.context import output_flags
16
+ from glaip_sdk.cli.display import display_agent_run_suggestions, handle_rich_output
17
+ from glaip_sdk.cli.core.output import coerce_to_row, output_list
18
+ from glaip_sdk.cli.core.prompting import _fuzzy_pick_for_resources
19
+ from glaip_sdk.cli.core.rendering import with_client_and_spinner
20
+ from glaip_sdk.icons import ICON_AGENT
21
+
22
+ from ._common import _display_agent_details, agents_group, console
23
+
24
+
25
+ @agents_group.command(name="list")
26
+ @click.option("--simple", is_flag=True, help="Show simple table without interactive picker")
27
+ @click.option("--type", "agent_type", help="Filter by agent type (config, code, a2a, langflow)")
28
+ @click.option("--framework", help="Filter by framework (langchain, langgraph, google_adk)")
29
+ @click.option("--name", help="Filter by partial name match (case-insensitive)")
30
+ @click.option("--version", help="Filter by exact version match")
31
+ @click.option(
32
+ "--sync-langflow",
33
+ is_flag=True,
34
+ help="Sync with LangFlow server before listing (only applies when filtering by langflow type)",
35
+ )
36
+ @output_flags()
37
+ @click.pass_context
38
+ def list_agents(
39
+ ctx: Any,
40
+ simple: bool,
41
+ agent_type: str | None,
42
+ framework: str | None,
43
+ name: str | None,
44
+ version: str | None,
45
+ sync_langflow: bool,
46
+ ) -> None:
47
+ """List agents with optional filtering."""
48
+ try:
49
+ with with_client_and_spinner(
50
+ ctx,
51
+ "[bold blue]Fetching agents…[/bold blue]",
52
+ console_override=console,
53
+ ) as client:
54
+ # Query agents with specified filters
55
+ filter_params = {
56
+ "agent_type": agent_type,
57
+ "framework": framework,
58
+ "name": name,
59
+ "version": version,
60
+ "sync_langflow_agents": sync_langflow,
61
+ }
62
+ agents = client.agents.list_agents(**filter_params)
63
+
64
+ # Define table columns: (data_key, header, style, width)
65
+ columns = [
66
+ ("id", "ID", "dim", 36),
67
+ ("name", "Name", ACCENT_STYLE, None),
68
+ ("type", "Type", WARNING_STYLE, None),
69
+ ("framework", "Framework", INFO, None),
70
+ ("version", "Version", SUCCESS, None),
71
+ ]
72
+
73
+ # Transform function for safe attribute access
74
+ def transform_agent(agent: Any) -> dict[str, Any]:
75
+ """Transform an agent object to a display row dictionary.
76
+
77
+ Args:
78
+ agent: Agent object to transform.
79
+
80
+ Returns:
81
+ Dictionary with id, name, type, framework, and version fields.
82
+ """
83
+ row = coerce_to_row(agent, ["id", "name", "type", "framework", "version"])
84
+ # Ensure id is always a string
85
+ row["id"] = str(row["id"])
86
+ return row
87
+
88
+ # Use fuzzy picker for interactive agent selection and details (default behavior)
89
+ # Skip if --simple flag is used, a name filter is applied, or non-rich output is requested
90
+ ctx_obj = ctx.obj if isinstance(getattr(ctx, "obj", None), dict) else {}
91
+ current_view = ctx_obj.get("view")
92
+ interactive_enabled = (
93
+ not simple
94
+ and name is None
95
+ and current_view not in {"json", "plain", "md"}
96
+ and console.is_terminal
97
+ and os.isatty(1)
98
+ and len(agents) > 0
99
+ )
100
+
101
+ # Track picker attempt so the fallback table doesn't re-open the palette
102
+ picker_attempted = False
103
+ if interactive_enabled:
104
+ picker_attempted = True
105
+ picked_agent = _fuzzy_pick_for_resources(agents, "agent", "")
106
+ if picked_agent:
107
+ _display_agent_details(ctx, client, picked_agent)
108
+ # Show run suggestions via centralized display helper
109
+ handle_rich_output(ctx, display_agent_run_suggestions(picked_agent))
110
+ return
111
+
112
+ # Show simple table (either --simple flag or non-interactive)
113
+ output_list(
114
+ ctx,
115
+ agents,
116
+ f"{ICON_AGENT} Available Agents",
117
+ columns,
118
+ transform_agent,
119
+ skip_picker=(
120
+ not interactive_enabled
121
+ or picker_attempted
122
+ or simple
123
+ or any(param is not None for param in (agent_type, framework, name, version))
124
+ ),
125
+ use_pager=False,
126
+ )
127
+
128
+ except Exception as e:
129
+ raise click.ClickException(str(e)) from e
@@ -0,0 +1,264 @@
1
+ """Run agent command.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from typing import Any
11
+
12
+ import click
13
+
14
+ from glaip_sdk.cli.context import get_ctx_value, output_flags
15
+ from glaip_sdk.cli.display import handle_json_output
16
+ from glaip_sdk.cli.core.context import get_client
17
+ from glaip_sdk.cli.core.rendering import build_renderer
18
+ from glaip_sdk.cli.transcript import maybe_launch_post_run_viewer, store_transcript_for_session
19
+ from glaip_sdk.cli.rich_helpers import print_markup
20
+ from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
21
+ from glaip_sdk.exceptions import AgentTimeoutError
22
+ from glaip_sdk.utils.rendering.renderer.toggle import TranscriptToggleController
23
+
24
+ from ._common import (
25
+ _emit_verbose_guidance,
26
+ _get_agent_model_name,
27
+ _handle_command_exception,
28
+ _resolve_agent,
29
+ _running_in_slash_mode,
30
+ _safe_agent_attribute,
31
+ agents_group,
32
+ console,
33
+ )
34
+
35
+ from glaip_sdk.branding import SUCCESS_STYLE
36
+
37
+
38
+ def _validate_run_input(input_option: str | None, input_text: str | None) -> str:
39
+ """Validate and determine the final input text for agent run."""
40
+ final_input_text = input_option if input_option else input_text
41
+
42
+ if not final_input_text:
43
+ raise click.ClickException("Input text is required. Use either positional argument or --input option.")
44
+
45
+ return final_input_text
46
+
47
+
48
+ def _parse_chat_history(chat_history: str | None) -> list[dict[str, Any]] | None:
49
+ """Parse chat history JSON if provided."""
50
+ if not chat_history:
51
+ return None
52
+
53
+ try:
54
+ return json.loads(chat_history)
55
+ except json.JSONDecodeError as err:
56
+ raise click.ClickException("Invalid JSON in chat history") from err
57
+
58
+
59
+ def _setup_run_renderer(ctx: Any, save: str | None, verbose: bool) -> Any:
60
+ """Set up renderer and working console for agent run."""
61
+ tty_enabled = bool(get_ctx_value(ctx, "tty", True))
62
+ return build_renderer(
63
+ ctx,
64
+ save_path=save,
65
+ verbose=verbose,
66
+ _tty_enabled=tty_enabled,
67
+ )
68
+
69
+
70
+ def _maybe_attach_transcript_toggle(ctx: Any, renderer: Any) -> None:
71
+ """Attach transcript toggle controller when interactive TTY is available."""
72
+ if renderer is None:
73
+ return
74
+
75
+ console_obj = getattr(renderer, "console", None)
76
+ if console_obj is None or not getattr(console_obj, "is_terminal", False):
77
+ return
78
+
79
+ tty_enabled = bool(get_ctx_value(ctx, "tty", True))
80
+ if not tty_enabled:
81
+ return
82
+
83
+ controller = TranscriptToggleController(enabled=True)
84
+ renderer.transcript_controller = controller
85
+
86
+
87
+ def _prepare_run_kwargs(
88
+ agent: Any,
89
+ final_input_text: str,
90
+ files: list[str] | None,
91
+ parsed_chat_history: list[dict[str, Any]] | None,
92
+ renderer: Any,
93
+ tty_enabled: bool,
94
+ ) -> dict[str, Any]:
95
+ """Prepare kwargs for agent run."""
96
+ run_kwargs = {
97
+ "agent_id": agent.id,
98
+ "message": final_input_text,
99
+ "files": list(files),
100
+ "agent_name": agent.name,
101
+ "tty": tty_enabled,
102
+ }
103
+
104
+ if parsed_chat_history:
105
+ run_kwargs["chat_history"] = parsed_chat_history
106
+
107
+ if renderer is not None:
108
+ run_kwargs["renderer"] = renderer
109
+
110
+ return run_kwargs
111
+
112
+
113
+ def _handle_run_output(ctx: Any, result: Any, renderer: Any) -> None:
114
+ """Handle output formatting for agent run results."""
115
+ printed_by_renderer = bool(renderer)
116
+ selected_view = get_ctx_value(ctx, "view", "rich")
117
+
118
+ if not printed_by_renderer:
119
+ if selected_view == "json":
120
+ handle_json_output(ctx, {"output": result})
121
+ elif selected_view == "md":
122
+ click.echo(f"# Assistant\n\n{result}")
123
+ elif selected_view == "plain":
124
+ click.echo(result)
125
+
126
+
127
+ def _save_run_transcript(save: str | None, result: Any, working_console: Any) -> None:
128
+ """Save transcript to file if requested."""
129
+ if not save:
130
+ return
131
+
132
+ ext = (save.rsplit(".", 1)[-1] or "").lower()
133
+ if ext == "json":
134
+ save_data = {
135
+ "output": result or "",
136
+ "full_debug_output": getattr(working_console, "get_captured_output", lambda: "")(),
137
+ "timestamp": "captured during agent execution",
138
+ }
139
+ content = json.dumps(save_data, indent=2)
140
+ else:
141
+ full_output = getattr(working_console, "get_captured_output", lambda: "")()
142
+ if full_output:
143
+ content = f"# Agent Debug Log\n\n{full_output}\n\n---\n\n## Final Result\n\n{result or ''}\n"
144
+ else:
145
+ content = f"# Assistant\n\n{result or ''}\n"
146
+
147
+ with open(save, "w", encoding="utf-8") as f:
148
+ f.write(content)
149
+ print_markup(f"[{SUCCESS_STYLE}]Full debug output saved to: {save}[/]", console=console)
150
+
151
+
152
+ @agents_group.command()
153
+ @click.argument("agent_ref")
154
+ @click.argument("input_text", required=False)
155
+ @click.option("--select", type=int, help="Choose among ambiguous matches (1-based)")
156
+ @click.option("--input", "input_option", help="Input text for the agent")
157
+ @click.option("--chat-history", help="JSON string of chat history")
158
+ @click.option(
159
+ "--timeout",
160
+ default=DEFAULT_AGENT_RUN_TIMEOUT,
161
+ type=int,
162
+ help="Agent execution timeout in seconds (default: 300s)",
163
+ )
164
+ @click.option(
165
+ "--save",
166
+ type=click.Path(dir_okay=False, writable=True),
167
+ help="Save transcript to file (md or json)",
168
+ )
169
+ @click.option(
170
+ "--file",
171
+ "files",
172
+ multiple=True,
173
+ type=click.Path(exists=True),
174
+ help="Attach file(s)",
175
+ )
176
+ @click.option(
177
+ "--verbose/--no-verbose",
178
+ default=False,
179
+ help="Show detailed SSE events during streaming",
180
+ )
181
+ @output_flags()
182
+ @click.pass_context
183
+ def run(
184
+ ctx: Any,
185
+ agent_ref: str,
186
+ select: int | None,
187
+ input_text: str | None,
188
+ input_option: str | None,
189
+ chat_history: str | None,
190
+ timeout: float | None,
191
+ save: str | None,
192
+ files: tuple[str, ...] | None,
193
+ verbose: bool,
194
+ ) -> None:
195
+ r"""Run an agent with input text.
196
+
197
+ Usage: aip agents run <agent_ref> <input_text> [OPTIONS]
198
+
199
+ \b
200
+ Examples:
201
+ aip agents run my-agent "Hello world"
202
+ aip agents run agent-123 "Process this data" --timeout 600
203
+ aip agents run my-agent --input "Hello world" # Legacy style
204
+ """
205
+ final_input_text = _validate_run_input(input_option, input_text)
206
+
207
+ if verbose:
208
+ _emit_verbose_guidance(ctx)
209
+ return
210
+
211
+ try:
212
+ client = get_client(ctx)
213
+ agent = _resolve_agent(ctx, client, agent_ref, select, interface_preference="fuzzy")
214
+
215
+ parsed_chat_history = _parse_chat_history(chat_history)
216
+ renderer, working_console = _setup_run_renderer(ctx, save, verbose)
217
+ _maybe_attach_transcript_toggle(ctx, renderer)
218
+
219
+ try:
220
+ client.timeout = float(timeout)
221
+ except Exception: # pragma: no cover # Defensive - timeout is int per Click, so float() always succeeds
222
+ pass
223
+
224
+ run_kwargs = _prepare_run_kwargs(
225
+ agent,
226
+ final_input_text,
227
+ files,
228
+ parsed_chat_history,
229
+ renderer,
230
+ bool(get_ctx_value(ctx, "tty", True)),
231
+ )
232
+
233
+ result = client.agents.run_agent(**run_kwargs, timeout=timeout)
234
+
235
+ slash_mode = _running_in_slash_mode(ctx)
236
+ agent_id = str(_safe_agent_attribute(agent, "id") or "") or None
237
+ agent_name = _safe_agent_attribute(agent, "name")
238
+ model_hint = _get_agent_model_name(agent)
239
+
240
+ transcript_context = store_transcript_for_session(
241
+ ctx,
242
+ renderer,
243
+ final_result=result,
244
+ agent_id=agent_id,
245
+ agent_name=agent_name,
246
+ model=model_hint,
247
+ source="slash" if slash_mode else "cli",
248
+ )
249
+
250
+ _handle_run_output(ctx, result, renderer)
251
+ _save_run_transcript(save, result, working_console)
252
+ maybe_launch_post_run_viewer(
253
+ ctx,
254
+ transcript_context,
255
+ console=console,
256
+ slash_mode=slash_mode,
257
+ )
258
+
259
+ except AgentTimeoutError as e:
260
+ error_msg = str(e)
261
+ handle_json_output(ctx, error=Exception(error_msg))
262
+ raise click.ClickException(error_msg) from e
263
+ except Exception as e:
264
+ _handle_command_exception(ctx, e)
@@ -0,0 +1,72 @@
1
+ """Sync LangFlow agents command.
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
+ import click
12
+
13
+ from glaip_sdk.cli.context import get_ctx_value, output_flags
14
+ from glaip_sdk.cli.display import handle_json_output, handle_rich_output
15
+ from glaip_sdk.cli.core.context import get_client
16
+ from glaip_sdk.cli.rich_helpers import markup_text
17
+
18
+ from ._common import _handle_command_exception, agents_group
19
+
20
+ from glaip_sdk.branding import SUCCESS_STYLE
21
+
22
+
23
+ @agents_group.command()
24
+ @click.option(
25
+ "--base-url",
26
+ help="Custom LangFlow server base URL (overrides LANGFLOW_BASE_URL env var)",
27
+ )
28
+ @click.option("--api-key", help="Custom LangFlow API key (overrides LANGFLOW_API_KEY env var)")
29
+ @output_flags()
30
+ @click.pass_context
31
+ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
32
+ r"""Sync agents with LangFlow server flows.
33
+
34
+ This command fetches all flows from the configured LangFlow server and
35
+ creates/updates corresponding agents in the platform.
36
+
37
+ The LangFlow server configuration can be provided via:
38
+ - Command options (--base-url, --api-key)
39
+ - Environment variables (LANGFLOW_BASE_URL, LANGFLOW_API_KEY)
40
+
41
+ \b
42
+ Examples:
43
+ aip agents sync-langflow
44
+ aip agents sync-langflow --base-url https://my-langflow.com --api-key my-key
45
+ """
46
+ try:
47
+ client = get_client(ctx)
48
+
49
+ # Perform the sync
50
+ result = client.sync_langflow_agents(base_url=base_url, api_key=api_key)
51
+
52
+ # Handle output format
53
+ handle_json_output(ctx, result)
54
+
55
+ # Show success message for non-JSON output
56
+ if get_ctx_value(ctx, "view") != "json":
57
+ # Extract some useful info from the result
58
+ success_count = result.get("data", {}).get("created_count", 0) + result.get("data", {}).get(
59
+ "updated_count", 0
60
+ )
61
+ total_count = result.get("data", {}).get("total_processed", 0)
62
+
63
+ handle_rich_output(
64
+ ctx,
65
+ markup_text(
66
+ f"[{SUCCESS_STYLE}]✅ Successfully synced {success_count} LangFlow agents "
67
+ f"({total_count} total processed)[/]"
68
+ ),
69
+ )
70
+
71
+ except Exception as e:
72
+ _handle_command_exception(ctx, e)