glaip-sdk 0.0.0b99__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 (207) hide show
  1. glaip_sdk/__init__.py +52 -0
  2. glaip_sdk/_version.py +81 -0
  3. glaip_sdk/agents/__init__.py +27 -0
  4. glaip_sdk/agents/base.py +1227 -0
  5. glaip_sdk/branding.py +211 -0
  6. glaip_sdk/cli/__init__.py +9 -0
  7. glaip_sdk/cli/account_store.py +540 -0
  8. glaip_sdk/cli/agent_config.py +78 -0
  9. glaip_sdk/cli/auth.py +705 -0
  10. glaip_sdk/cli/commands/__init__.py +5 -0
  11. glaip_sdk/cli/commands/accounts.py +746 -0
  12. glaip_sdk/cli/commands/agents/__init__.py +119 -0
  13. glaip_sdk/cli/commands/agents/_common.py +561 -0
  14. glaip_sdk/cli/commands/agents/create.py +151 -0
  15. glaip_sdk/cli/commands/agents/delete.py +64 -0
  16. glaip_sdk/cli/commands/agents/get.py +89 -0
  17. glaip_sdk/cli/commands/agents/list.py +129 -0
  18. glaip_sdk/cli/commands/agents/run.py +264 -0
  19. glaip_sdk/cli/commands/agents/sync_langflow.py +72 -0
  20. glaip_sdk/cli/commands/agents/update.py +112 -0
  21. glaip_sdk/cli/commands/common_config.py +104 -0
  22. glaip_sdk/cli/commands/configure.py +895 -0
  23. glaip_sdk/cli/commands/mcps/__init__.py +94 -0
  24. glaip_sdk/cli/commands/mcps/_common.py +459 -0
  25. glaip_sdk/cli/commands/mcps/connect.py +82 -0
  26. glaip_sdk/cli/commands/mcps/create.py +152 -0
  27. glaip_sdk/cli/commands/mcps/delete.py +73 -0
  28. glaip_sdk/cli/commands/mcps/get.py +212 -0
  29. glaip_sdk/cli/commands/mcps/list.py +69 -0
  30. glaip_sdk/cli/commands/mcps/tools.py +235 -0
  31. glaip_sdk/cli/commands/mcps/update.py +190 -0
  32. glaip_sdk/cli/commands/models.py +67 -0
  33. glaip_sdk/cli/commands/shared/__init__.py +21 -0
  34. glaip_sdk/cli/commands/shared/formatters.py +91 -0
  35. glaip_sdk/cli/commands/tools/__init__.py +69 -0
  36. glaip_sdk/cli/commands/tools/_common.py +80 -0
  37. glaip_sdk/cli/commands/tools/create.py +228 -0
  38. glaip_sdk/cli/commands/tools/delete.py +61 -0
  39. glaip_sdk/cli/commands/tools/get.py +103 -0
  40. glaip_sdk/cli/commands/tools/list.py +69 -0
  41. glaip_sdk/cli/commands/tools/script.py +49 -0
  42. glaip_sdk/cli/commands/tools/update.py +102 -0
  43. glaip_sdk/cli/commands/transcripts/__init__.py +90 -0
  44. glaip_sdk/cli/commands/transcripts/_common.py +9 -0
  45. glaip_sdk/cli/commands/transcripts/clear.py +5 -0
  46. glaip_sdk/cli/commands/transcripts/detail.py +5 -0
  47. glaip_sdk/cli/commands/transcripts_original.py +756 -0
  48. glaip_sdk/cli/commands/update.py +192 -0
  49. glaip_sdk/cli/config.py +95 -0
  50. glaip_sdk/cli/constants.py +38 -0
  51. glaip_sdk/cli/context.py +150 -0
  52. glaip_sdk/cli/core/__init__.py +79 -0
  53. glaip_sdk/cli/core/context.py +124 -0
  54. glaip_sdk/cli/core/output.py +851 -0
  55. glaip_sdk/cli/core/prompting.py +649 -0
  56. glaip_sdk/cli/core/rendering.py +187 -0
  57. glaip_sdk/cli/display.py +355 -0
  58. glaip_sdk/cli/hints.py +57 -0
  59. glaip_sdk/cli/io.py +112 -0
  60. glaip_sdk/cli/main.py +686 -0
  61. glaip_sdk/cli/masking.py +136 -0
  62. glaip_sdk/cli/mcp_validators.py +287 -0
  63. glaip_sdk/cli/pager.py +266 -0
  64. glaip_sdk/cli/parsers/__init__.py +7 -0
  65. glaip_sdk/cli/parsers/json_input.py +177 -0
  66. glaip_sdk/cli/resolution.py +68 -0
  67. glaip_sdk/cli/rich_helpers.py +27 -0
  68. glaip_sdk/cli/slash/__init__.py +15 -0
  69. glaip_sdk/cli/slash/accounts_controller.py +580 -0
  70. glaip_sdk/cli/slash/accounts_shared.py +75 -0
  71. glaip_sdk/cli/slash/agent_session.py +285 -0
  72. glaip_sdk/cli/slash/prompt.py +256 -0
  73. glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
  74. glaip_sdk/cli/slash/session.py +1724 -0
  75. glaip_sdk/cli/slash/tui/__init__.py +34 -0
  76. glaip_sdk/cli/slash/tui/accounts.tcss +88 -0
  77. glaip_sdk/cli/slash/tui/accounts_app.py +933 -0
  78. glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
  79. glaip_sdk/cli/slash/tui/clipboard.py +147 -0
  80. glaip_sdk/cli/slash/tui/context.py +59 -0
  81. glaip_sdk/cli/slash/tui/keybind_registry.py +235 -0
  82. glaip_sdk/cli/slash/tui/loading.py +58 -0
  83. glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
  84. glaip_sdk/cli/slash/tui/terminal.py +402 -0
  85. glaip_sdk/cli/slash/tui/theme/__init__.py +15 -0
  86. glaip_sdk/cli/slash/tui/theme/catalog.py +79 -0
  87. glaip_sdk/cli/slash/tui/theme/manager.py +86 -0
  88. glaip_sdk/cli/slash/tui/theme/tokens.py +55 -0
  89. glaip_sdk/cli/slash/tui/toast.py +123 -0
  90. glaip_sdk/cli/transcript/__init__.py +31 -0
  91. glaip_sdk/cli/transcript/cache.py +536 -0
  92. glaip_sdk/cli/transcript/capture.py +329 -0
  93. glaip_sdk/cli/transcript/export.py +38 -0
  94. glaip_sdk/cli/transcript/history.py +815 -0
  95. glaip_sdk/cli/transcript/launcher.py +77 -0
  96. glaip_sdk/cli/transcript/viewer.py +374 -0
  97. glaip_sdk/cli/update_notifier.py +369 -0
  98. glaip_sdk/cli/validators.py +238 -0
  99. glaip_sdk/client/__init__.py +12 -0
  100. glaip_sdk/client/_schedule_payloads.py +89 -0
  101. glaip_sdk/client/agent_runs.py +147 -0
  102. glaip_sdk/client/agents.py +1353 -0
  103. glaip_sdk/client/base.py +502 -0
  104. glaip_sdk/client/main.py +253 -0
  105. glaip_sdk/client/mcps.py +401 -0
  106. glaip_sdk/client/payloads/agent/__init__.py +23 -0
  107. glaip_sdk/client/payloads/agent/requests.py +495 -0
  108. glaip_sdk/client/payloads/agent/responses.py +43 -0
  109. glaip_sdk/client/run_rendering.py +747 -0
  110. glaip_sdk/client/schedules.py +439 -0
  111. glaip_sdk/client/shared.py +21 -0
  112. glaip_sdk/client/tools.py +690 -0
  113. glaip_sdk/client/validators.py +198 -0
  114. glaip_sdk/config/constants.py +52 -0
  115. glaip_sdk/exceptions.py +113 -0
  116. glaip_sdk/hitl/__init__.py +15 -0
  117. glaip_sdk/hitl/local.py +151 -0
  118. glaip_sdk/icons.py +25 -0
  119. glaip_sdk/mcps/__init__.py +21 -0
  120. glaip_sdk/mcps/base.py +345 -0
  121. glaip_sdk/models/__init__.py +107 -0
  122. glaip_sdk/models/agent.py +47 -0
  123. glaip_sdk/models/agent_runs.py +117 -0
  124. glaip_sdk/models/common.py +42 -0
  125. glaip_sdk/models/mcp.py +33 -0
  126. glaip_sdk/models/schedule.py +224 -0
  127. glaip_sdk/models/tool.py +33 -0
  128. glaip_sdk/payload_schemas/__init__.py +7 -0
  129. glaip_sdk/payload_schemas/agent.py +85 -0
  130. glaip_sdk/registry/__init__.py +55 -0
  131. glaip_sdk/registry/agent.py +164 -0
  132. glaip_sdk/registry/base.py +139 -0
  133. glaip_sdk/registry/mcp.py +253 -0
  134. glaip_sdk/registry/tool.py +393 -0
  135. glaip_sdk/rich_components.py +125 -0
  136. glaip_sdk/runner/__init__.py +59 -0
  137. glaip_sdk/runner/base.py +84 -0
  138. glaip_sdk/runner/deps.py +112 -0
  139. glaip_sdk/runner/langgraph.py +870 -0
  140. glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
  141. glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
  142. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
  143. glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
  144. glaip_sdk/runner/tool_adapter/__init__.py +18 -0
  145. glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
  146. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
  147. glaip_sdk/schedules/__init__.py +22 -0
  148. glaip_sdk/schedules/base.py +291 -0
  149. glaip_sdk/tools/__init__.py +22 -0
  150. glaip_sdk/tools/base.py +466 -0
  151. glaip_sdk/utils/__init__.py +86 -0
  152. glaip_sdk/utils/a2a/__init__.py +34 -0
  153. glaip_sdk/utils/a2a/event_processor.py +188 -0
  154. glaip_sdk/utils/agent_config.py +194 -0
  155. glaip_sdk/utils/bundler.py +267 -0
  156. glaip_sdk/utils/client.py +111 -0
  157. glaip_sdk/utils/client_utils.py +486 -0
  158. glaip_sdk/utils/datetime_helpers.py +58 -0
  159. glaip_sdk/utils/discovery.py +78 -0
  160. glaip_sdk/utils/display.py +135 -0
  161. glaip_sdk/utils/export.py +143 -0
  162. glaip_sdk/utils/general.py +61 -0
  163. glaip_sdk/utils/import_export.py +168 -0
  164. glaip_sdk/utils/import_resolver.py +530 -0
  165. glaip_sdk/utils/instructions.py +101 -0
  166. glaip_sdk/utils/rendering/__init__.py +115 -0
  167. glaip_sdk/utils/rendering/formatting.py +264 -0
  168. glaip_sdk/utils/rendering/layout/__init__.py +64 -0
  169. glaip_sdk/utils/rendering/layout/panels.py +156 -0
  170. glaip_sdk/utils/rendering/layout/progress.py +202 -0
  171. glaip_sdk/utils/rendering/layout/summary.py +74 -0
  172. glaip_sdk/utils/rendering/layout/transcript.py +606 -0
  173. glaip_sdk/utils/rendering/models.py +85 -0
  174. glaip_sdk/utils/rendering/renderer/__init__.py +55 -0
  175. glaip_sdk/utils/rendering/renderer/base.py +1082 -0
  176. glaip_sdk/utils/rendering/renderer/config.py +27 -0
  177. glaip_sdk/utils/rendering/renderer/console.py +55 -0
  178. glaip_sdk/utils/rendering/renderer/debug.py +178 -0
  179. glaip_sdk/utils/rendering/renderer/factory.py +138 -0
  180. glaip_sdk/utils/rendering/renderer/stream.py +202 -0
  181. glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
  182. glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
  183. glaip_sdk/utils/rendering/renderer/toggle.py +182 -0
  184. glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
  185. glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
  186. glaip_sdk/utils/rendering/state.py +204 -0
  187. glaip_sdk/utils/rendering/step_tree_state.py +100 -0
  188. glaip_sdk/utils/rendering/steps/__init__.py +34 -0
  189. glaip_sdk/utils/rendering/steps/event_processor.py +778 -0
  190. glaip_sdk/utils/rendering/steps/format.py +176 -0
  191. glaip_sdk/utils/rendering/steps/manager.py +387 -0
  192. glaip_sdk/utils/rendering/timing.py +36 -0
  193. glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
  194. glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
  195. glaip_sdk/utils/resource_refs.py +195 -0
  196. glaip_sdk/utils/run_renderer.py +41 -0
  197. glaip_sdk/utils/runtime_config.py +425 -0
  198. glaip_sdk/utils/serialization.py +424 -0
  199. glaip_sdk/utils/sync.py +142 -0
  200. glaip_sdk/utils/tool_detection.py +33 -0
  201. glaip_sdk/utils/tool_storage_provider.py +140 -0
  202. glaip_sdk/utils/validation.py +264 -0
  203. glaip_sdk-0.0.0b99.dist-info/METADATA +239 -0
  204. glaip_sdk-0.0.0b99.dist-info/RECORD +207 -0
  205. glaip_sdk-0.0.0b99.dist-info/WHEEL +5 -0
  206. glaip_sdk-0.0.0b99.dist-info/entry_points.txt +2 -0
  207. glaip_sdk-0.0.0b99.dist-info/top_level.txt +1 -0
@@ -0,0 +1,140 @@
1
+ """Helpers for local tool output storage setup.
2
+
3
+ This module bridges agent_config.tool_output_sharing to ToolOutputManager
4
+ for local execution without modifying aip-agents.
5
+
6
+ Authors:
7
+ Fachriza Adhiatma (fachriza.d.adhiatma@gdplabs.id)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ from typing import Any
14
+
15
+ from gllm_core.utils import LoggerManager
16
+
17
+ logger = LoggerManager().get_logger(__name__)
18
+
19
+
20
+ def build_tool_output_manager(agent_name: str, agent_config: dict[str, Any]) -> Any | None:
21
+ """Build a ToolOutputManager for local tool output sharing.
22
+
23
+ Args:
24
+ agent_name: Name of the agent whose tool outputs will be stored.
25
+ agent_config: Agent configuration that may enable tool output sharing and contain task_id.
26
+
27
+ Returns:
28
+ A ToolOutputManager instance when tool output sharing is enabled and
29
+ dependencies are available, otherwise ``None``.
30
+ """
31
+ tool_output_sharing_enabled = agent_config.get("tool_output_sharing", False)
32
+ if not tool_output_sharing_enabled:
33
+ return None
34
+
35
+ try:
36
+ from aip_agents.storage.clients.minio_client import MinioConfig, MinioObjectStorage # noqa: PLC0415
37
+ from aip_agents.storage.providers.memory import InMemoryStorageProvider # noqa: PLC0415
38
+ from aip_agents.storage.providers.object_storage import ObjectStorageProvider # noqa: PLC0415
39
+ from aip_agents.utils.langgraph.tool_output_management import ( # noqa: PLC0415
40
+ ToolOutputConfig,
41
+ ToolOutputManager,
42
+ )
43
+ except ImportError:
44
+ logger.warning("Tool output sharing requested but aip-agents is unavailable; skipping.")
45
+ return None
46
+
47
+ task_id = agent_config.get("task_id")
48
+
49
+ storage_provider = _build_tool_output_storage_provider(
50
+ agent_name=agent_name,
51
+ task_id=task_id,
52
+ minio_config_cls=MinioConfig,
53
+ minio_client_cls=MinioObjectStorage,
54
+ object_storage_provider_cls=ObjectStorageProvider,
55
+ memory_storage_provider_cls=InMemoryStorageProvider,
56
+ )
57
+ tool_output_config = _build_tool_output_config(storage_provider, ToolOutputConfig)
58
+ return ToolOutputManager(tool_output_config)
59
+
60
+
61
+ def _build_tool_output_storage_provider(
62
+ agent_name: str,
63
+ task_id: str | None,
64
+ minio_config_cls: Any,
65
+ minio_client_cls: Any,
66
+ object_storage_provider_cls: Any,
67
+ memory_storage_provider_cls: Any,
68
+ ) -> Any:
69
+ """Create a storage provider for tool output sharing.
70
+
71
+ Args:
72
+ agent_name: Name of the agent whose tool outputs are stored.
73
+ task_id: Optional task identifier for coordination context.
74
+ minio_config_cls: Class exposing a ``from_env`` constructor for MinIO config.
75
+ minio_client_cls: MinIO client class used to talk to the object store.
76
+ object_storage_provider_cls: Storage provider wrapping the MinIO client.
77
+ memory_storage_provider_cls: In-memory provider used as a fallback.
78
+
79
+ Returns:
80
+ An instance of ``object_storage_provider_cls`` when MinIO initialization
81
+ succeeds, otherwise an instance of ``memory_storage_provider_cls``.
82
+ """
83
+ try:
84
+ config_obj = minio_config_cls.from_env()
85
+ minio_client = minio_client_cls(config=config_obj)
86
+ prefix = _build_tool_output_prefix(agent_name, task_id)
87
+ return object_storage_provider_cls(client=minio_client, prefix=prefix, use_json=False)
88
+ except Exception as exc:
89
+ logger.warning("Failed to initialize MinIO for tool outputs: %s. Using in-memory storage.", exc)
90
+ return memory_storage_provider_cls()
91
+
92
+
93
+ def _build_tool_output_prefix(agent_name: str, task_id: str | None) -> str:
94
+ """Build object storage prefix for tool outputs in local mode.
95
+
96
+ Args:
97
+ agent_name: Name of the agent whose outputs are stored.
98
+ task_id: Optional task identifier for coordination context.
99
+
100
+ Returns:
101
+ Object storage key prefix dedicated to the provided agent.
102
+ """
103
+ if task_id:
104
+ return f"tool-outputs/tasks/{task_id}/agents/{agent_name}/"
105
+ return f"tool-outputs/agents/{agent_name}/"
106
+
107
+
108
+ def _build_tool_output_config(storage_provider: Any, config_cls: Any) -> Any:
109
+ """Build ToolOutputConfig using env vars, with safe defaults.
110
+
111
+ Args:
112
+ storage_provider: Provider that will persist tool outputs.
113
+ config_cls: Tool output configuration class to instantiate.
114
+
115
+ Returns:
116
+ A configured ``config_cls`` instance ready for ToolOutputManager use.
117
+ """
118
+
119
+ def safe_int_conversion(env_var: str, default: str) -> int:
120
+ """Convert an environment variable to int with a fallback default.
121
+
122
+ Args:
123
+ env_var: Environment variable name to read.
124
+ default: Default string value used when parsing fails.
125
+
126
+ Returns:
127
+ Integer representation of the environment variable or the default.
128
+ """
129
+ try:
130
+ return int(os.getenv(env_var, default))
131
+ except (ValueError, TypeError):
132
+ logger.warning("Invalid value for %s, using default: %s", env_var, default)
133
+ return int(default)
134
+
135
+ return config_cls(
136
+ max_stored_outputs=safe_int_conversion("TOOL_OUTPUT_MAX_STORED", "200"),
137
+ max_age_minutes=safe_int_conversion("TOOL_OUTPUT_MAX_AGE_MINUTES", str(24 * 60)),
138
+ cleanup_interval=safe_int_conversion("TOOL_OUTPUT_CLEANUP_INTERVAL", "50"),
139
+ storage_provider=storage_provider,
140
+ )
@@ -0,0 +1,264 @@
1
+ """Validation utilities for resource names, timeouts, and other constraints.
2
+
3
+ This module provides pure validation functions that raise ValueError for invalid
4
+ inputs. CLI layer wraps these with click.ClickException for user-friendly errors.
5
+
6
+ Authors:
7
+ Raymond Christopher (raymond.christopher@gdplabs.id)
8
+ """
9
+
10
+ import re
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
15
+ from glaip_sdk.utils.resource_refs import validate_name_format
16
+
17
+ # Constants for validation
18
+ RESERVED_NAMES = ["admin", "root", "system", "api", "test", "demo"]
19
+
20
+
21
+ def _validate_named_resource(name: str, resource_type: str) -> str:
22
+ """Shared validator that prevents reserved-name duplication."""
23
+ cleaned_name = validate_name_format(name, resource_type)
24
+
25
+ if cleaned_name.lower() in RESERVED_NAMES:
26
+ raise ValueError(f"{resource_type.capitalize()} name '{cleaned_name}' is reserved and cannot be used")
27
+
28
+ return cleaned_name
29
+
30
+
31
+ def validate_agent_name(name: str) -> str:
32
+ """Validate agent name and return cleaned version.
33
+
34
+ Args:
35
+ name: Agent name to validate
36
+
37
+ Returns:
38
+ Cleaned agent name
39
+
40
+ Raises:
41
+ ValueError: If name is invalid
42
+ """
43
+ return _validate_named_resource(name, "agent")
44
+
45
+
46
+ def validate_agent_instruction(instruction: str) -> str:
47
+ """Validate agent instruction and return cleaned version.
48
+
49
+ Args:
50
+ instruction: Agent instruction to validate
51
+
52
+ Returns:
53
+ Cleaned agent instruction
54
+
55
+ Raises:
56
+ ValueError: If instruction is invalid
57
+ """
58
+ if not instruction or not instruction.strip():
59
+ raise ValueError("Agent instruction cannot be empty or whitespace")
60
+
61
+ cleaned_instruction = instruction.strip()
62
+
63
+ if len(cleaned_instruction) > 100000:
64
+ raise ValueError("Agent instruction cannot be longer than 100,000 characters")
65
+
66
+ return cleaned_instruction
67
+
68
+
69
+ def validate_tool_name(name: str) -> str:
70
+ """Validate tool name and return cleaned version.
71
+
72
+ Args:
73
+ name: Tool name to validate
74
+
75
+ Returns:
76
+ Cleaned tool name
77
+
78
+ Raises:
79
+ ValueError: If name is invalid
80
+ """
81
+ return _validate_named_resource(name, "tool")
82
+
83
+
84
+ def validate_mcp_name(name: str) -> str:
85
+ """Validate MCP name and return cleaned version.
86
+
87
+ Args:
88
+ name: MCP name to validate
89
+
90
+ Returns:
91
+ Cleaned MCP name
92
+
93
+ Raises:
94
+ ValueError: If name is invalid
95
+ """
96
+ return _validate_named_resource(name, "mcp")
97
+
98
+
99
+ def validate_timeout(timeout: int) -> int:
100
+ """Validate timeout value.
101
+
102
+ Args:
103
+ timeout: Timeout value in seconds
104
+
105
+ Returns:
106
+ Validated timeout value
107
+
108
+ Raises:
109
+ ValueError: If timeout is invalid
110
+ """
111
+ if timeout < 1:
112
+ raise ValueError("Timeout must be at least 1 second")
113
+
114
+ if timeout > 3600: # 1 hour max
115
+ raise ValueError("Timeout cannot be longer than 1 hour (3600 seconds)")
116
+
117
+ return timeout
118
+
119
+
120
+ def coerce_timeout(value: Any) -> int:
121
+ """Coerce timeout value to integer, handling various input types.
122
+
123
+ Args:
124
+ value: The timeout value to coerce (int, float, str, etc.)
125
+
126
+ Returns:
127
+ Integer timeout value
128
+
129
+ Raises:
130
+ ValueError: If value cannot be coerced to valid timeout
131
+
132
+ Examples:
133
+ coerce_timeout(300) -> 300
134
+ coerce_timeout(300.0) -> 300
135
+ coerce_timeout("300") -> 300
136
+ coerce_timeout(None) -> 300 # Uses DEFAULT_AGENT_RUN_TIMEOUT
137
+ """
138
+ if value is None:
139
+ return DEFAULT_AGENT_RUN_TIMEOUT
140
+ elif isinstance(value, int):
141
+ return validate_timeout(value)
142
+ elif isinstance(value, float):
143
+ if value.is_integer():
144
+ return validate_timeout(int(value))
145
+ return validate_timeout(int(value)) # Truncate if not integer
146
+ elif isinstance(value, str):
147
+ try:
148
+ fval = float(value)
149
+ return validate_timeout(int(fval))
150
+ except ValueError as err:
151
+ raise ValueError(f"Invalid timeout value: {value}") from err
152
+ else:
153
+ try:
154
+ return validate_timeout(int(value))
155
+ except (TypeError, ValueError) as err:
156
+ raise ValueError(f"Invalid timeout value: {value}") from err
157
+
158
+
159
+ def validate_file_path(file_path: str | Path, must_exist: bool = True) -> Path:
160
+ """Validate file path.
161
+
162
+ Args:
163
+ file_path: File path to validate
164
+ must_exist: Whether file must exist
165
+
166
+ Returns:
167
+ Path object
168
+
169
+ Raises:
170
+ ValueError: If file path is invalid
171
+ """
172
+ path = Path(file_path)
173
+
174
+ if must_exist and not path.exists():
175
+ raise ValueError(f"File does not exist: {file_path}")
176
+
177
+ if must_exist and not path.is_file():
178
+ raise ValueError(f"Path is not a file: {file_path}")
179
+
180
+ return path
181
+
182
+
183
+ def validate_directory_path(dir_path: str | Path, must_exist: bool = True) -> Path:
184
+ """Validate directory path.
185
+
186
+ Args:
187
+ dir_path: Directory path to validate
188
+ must_exist: Whether directory must exist
189
+
190
+ Returns:
191
+ Path object
192
+
193
+ Raises:
194
+ ValueError: If directory path is invalid
195
+ """
196
+ path = Path(dir_path)
197
+
198
+ if must_exist and not path.exists():
199
+ raise ValueError(f"Directory does not exist: {dir_path}")
200
+
201
+ if must_exist and not path.is_dir():
202
+ raise ValueError(f"Path is not a directory: {dir_path}")
203
+
204
+ return path
205
+
206
+
207
+ def validate_url(url: str) -> str:
208
+ """Validate URL format (HTTPS only).
209
+
210
+ Args:
211
+ url: URL to validate
212
+
213
+ Returns:
214
+ Validated URL
215
+
216
+ Raises:
217
+ ValueError: If URL is invalid
218
+ """
219
+ url_pattern = re.compile(
220
+ r"^https://" # https:// only
221
+ r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|" # domain...
222
+ r"localhost|" # localhost...
223
+ r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # ...or ip
224
+ r"(?::\d+)?" # optional port
225
+ r"(?:/?|[/?]\S+)$",
226
+ re.IGNORECASE,
227
+ )
228
+
229
+ if not url_pattern.match(url):
230
+ raise ValueError("API URL must start with https:// and be a valid host.")
231
+
232
+ return url
233
+
234
+
235
+ def validate_api_key(api_key: str) -> str:
236
+ """Validate API key format.
237
+
238
+ Args:
239
+ api_key: API key to validate
240
+
241
+ Returns:
242
+ Validated API key
243
+
244
+ Raises:
245
+ ValueError: If API key is invalid
246
+ """
247
+ if not api_key or not api_key.strip():
248
+ raise ValueError("API key cannot be empty")
249
+
250
+ cleaned_key = api_key.strip()
251
+
252
+ if len(cleaned_key) < 10:
253
+ raise ValueError("API key appears to be too short")
254
+
255
+ if len(cleaned_key) > 200:
256
+ raise ValueError("API key appears to be too long")
257
+
258
+ # Check for potentially invalid characters
259
+ if not re.match(r"^[a-zA-Z0-9_-]+$", cleaned_key):
260
+ raise ValueError(
261
+ "API key contains invalid characters. Only letters, numbers, hyphens, and underscores are allowed."
262
+ )
263
+
264
+ return cleaned_key
@@ -0,0 +1,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: glaip-sdk
3
+ Version: 0.0.0b99
4
+ Summary: Python SDK and CLI for GL AIP (GDP Labs AI Agent Package) - Build, run, and manage AI agents
5
+ Author-email: Raymond Christopher <raymond.christopher@gdplabs.id>
6
+ License: MIT
7
+ Requires-Python: <3.13,>=3.11
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: httpx>=0.28.1
10
+ Requires-Dist: pydantic>=2.0.0
11
+ Requires-Dist: pyyaml>=6.0.0
12
+ Requires-Dist: python-dotenv<2.0.0,>=1.1.1
13
+ Requires-Dist: readchar<5.0.0,>=4.2.1
14
+ Requires-Dist: questionary<3.0.0,>=2.1.0
15
+ Requires-Dist: click<8.3.0,>=8.2.0
16
+ Requires-Dist: rich>=13.0.0
17
+ Requires-Dist: packaging>=23.2
18
+ Requires-Dist: textual>=0.52.0
19
+ Requires-Dist: gllm-core-binary>=0.1.0
20
+ Requires-Dist: langchain-core>=0.3.0
21
+ Requires-Dist: gllm-tools-binary>=0.1.3
22
+ Provides-Extra: local
23
+ Requires-Dist: aip-agents-binary[local]>=0.5.18; (python_version >= "3.11" and python_version < "3.13") and extra == "local"
24
+ Provides-Extra: memory
25
+ Requires-Dist: aip-agents-binary[memory]>=0.5.18; (python_version >= "3.11" and python_version < "3.13") and extra == "memory"
26
+ Provides-Extra: privacy
27
+ Requires-Dist: aip-agents-binary[privacy]>=0.5.18; (python_version >= "3.11" and python_version < "3.13") and extra == "privacy"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
31
+ Requires-Dist: pytest-dotenv>=0.5.2; extra == "dev"
32
+ Requires-Dist: pre-commit>=4.3.0; extra == "dev"
33
+ Requires-Dist: pytest-xdist>=3.8.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.23.6; extra == "dev"
35
+ Requires-Dist: pytest-timeout>=2.3.1; extra == "dev"
36
+ Requires-Dist: ruff>=0.14.0; extra == "dev"
37
+
38
+ # GL AIP β€” GDP Labs AI Agents Package
39
+
40
+ [![Python 3.11-3.12](https://img.shields.io/badge/python-3.11--3.12-blue.svg)](https://www.python.org/downloads/)
41
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
42
+
43
+ GL stands for **GDP Labs**β€”GL AIP is our AI Agents Package for building, running, and operating agents.
44
+
45
+ > **Python SDK and CLI for GL AIP - Connect, configure, and manage AI agents on the GDP Labs AI Agents Package.**
46
+
47
+ ## πŸš€ Quick Start
48
+
49
+ ### Installation
50
+
51
+ Installing `glaip-sdk` provides both the **Python SDK** and the **`aip` CLI command** in a single package.
52
+
53
+ ```bash
54
+ # Using pip (recommended)
55
+ pip install --upgrade glaip-sdk
56
+
57
+ # Using uv (fast alternative)
58
+ uv tool install glaip-sdk
59
+
60
+ # Using pipx (CLI-focused, isolated environment)
61
+ pipx install glaip-sdk
62
+ ```
63
+
64
+ **Requirements**: Python 3.11 or 3.12
65
+
66
+ **Updating**: The `aip` CLI automatically detects your installation method and uses the correct update command:
67
+
68
+ - If installed via `pip`: Uses `pip install --upgrade glaip-sdk`
69
+ - If installed via `uv tool install`: Uses `uv tool install --upgrade glaip-sdk`
70
+ - You can also update manually using the same command you used to install
71
+
72
+ ## 🐍 Hello World - Python SDK
73
+
74
+ Perfect for building applications and integrations.
75
+
76
+ ### Step 1: Environment Setup
77
+
78
+ Create a `.env` file:
79
+
80
+ ```bash
81
+ # .env
82
+ AIP_API_URL=https://your-gl-aip-instance.com
83
+ AIP_API_KEY=your-api-key
84
+ ```
85
+
86
+ ### Step 2: Basic Python Script
87
+
88
+ ```python
89
+ # hello_world.py
90
+ from glaip_sdk import Client
91
+ import os
92
+ from dotenv import load_dotenv
93
+
94
+ # Load environment variables
95
+ load_dotenv()
96
+
97
+ # Initialize client
98
+ client = Client()
99
+
100
+ # Create a simple agent
101
+ agent = client.agents.create(
102
+ name="hello-sdk",
103
+ instruction="You are a helpful assistant who responds clearly and concisely."
104
+ )
105
+
106
+ # Run the agent
107
+ result = agent.run("Hello world, what's 2+2?")
108
+
109
+ print(f"Agent response: {result}")
110
+ ```
111
+
112
+ ### Step 3: Run Your Script
113
+
114
+ ```bash
115
+ python hello_world.py
116
+ ```
117
+
118
+ ### Step 4: Advanced Example with Streaming
119
+
120
+ ```python
121
+ # streaming_example.py
122
+ from glaip_sdk import Client
123
+ import os
124
+ from dotenv import load_dotenv
125
+
126
+ load_dotenv()
127
+ client = Client()
128
+
129
+ # Create agent with streaming
130
+ agent = client.agents.create(
131
+ name="streaming-agent",
132
+ instruction="You are a helpful assistant. Provide detailed responses."
133
+ )
134
+
135
+ # Stream the response
136
+ print("Streaming response:")
137
+ client.agents.run_agent(
138
+ agent.id,
139
+ "Explain quantum computing in simple terms",
140
+ verbose=True,
141
+ )
142
+ print("--- Stream complete ---")
143
+ ```
144
+
145
+ πŸŽ‰ **SDK Success!** You're now ready to build AI-powered applications with Python.
146
+
147
+ ______________________________________________________________________
148
+
149
+ ## πŸ’» Hello World - CLI
150
+
151
+ Perfect for quick testing and command-line workflows.
152
+
153
+ ### Step 1: Configure Connection
154
+
155
+ ```bash
156
+ # Interactive setup (recommended)
157
+ aip configure
158
+ ```
159
+
160
+ Or set environment variables:
161
+
162
+ ```bash
163
+ export AIP_API_URL="https://your-gl-aip-instance.com"
164
+ export AIP_API_KEY="your-api-key"
165
+ ```
166
+
167
+ ### Step 2: Verify Connection
168
+
169
+ ```bash
170
+ aip status
171
+ ```
172
+
173
+ ### Step 3: Create & Run Your First Agent
174
+
175
+ ```bash
176
+ # Create a simple agent
177
+ aip agents create --name "hello-cli" --instruction "You are a helpful assistant"
178
+
179
+ # List agents to get the ID
180
+ aip agents list
181
+
182
+ # Run the agent with input
183
+ aip agents run <AGENT_ID> --input "Hello world, what's the weather like?"
184
+ ```
185
+
186
+ πŸŽ‰ **CLI Success!** You're now ready to use the CLI for AI agent workflows.
187
+
188
+ ## ✨ Key Features
189
+
190
+ - **πŸ€– Agent Management**: Create, run, and orchestrate AI agents with custom instructions and streaming
191
+ - **🧠 Language Models**: Choose from multiple AI models per agent with manual PII tag mapping
192
+ - **πŸ› οΈ Tool Integration**: Extend agents with custom Python tools and script management
193
+ - **πŸ”Œ MCP Support**: Connect external services through Model Context Protocols with tool discovery
194
+ - **πŸ”„ Multi-Agent Patterns**: Hierarchical, parallel, sequential, router, and aggregator patterns
195
+ - **πŸ’» Modern CLI**: Rich terminal interface with fuzzy search and multiple output formats
196
+
197
+ ## 🌳 Live Steps Panel
198
+
199
+ The CLI steps panel now streams a fully hierarchical tree so you can audit complex agent runs without leaving the terminal.
200
+
201
+ - Renders parent/child relationships with `β”‚β”œβ””` connectors, even when events arrive out of order
202
+ - Marks running steps with spinners and duration badges sourced from SSE metadata before local fallbacks
203
+ - Highlights failures inline (`βœ— reason`) and raises warning glyphs on affected delegate branches
204
+ - Derives deterministic β€œπŸ’­ Thinking…” spans before/after each delegate or tool action to show scheduling gaps
205
+ - Flags parallel work with a dedicated glyph and argument-derived labels so simultaneous tool calls stay readable
206
+ - Try it locally: `poetry run python scripts/replay_steps_log.py --transcript tests/fixtures/rendering/transcripts/parallel_research.jsonl --output /tmp/parallel.log`
207
+
208
+ ## πŸ“š Documentation
209
+
210
+ πŸ“– **[Complete Documentation](https://gdplabs.gitbook.io/gl-aip/gl-aip-sdk/overview)** - Visit our GitBook for comprehensive guides, tutorials, and API reference.
211
+
212
+ Quick links:
213
+
214
+ - **[Quick Start Guide](https://gdplabs.gitbook.io/gl-aip/gl-aip-sdk/get-started/quick-start-guide)**: Get your first agent running in 5 minutes
215
+ - **[Agent Management](https://gdplabs.gitbook.io/gl-aip/gl-aip-sdk/guides/agents-guide)**: Complete agent lifecycle management
216
+ - **[Custom Tools](https://gdplabs.gitbook.io/gl-aip/gl-aip-sdk/guides/tools-guide)**: Build and integrate custom tools
217
+ - **[MCP Integration](https://gdplabs.gitbook.io/gl-aip/gl-aip-sdk/guides/mcps-guide)**: Connect external services
218
+ - **[API Reference](https://gdplabs.gitbook.io/gl-aip/gl-aip-sdk/reference/python-sdk-reference)**: Complete SDK reference
219
+
220
+ ## πŸ§ͺ Simulate the Update Notifier
221
+
222
+ Need to verify the in-session upgrade flow without hitting PyPI or actually running `pip install`? Use the bundled helper:
223
+
224
+ ```bash
225
+ cd python/glaip-sdk
226
+ poetry run python scripts/mock_update_notifier.py
227
+ # or customize the mock payload:
228
+ # poetry run python scripts/mock_update_notifier.py --version 3.3.3 --marker "[nightly build]"
229
+ ```
230
+
231
+ The script:
232
+
233
+ - Launches a SlashSession with prompt-toolkit disabled (so it runs cleanly in tests/CI).
234
+ - Forces the notifier to believe a newer version exists (`--version 9.9.9` by default).
235
+ - Appends a visible marker (default `[mock update]`) to the banner so you can prove the branding reload happened; pass `--marker ""` to skip.
236
+ - Auto-selects β€œUpdate now”, mocks the install step, and runs the real branding refresh logic.
237
+ - Resets module metadata afterwards so your environment remains untouched.
238
+
239
+ You should see the Rich banner re-render with the mocked version (and optional marker) at the end of the run.