glaip-sdk 0.1.3__py3-none-any.whl → 0.6.19__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 (146) hide show
  1. glaip_sdk/__init__.py +44 -4
  2. glaip_sdk/_version.py +9 -0
  3. glaip_sdk/agents/__init__.py +27 -0
  4. glaip_sdk/agents/base.py +1196 -0
  5. glaip_sdk/branding.py +13 -0
  6. glaip_sdk/cli/account_store.py +540 -0
  7. glaip_sdk/cli/auth.py +254 -15
  8. glaip_sdk/cli/commands/__init__.py +2 -2
  9. glaip_sdk/cli/commands/accounts.py +746 -0
  10. glaip_sdk/cli/commands/agents.py +213 -73
  11. glaip_sdk/cli/commands/common_config.py +104 -0
  12. glaip_sdk/cli/commands/configure.py +729 -113
  13. glaip_sdk/cli/commands/mcps.py +241 -72
  14. glaip_sdk/cli/commands/models.py +11 -5
  15. glaip_sdk/cli/commands/tools.py +49 -57
  16. glaip_sdk/cli/commands/transcripts.py +755 -0
  17. glaip_sdk/cli/config.py +48 -4
  18. glaip_sdk/cli/constants.py +38 -0
  19. glaip_sdk/cli/context.py +8 -0
  20. glaip_sdk/cli/core/__init__.py +79 -0
  21. glaip_sdk/cli/core/context.py +124 -0
  22. glaip_sdk/cli/core/output.py +851 -0
  23. glaip_sdk/cli/core/prompting.py +649 -0
  24. glaip_sdk/cli/core/rendering.py +187 -0
  25. glaip_sdk/cli/display.py +35 -19
  26. glaip_sdk/cli/hints.py +57 -0
  27. glaip_sdk/cli/io.py +6 -3
  28. glaip_sdk/cli/main.py +241 -121
  29. glaip_sdk/cli/masking.py +21 -33
  30. glaip_sdk/cli/pager.py +9 -10
  31. glaip_sdk/cli/parsers/__init__.py +1 -3
  32. glaip_sdk/cli/slash/__init__.py +0 -9
  33. glaip_sdk/cli/slash/accounts_controller.py +578 -0
  34. glaip_sdk/cli/slash/accounts_shared.py +75 -0
  35. glaip_sdk/cli/slash/agent_session.py +62 -21
  36. glaip_sdk/cli/slash/prompt.py +21 -0
  37. glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
  38. glaip_sdk/cli/slash/session.py +771 -140
  39. glaip_sdk/cli/slash/tui/__init__.py +9 -0
  40. glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
  41. glaip_sdk/cli/slash/tui/accounts_app.py +876 -0
  42. glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
  43. glaip_sdk/cli/slash/tui/loading.py +58 -0
  44. glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
  45. glaip_sdk/cli/transcript/__init__.py +12 -52
  46. glaip_sdk/cli/transcript/cache.py +255 -44
  47. glaip_sdk/cli/transcript/capture.py +27 -1
  48. glaip_sdk/cli/transcript/history.py +815 -0
  49. glaip_sdk/cli/transcript/viewer.py +72 -499
  50. glaip_sdk/cli/update_notifier.py +14 -5
  51. glaip_sdk/cli/utils.py +243 -1252
  52. glaip_sdk/cli/validators.py +5 -6
  53. glaip_sdk/client/__init__.py +2 -1
  54. glaip_sdk/client/_agent_payloads.py +45 -9
  55. glaip_sdk/client/agent_runs.py +147 -0
  56. glaip_sdk/client/agents.py +291 -35
  57. glaip_sdk/client/base.py +1 -0
  58. glaip_sdk/client/main.py +19 -10
  59. glaip_sdk/client/mcps.py +122 -12
  60. glaip_sdk/client/run_rendering.py +466 -89
  61. glaip_sdk/client/shared.py +21 -0
  62. glaip_sdk/client/tools.py +155 -10
  63. glaip_sdk/config/constants.py +11 -0
  64. glaip_sdk/hitl/__init__.py +15 -0
  65. glaip_sdk/hitl/local.py +151 -0
  66. glaip_sdk/mcps/__init__.py +21 -0
  67. glaip_sdk/mcps/base.py +345 -0
  68. glaip_sdk/models/__init__.py +90 -0
  69. glaip_sdk/models/agent.py +47 -0
  70. glaip_sdk/models/agent_runs.py +116 -0
  71. glaip_sdk/models/common.py +42 -0
  72. glaip_sdk/models/mcp.py +33 -0
  73. glaip_sdk/models/tool.py +33 -0
  74. glaip_sdk/payload_schemas/__init__.py +1 -13
  75. glaip_sdk/registry/__init__.py +55 -0
  76. glaip_sdk/registry/agent.py +164 -0
  77. glaip_sdk/registry/base.py +139 -0
  78. glaip_sdk/registry/mcp.py +253 -0
  79. glaip_sdk/registry/tool.py +232 -0
  80. glaip_sdk/rich_components.py +58 -2
  81. glaip_sdk/runner/__init__.py +59 -0
  82. glaip_sdk/runner/base.py +84 -0
  83. glaip_sdk/runner/deps.py +112 -0
  84. glaip_sdk/runner/langgraph.py +870 -0
  85. glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
  86. glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
  87. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
  88. glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
  89. glaip_sdk/runner/tool_adapter/__init__.py +18 -0
  90. glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
  91. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
  92. glaip_sdk/tools/__init__.py +22 -0
  93. glaip_sdk/tools/base.py +435 -0
  94. glaip_sdk/utils/__init__.py +58 -12
  95. glaip_sdk/utils/a2a/__init__.py +34 -0
  96. glaip_sdk/utils/a2a/event_processor.py +188 -0
  97. glaip_sdk/utils/bundler.py +267 -0
  98. glaip_sdk/utils/client.py +111 -0
  99. glaip_sdk/utils/client_utils.py +39 -7
  100. glaip_sdk/utils/datetime_helpers.py +58 -0
  101. glaip_sdk/utils/discovery.py +78 -0
  102. glaip_sdk/utils/display.py +23 -15
  103. glaip_sdk/utils/export.py +143 -0
  104. glaip_sdk/utils/general.py +0 -33
  105. glaip_sdk/utils/import_export.py +12 -7
  106. glaip_sdk/utils/import_resolver.py +492 -0
  107. glaip_sdk/utils/instructions.py +101 -0
  108. glaip_sdk/utils/rendering/__init__.py +115 -1
  109. glaip_sdk/utils/rendering/formatting.py +5 -30
  110. glaip_sdk/utils/rendering/layout/__init__.py +64 -0
  111. glaip_sdk/utils/rendering/{renderer → layout}/panels.py +9 -0
  112. glaip_sdk/utils/rendering/{renderer → layout}/progress.py +70 -1
  113. glaip_sdk/utils/rendering/layout/summary.py +74 -0
  114. glaip_sdk/utils/rendering/layout/transcript.py +606 -0
  115. glaip_sdk/utils/rendering/models.py +1 -0
  116. glaip_sdk/utils/rendering/renderer/__init__.py +9 -47
  117. glaip_sdk/utils/rendering/renderer/base.py +275 -1476
  118. glaip_sdk/utils/rendering/renderer/debug.py +26 -20
  119. glaip_sdk/utils/rendering/renderer/factory.py +138 -0
  120. glaip_sdk/utils/rendering/renderer/stream.py +4 -12
  121. glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
  122. glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
  123. glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
  124. glaip_sdk/utils/rendering/state.py +204 -0
  125. glaip_sdk/utils/rendering/steps/__init__.py +34 -0
  126. glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +53 -440
  127. glaip_sdk/utils/rendering/steps/format.py +176 -0
  128. glaip_sdk/utils/rendering/steps/manager.py +387 -0
  129. glaip_sdk/utils/rendering/timing.py +36 -0
  130. glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
  131. glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
  132. glaip_sdk/utils/resource_refs.py +25 -13
  133. glaip_sdk/utils/runtime_config.py +425 -0
  134. glaip_sdk/utils/serialization.py +18 -0
  135. glaip_sdk/utils/sync.py +142 -0
  136. glaip_sdk/utils/tool_detection.py +33 -0
  137. glaip_sdk/utils/tool_storage_provider.py +140 -0
  138. glaip_sdk/utils/validation.py +16 -24
  139. {glaip_sdk-0.1.3.dist-info → glaip_sdk-0.6.19.dist-info}/METADATA +56 -21
  140. glaip_sdk-0.6.19.dist-info/RECORD +163 -0
  141. {glaip_sdk-0.1.3.dist-info → glaip_sdk-0.6.19.dist-info}/WHEEL +2 -1
  142. glaip_sdk-0.6.19.dist-info/entry_points.txt +2 -0
  143. glaip_sdk-0.6.19.dist-info/top_level.txt +1 -0
  144. glaip_sdk/models.py +0 -240
  145. glaip_sdk-0.1.3.dist-info/RECORD +0 -83
  146. glaip_sdk-0.1.3.dist-info/entry_points.txt +0 -3
@@ -0,0 +1,232 @@
1
+ """Tool registry for glaip_sdk.
2
+
3
+ This module provides the ToolRegistry that caches deployed tools
4
+ to avoid redundant API calls when deploying agents with tools.
5
+
6
+ Authors:
7
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from typing import TYPE_CHECKING, Any
14
+
15
+ from glaip_sdk.registry.base import BaseRegistry
16
+
17
+ if TYPE_CHECKING:
18
+ from glaip_sdk.tools import Tool
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class ToolRegistry(BaseRegistry["Tool"]):
24
+ """Registry for tools.
25
+
26
+ Resolves tool references to glaip_sdk.models.Tool objects.
27
+ Caches results to avoid redundant API calls and duplicate uploads.
28
+
29
+ Handles:
30
+ - Tool classes (LangChain BaseTool subclasses) → upload, cache, return Tool
31
+ - glaip_sdk.models.Tool → return as-is (uses tool.id)
32
+ - String names → lookup on platform, cache, return Tool
33
+
34
+ Attributes:
35
+ _cache: Internal cache mapping names to Tool objects.
36
+
37
+ Example:
38
+ >>> registry = get_tool_registry()
39
+ >>> tool = registry.resolve(WebSearchTool)
40
+ >>> print(tool.id)
41
+ """
42
+
43
+ def _get_name_from_model_fields(self, ref: type) -> str | None:
44
+ """Extract name from Pydantic model_fields if available."""
45
+ model_fields = getattr(ref, "model_fields", {})
46
+ if "name" not in model_fields:
47
+ return None
48
+ field_info = model_fields["name"]
49
+ default = getattr(field_info, "default", None)
50
+ return default if isinstance(default, str) else None
51
+
52
+ def _get_string_attr(self, obj: Any, attr: str) -> str | None:
53
+ """Get attribute if it's a string, otherwise None."""
54
+ value = getattr(obj, attr, None)
55
+ return value if isinstance(value, str) else None
56
+
57
+ def _extract_name(self, ref: Any) -> str:
58
+ """Extract tool name from a reference.
59
+
60
+ Args:
61
+ ref: A tool class, instance, dict, or string name.
62
+
63
+ Returns:
64
+ The extracted tool name.
65
+
66
+ Raises:
67
+ ValueError: If name cannot be extracted from the reference.
68
+ """
69
+ if isinstance(ref, str):
70
+ return ref
71
+
72
+ # Dict from API response - extract name or id
73
+ if isinstance(ref, dict):
74
+ return ref.get("name") or ref.get("id") or ""
75
+
76
+ # Tool instance (not a class) with name attribute
77
+ if not isinstance(ref, type):
78
+ name = self._get_string_attr(ref, "name")
79
+ if name:
80
+ return name
81
+
82
+ # Tool class - try direct attribute first, then model_fields
83
+ if isinstance(ref, type):
84
+ name = self._get_string_attr(ref, "name") or self._get_name_from_model_fields(ref)
85
+ if name:
86
+ return name
87
+
88
+ raise ValueError(f"Cannot extract name from: {ref}")
89
+
90
+ def _resolve_and_cache(self, ref: Any, name: str) -> Tool:
91
+ """Resolve tool reference - upload if class, find if string/native.
92
+
93
+ Args:
94
+ ref: The tool reference to resolve.
95
+ name: The extracted tool name.
96
+
97
+ Returns:
98
+ The resolved glaip_sdk.models.Tool object.
99
+
100
+ Raises:
101
+ ValueError: If the tool cannot be resolved.
102
+ """
103
+ # Lazy imports to avoid circular dependency
104
+ from glaip_sdk.utils.discovery import find_tool # noqa: PLC0415
105
+ from glaip_sdk.utils.sync import update_or_create_tool # noqa: PLC0415
106
+
107
+ # Already deployed tool (glaip_sdk.models.Tool with ID) - just cache and return
108
+ if hasattr(ref, "id") and hasattr(ref, "name") and not isinstance(ref, type):
109
+ if ref.id is not None:
110
+ logger.debug("Caching already deployed tool: %s", name)
111
+ self._cache[name] = ref
112
+ return ref
113
+
114
+ # Tool without ID (e.g., Tool.from_native()) - look up on platform
115
+ logger.info("Looking up native tool: %s", name)
116
+ tool = find_tool(name)
117
+ if tool:
118
+ self._cache[name] = tool
119
+ return tool
120
+ raise ValueError(f"Native tool not found on platform: {name}")
121
+
122
+ # Custom tool class - upload it
123
+ if self._is_custom_tool(ref):
124
+ logger.info("Uploading custom tool: %s", name)
125
+ tool = update_or_create_tool(ref)
126
+ self._cache[name] = tool
127
+ if tool.id:
128
+ self._cache[tool.id] = tool
129
+ return tool
130
+
131
+ # Dict from API response - use ID directly if available
132
+ if isinstance(ref, dict):
133
+ tool_id = ref.get("id")
134
+ if tool_id:
135
+ from glaip_sdk.tools.base import Tool # noqa: PLC0415
136
+
137
+ tool = Tool(id=tool_id, name=ref.get("name", ""))
138
+ self._cache[name] = tool
139
+ return tool
140
+ raise ValueError(f"Tool dict missing 'id': {ref}")
141
+
142
+ # String name - look up on platform (could be native or existing tool)
143
+ if isinstance(ref, str):
144
+ logger.info("Looking up tool by name: %s", name)
145
+ tool = find_tool(name)
146
+ if tool:
147
+ self._cache[name] = tool
148
+ return tool
149
+ raise ValueError(f"Tool not found on platform: {name}")
150
+
151
+ raise ValueError(f"Could not resolve tool reference: {ref}")
152
+
153
+ def _is_custom_tool(self, ref: Any) -> bool:
154
+ """Check if reference is a custom tool class/instance.
155
+
156
+ Args:
157
+ ref: The reference to check.
158
+
159
+ Returns:
160
+ True if ref is a custom tool that needs uploading.
161
+ """
162
+ try:
163
+ from glaip_sdk.utils.tool_detection import ( # noqa: PLC0415
164
+ is_langchain_tool,
165
+ )
166
+ except ImportError:
167
+ return False
168
+
169
+ return is_langchain_tool(ref)
170
+
171
+ def resolve(self, ref: Any) -> Tool:
172
+ """Resolve a tool reference to a platform Tool object.
173
+
174
+ Overrides base resolve to handle SDK tools differently.
175
+
176
+ Args:
177
+ ref: The tool reference to resolve.
178
+
179
+ Returns:
180
+ The resolved glaip_sdk.models.Tool object.
181
+ """
182
+ # Check if it's a Tool instance (not a class)
183
+ if hasattr(ref, "id") and hasattr(ref, "name") and not isinstance(ref, type):
184
+ # If Tool has an ID, it's already deployed - return as-is
185
+ if ref.id is not None:
186
+ name = self._extract_name(ref)
187
+ if name not in self._cache:
188
+ self._cache[name] = ref
189
+ return ref
190
+
191
+ # Tool without ID (e.g., from Tool.from_native()) - needs platform lookup
192
+ # Fall through to normal resolution
193
+
194
+ return super().resolve(ref)
195
+
196
+
197
+ class _ToolRegistrySingleton:
198
+ """Singleton holder for ToolRegistry to avoid global statement."""
199
+
200
+ _instance: ToolRegistry | None = None
201
+
202
+ @classmethod
203
+ def get_instance(cls) -> ToolRegistry:
204
+ """Get or create the singleton instance.
205
+
206
+ Returns:
207
+ The global ToolRegistry instance.
208
+ """
209
+ if cls._instance is None:
210
+ cls._instance = ToolRegistry()
211
+ return cls._instance
212
+
213
+ @classmethod
214
+ def reset(cls) -> None:
215
+ """Reset the singleton instance (for testing)."""
216
+ cls._instance = None
217
+
218
+
219
+ def get_tool_registry() -> ToolRegistry:
220
+ """Get the singleton ToolRegistry instance.
221
+
222
+ Returns a global ToolRegistry that caches tools across the session.
223
+
224
+ Returns:
225
+ The global ToolRegistry instance.
226
+
227
+ Example:
228
+ >>> from glaip_sdk.registry import get_tool_registry
229
+ >>> registry = get_tool_registry()
230
+ >>> tool = registry.resolve("web_search")
231
+ """
232
+ return _ToolRegistrySingleton.get_instance()
@@ -1,10 +1,15 @@
1
- """Custom Rich components with copy-friendly defaults."""
1
+ """Custom Rich components with copy-friendly defaults.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
2
6
 
3
7
  from __future__ import annotations
4
8
 
5
9
  from rich import box
6
10
  from rich.panel import Panel
7
11
  from rich.table import Table
12
+ from rich.text import Text
8
13
 
9
14
 
10
15
  class AIPPanel(Panel):
@@ -66,4 +71,55 @@ class AIPGrid(Table):
66
71
  )
67
72
 
68
73
 
69
- __all__ = ["AIPPanel", "AIPTable", "AIPGrid"]
74
+ class RemoteRunsTable(AIPTable):
75
+ """Rich Table for displaying remote agent runs with pagination support."""
76
+
77
+ def __init__(self, *args, **kwargs):
78
+ """Initialize RemoteRunsTable with columns for run display.
79
+
80
+ Args:
81
+ *args: Positional arguments passed to AIPTable
82
+ **kwargs: Keyword arguments passed to AIPTable
83
+ """
84
+ kwargs.setdefault("row_styles", ("dim", "none"))
85
+ kwargs.setdefault("show_header", True)
86
+ super().__init__(*args, **kwargs)
87
+ # Add columns for run display
88
+ self.add_column("", width=2, no_wrap=True) # Selection gutter
89
+ self.add_column("Run UUID", style="cyan", width=36, no_wrap=True)
90
+ self.add_column("Type", style="yellow", width=8, no_wrap=True)
91
+ self.add_column("Status", style="magenta", width=12, no_wrap=True)
92
+ self.add_column("Started (UTC)", style="dim", width=20, no_wrap=True)
93
+ self.add_column("Completed (UTC)", style="dim", width=20, no_wrap=True)
94
+ self.add_column("Duration", style="green", width=10, no_wrap=True)
95
+ self.add_column("Input Preview", style="white", width=40, overflow="ellipsis")
96
+
97
+ def add_run_row(
98
+ self,
99
+ run_uuid: str,
100
+ run_type: str,
101
+ status: str,
102
+ started: str,
103
+ completed: str,
104
+ duration: str,
105
+ preview: str,
106
+ *,
107
+ selected: bool = False,
108
+ ) -> None:
109
+ """Append a run row with optional selection styling."""
110
+ gutter = Text("› ", style="bold bright_cyan") if selected else Text(" ")
111
+ row_style = "reverse" if selected else None
112
+ self.add_row(
113
+ gutter,
114
+ run_uuid,
115
+ run_type,
116
+ status,
117
+ started,
118
+ completed,
119
+ duration,
120
+ preview,
121
+ style=row_style,
122
+ )
123
+
124
+
125
+ __all__ = ["AIPPanel", "AIPTable", "AIPGrid", "RemoteRunsTable"]
@@ -0,0 +1,59 @@
1
+ """Local agent execution runners.
2
+
3
+ This module provides runners for executing glaip-sdk agents locally
4
+ without requiring the AIP backend server. The primary runner is
5
+ LangGraphRunner which uses the aip-agents library.
6
+
7
+ To use local execution, install with the [local] extra:
8
+ pip install "glaip-sdk[local]"
9
+
10
+ Authors:
11
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
12
+
13
+ Example:
14
+ >>> from glaip_sdk.runner import get_default_runner
15
+ >>> from glaip_sdk.agents import Agent
16
+ >>>
17
+ >>> agent = Agent(name="my-agent", instruction="You are helpful.")
18
+ >>> runner = get_default_runner()
19
+ >>> result = runner.run(agent, "Hello!")
20
+ """
21
+
22
+ from glaip_sdk.runner.deps import (
23
+ LOCAL_RUNTIME_AVAILABLE,
24
+ check_local_runtime_available,
25
+ get_local_runtime_missing_message,
26
+ )
27
+ from glaip_sdk.runner.langgraph import LangGraphRunner
28
+
29
+ # Default runner instance
30
+ _default_runner: LangGraphRunner | None = None
31
+
32
+
33
+ def get_default_runner() -> LangGraphRunner:
34
+ """Get the default runner instance for local agent execution.
35
+
36
+ Returns:
37
+ The default LangGraphRunner instance.
38
+
39
+ Raises:
40
+ RuntimeError: If local runtime dependencies are not available.
41
+ """
42
+ global _default_runner
43
+
44
+ if not check_local_runtime_available():
45
+ raise RuntimeError(get_local_runtime_missing_message())
46
+
47
+ if _default_runner is None:
48
+ _default_runner = LangGraphRunner()
49
+
50
+ return _default_runner
51
+
52
+
53
+ __all__ = [
54
+ "LOCAL_RUNTIME_AVAILABLE",
55
+ "LangGraphRunner",
56
+ "check_local_runtime_available",
57
+ "get_default_runner",
58
+ "get_local_runtime_missing_message",
59
+ ]
@@ -0,0 +1,84 @@
1
+ """Abstract base class for agent execution runners.
2
+
3
+ Authors:
4
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from abc import ABC, abstractmethod
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ if TYPE_CHECKING:
13
+ from glaip_sdk.agents.base import Agent
14
+
15
+
16
+ class BaseRunner(ABC):
17
+ """Abstract base class for agent execution runners.
18
+
19
+ Runners are responsible for executing glaip-sdk Agent instances
20
+ and returning results. Different runner implementations may use
21
+ different execution backends (LangGraph, Google ADK, etc.).
22
+ """
23
+
24
+ @abstractmethod
25
+ def run(
26
+ self,
27
+ agent: Agent,
28
+ message: str,
29
+ verbose: bool = False,
30
+ runtime_config: dict[str, Any] | None = None,
31
+ chat_history: list[dict[str, str]] | None = None,
32
+ **kwargs: Any,
33
+ ) -> str:
34
+ """Execute agent synchronously and return final response text.
35
+
36
+ Args:
37
+ agent: The glaip_sdk Agent to execute.
38
+ message: The user message to send to the agent.
39
+ verbose: If True, emit debug trace output during execution.
40
+ Defaults to False.
41
+ runtime_config: Optional runtime configuration for tools, MCPs, etc.
42
+ Defaults to None.
43
+ chat_history: Optional list of prior conversation messages.
44
+ Defaults to None.
45
+ **kwargs: Additional keyword arguments passed to the backend.
46
+
47
+ Returns:
48
+ The final response text from the agent.
49
+
50
+ Raises:
51
+ RuntimeError: If execution fails.
52
+ """
53
+ ...
54
+
55
+ @abstractmethod
56
+ async def arun(
57
+ self,
58
+ agent: Agent,
59
+ message: str,
60
+ verbose: bool = False,
61
+ runtime_config: dict[str, Any] | None = None,
62
+ chat_history: list[dict[str, str]] | None = None,
63
+ **kwargs: Any,
64
+ ) -> str:
65
+ """Execute agent asynchronously and return final response text.
66
+
67
+ Args:
68
+ agent: The glaip_sdk Agent to execute.
69
+ message: The user message to send to the agent.
70
+ verbose: If True, emit debug trace output during execution.
71
+ Defaults to False.
72
+ runtime_config: Optional runtime configuration for tools, MCPs, etc.
73
+ Defaults to None.
74
+ chat_history: Optional list of prior conversation messages.
75
+ Defaults to None.
76
+ **kwargs: Additional keyword arguments passed to the backend.
77
+
78
+ Returns:
79
+ The final response text from the agent.
80
+
81
+ Raises:
82
+ RuntimeError: If execution fails.
83
+ """
84
+ ...
@@ -0,0 +1,112 @@
1
+ """Dependency detection utilities for the runner module.
2
+
3
+ This module provides helpers to detect whether the local runtime dependencies
4
+ (aip-agents) are installed and to generate actionable error messages when they
5
+ are not available.
6
+
7
+ Authors:
8
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
9
+
10
+ Example:
11
+ >>> from glaip_sdk.runner.deps import check_local_runtime_available
12
+ >>> if not check_local_runtime_available():
13
+ ... print(get_local_runtime_missing_message())
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import importlib.util
19
+
20
+ from gllm_core.utils import LoggerManager
21
+
22
+ logger = LoggerManager().get_logger(__name__)
23
+
24
+ # Module-level cache for import availability check
25
+ _local_runtime_available: bool | None = None
26
+
27
+
28
+ def _probe_aip_agents_import() -> bool:
29
+ """Check if aip_agents is available without importing it.
30
+
31
+ Returns:
32
+ True if aip_agents appears importable, False otherwise.
33
+ """
34
+ return importlib.util.find_spec("aip_agents") is not None
35
+
36
+
37
+ def check_local_runtime_available() -> bool:
38
+ """Check if the local runtime dependencies are installed and importable.
39
+
40
+ This function probes for the aip_agents module which provides the
41
+ LangGraphReactAgent for local execution. Results are cached for efficiency.
42
+
43
+ Returns:
44
+ True if local runtime is available, False otherwise.
45
+ """
46
+ global _local_runtime_available
47
+
48
+ if _local_runtime_available is None:
49
+ _local_runtime_available = _probe_aip_agents_import()
50
+ if _local_runtime_available:
51
+ logger.debug("Local runtime dependencies (aip-agents) detected")
52
+ else:
53
+ logger.debug("Local runtime dependencies (aip-agents) not available")
54
+
55
+ return _local_runtime_available
56
+
57
+
58
+ # Cached availability flag for use in conditions without function call overhead
59
+ LOCAL_RUNTIME_AVAILABLE: bool = check_local_runtime_available()
60
+
61
+
62
+ def get_local_runtime_missing_message() -> str:
63
+ """Generate an actionable error message when local runtime is not available.
64
+
65
+ Returns:
66
+ A user-friendly message explaining how to install local runtime dependencies
67
+ or how to use server-backed execution as an alternative.
68
+ """
69
+ return (
70
+ "Local runtime dependencies are not installed. "
71
+ "To run agents locally without an AIP server, install with:\n\n"
72
+ ' pip install "glaip-sdk[local]"\n\n'
73
+ "Alternatively, call deploy() to run this agent on the AIP server."
74
+ )
75
+
76
+
77
+ def get_local_mode_not_supported_for_tool_message(tool_ref: str) -> str:
78
+ """Generate an error message for tools that cannot be used in local mode.
79
+
80
+ Args:
81
+ tool_ref: A string identifier for the tool (name, ID, or type description).
82
+
83
+ Returns:
84
+ Error message explaining that the tool type is not supported locally.
85
+ """
86
+ return (
87
+ f"Tool '{tool_ref}' cannot be used in local mode. "
88
+ "Local runtime only supports LangChain-compatible tool classes/instances "
89
+ "and Tool.from_langchain(...) references. "
90
+ "Native platform tools (Tool.from_native()) require server-backed execution "
91
+ "via deploy()."
92
+ )
93
+
94
+
95
+ def get_uuid_not_supported_message(key_type: str, uuid_value: str) -> str:
96
+ """Generate an error message when a UUID is used in local mode.
97
+
98
+ In local mode, runtime_config keys must be tool/agent/MCP objects or names,
99
+ not UUIDs which require platform registry resolution.
100
+
101
+ Args:
102
+ key_type: The type of key (e.g., "tool", "agent", "mcp").
103
+ uuid_value: The UUID that was incorrectly provided.
104
+
105
+ Returns:
106
+ Error message explaining that UUIDs are not supported in local mode.
107
+ """
108
+ return (
109
+ f"UUID-like {key_type} key '{uuid_value}' is not supported in local mode. "
110
+ f"Local runtime cannot resolve {key_type} IDs - pass the {key_type} "
111
+ f"class/instance or name instead."
112
+ )