glaip-sdk 0.1.0__py3-none-any.whl → 0.6.10__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 (156) hide show
  1. glaip_sdk/__init__.py +5 -2
  2. glaip_sdk/_version.py +10 -3
  3. glaip_sdk/agents/__init__.py +27 -0
  4. glaip_sdk/agents/base.py +1191 -0
  5. glaip_sdk/branding.py +15 -6
  6. glaip_sdk/cli/account_store.py +540 -0
  7. glaip_sdk/cli/agent_config.py +2 -6
  8. glaip_sdk/cli/auth.py +265 -45
  9. glaip_sdk/cli/commands/__init__.py +2 -2
  10. glaip_sdk/cli/commands/accounts.py +746 -0
  11. glaip_sdk/cli/commands/agents.py +251 -173
  12. glaip_sdk/cli/commands/common_config.py +101 -0
  13. glaip_sdk/cli/commands/configure.py +735 -143
  14. glaip_sdk/cli/commands/mcps.py +266 -134
  15. glaip_sdk/cli/commands/models.py +13 -9
  16. glaip_sdk/cli/commands/tools.py +67 -88
  17. glaip_sdk/cli/commands/transcripts.py +755 -0
  18. glaip_sdk/cli/commands/update.py +3 -8
  19. glaip_sdk/cli/config.py +49 -7
  20. glaip_sdk/cli/constants.py +38 -0
  21. glaip_sdk/cli/context.py +8 -0
  22. glaip_sdk/cli/core/__init__.py +79 -0
  23. glaip_sdk/cli/core/context.py +124 -0
  24. glaip_sdk/cli/core/output.py +846 -0
  25. glaip_sdk/cli/core/prompting.py +649 -0
  26. glaip_sdk/cli/core/rendering.py +187 -0
  27. glaip_sdk/cli/display.py +45 -32
  28. glaip_sdk/cli/hints.py +57 -0
  29. glaip_sdk/cli/io.py +14 -17
  30. glaip_sdk/cli/main.py +232 -143
  31. glaip_sdk/cli/masking.py +21 -33
  32. glaip_sdk/cli/mcp_validators.py +5 -15
  33. glaip_sdk/cli/pager.py +12 -19
  34. glaip_sdk/cli/parsers/__init__.py +1 -3
  35. glaip_sdk/cli/parsers/json_input.py +11 -22
  36. glaip_sdk/cli/resolution.py +3 -9
  37. glaip_sdk/cli/rich_helpers.py +1 -3
  38. glaip_sdk/cli/slash/__init__.py +0 -9
  39. glaip_sdk/cli/slash/accounts_controller.py +578 -0
  40. glaip_sdk/cli/slash/accounts_shared.py +75 -0
  41. glaip_sdk/cli/slash/agent_session.py +65 -29
  42. glaip_sdk/cli/slash/prompt.py +24 -10
  43. glaip_sdk/cli/slash/remote_runs_controller.py +566 -0
  44. glaip_sdk/cli/slash/session.py +807 -225
  45. glaip_sdk/cli/slash/tui/__init__.py +9 -0
  46. glaip_sdk/cli/slash/tui/accounts.tcss +86 -0
  47. glaip_sdk/cli/slash/tui/accounts_app.py +876 -0
  48. glaip_sdk/cli/slash/tui/background_tasks.py +72 -0
  49. glaip_sdk/cli/slash/tui/loading.py +58 -0
  50. glaip_sdk/cli/slash/tui/remote_runs_app.py +628 -0
  51. glaip_sdk/cli/transcript/__init__.py +12 -52
  52. glaip_sdk/cli/transcript/cache.py +258 -60
  53. glaip_sdk/cli/transcript/capture.py +72 -21
  54. glaip_sdk/cli/transcript/history.py +815 -0
  55. glaip_sdk/cli/transcript/launcher.py +1 -3
  56. glaip_sdk/cli/transcript/viewer.py +79 -499
  57. glaip_sdk/cli/update_notifier.py +177 -24
  58. glaip_sdk/cli/utils.py +242 -1308
  59. glaip_sdk/cli/validators.py +16 -18
  60. glaip_sdk/client/__init__.py +2 -1
  61. glaip_sdk/client/_agent_payloads.py +53 -37
  62. glaip_sdk/client/agent_runs.py +147 -0
  63. glaip_sdk/client/agents.py +320 -92
  64. glaip_sdk/client/base.py +78 -35
  65. glaip_sdk/client/main.py +19 -10
  66. glaip_sdk/client/mcps.py +123 -15
  67. glaip_sdk/client/run_rendering.py +136 -101
  68. glaip_sdk/client/shared.py +21 -0
  69. glaip_sdk/client/tools.py +163 -34
  70. glaip_sdk/client/validators.py +20 -48
  71. glaip_sdk/config/constants.py +11 -0
  72. glaip_sdk/exceptions.py +1 -3
  73. glaip_sdk/mcps/__init__.py +21 -0
  74. glaip_sdk/mcps/base.py +345 -0
  75. glaip_sdk/models/__init__.py +90 -0
  76. glaip_sdk/models/agent.py +47 -0
  77. glaip_sdk/models/agent_runs.py +116 -0
  78. glaip_sdk/models/common.py +42 -0
  79. glaip_sdk/models/mcp.py +33 -0
  80. glaip_sdk/models/tool.py +33 -0
  81. glaip_sdk/payload_schemas/__init__.py +1 -13
  82. glaip_sdk/payload_schemas/agent.py +1 -3
  83. glaip_sdk/registry/__init__.py +55 -0
  84. glaip_sdk/registry/agent.py +164 -0
  85. glaip_sdk/registry/base.py +139 -0
  86. glaip_sdk/registry/mcp.py +253 -0
  87. glaip_sdk/registry/tool.py +232 -0
  88. glaip_sdk/rich_components.py +58 -2
  89. glaip_sdk/runner/__init__.py +59 -0
  90. glaip_sdk/runner/base.py +84 -0
  91. glaip_sdk/runner/deps.py +115 -0
  92. glaip_sdk/runner/langgraph.py +706 -0
  93. glaip_sdk/runner/mcp_adapter/__init__.py +13 -0
  94. glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py +43 -0
  95. glaip_sdk/runner/mcp_adapter/langchain_mcp_adapter.py +257 -0
  96. glaip_sdk/runner/mcp_adapter/mcp_config_builder.py +95 -0
  97. glaip_sdk/runner/tool_adapter/__init__.py +18 -0
  98. glaip_sdk/runner/tool_adapter/base_tool_adapter.py +44 -0
  99. glaip_sdk/runner/tool_adapter/langchain_tool_adapter.py +219 -0
  100. glaip_sdk/tools/__init__.py +22 -0
  101. glaip_sdk/tools/base.py +435 -0
  102. glaip_sdk/utils/__init__.py +58 -12
  103. glaip_sdk/utils/a2a/__init__.py +34 -0
  104. glaip_sdk/utils/a2a/event_processor.py +188 -0
  105. glaip_sdk/utils/agent_config.py +4 -14
  106. glaip_sdk/utils/bundler.py +267 -0
  107. glaip_sdk/utils/client.py +111 -0
  108. glaip_sdk/utils/client_utils.py +46 -28
  109. glaip_sdk/utils/datetime_helpers.py +58 -0
  110. glaip_sdk/utils/discovery.py +78 -0
  111. glaip_sdk/utils/display.py +25 -21
  112. glaip_sdk/utils/export.py +143 -0
  113. glaip_sdk/utils/general.py +1 -36
  114. glaip_sdk/utils/import_export.py +15 -16
  115. glaip_sdk/utils/import_resolver.py +492 -0
  116. glaip_sdk/utils/instructions.py +101 -0
  117. glaip_sdk/utils/rendering/__init__.py +115 -1
  118. glaip_sdk/utils/rendering/formatting.py +7 -35
  119. glaip_sdk/utils/rendering/layout/__init__.py +64 -0
  120. glaip_sdk/utils/rendering/{renderer → layout}/panels.py +10 -3
  121. glaip_sdk/utils/rendering/{renderer → layout}/progress.py +73 -12
  122. glaip_sdk/utils/rendering/layout/summary.py +74 -0
  123. glaip_sdk/utils/rendering/layout/transcript.py +606 -0
  124. glaip_sdk/utils/rendering/models.py +3 -6
  125. glaip_sdk/utils/rendering/renderer/__init__.py +9 -49
  126. glaip_sdk/utils/rendering/renderer/base.py +258 -1577
  127. glaip_sdk/utils/rendering/renderer/config.py +1 -5
  128. glaip_sdk/utils/rendering/renderer/debug.py +30 -34
  129. glaip_sdk/utils/rendering/renderer/factory.py +138 -0
  130. glaip_sdk/utils/rendering/renderer/stream.py +10 -51
  131. glaip_sdk/utils/rendering/renderer/summary_window.py +79 -0
  132. glaip_sdk/utils/rendering/renderer/thinking.py +273 -0
  133. glaip_sdk/utils/rendering/renderer/toggle.py +1 -3
  134. glaip_sdk/utils/rendering/renderer/tool_panels.py +442 -0
  135. glaip_sdk/utils/rendering/renderer/transcript_mode.py +162 -0
  136. glaip_sdk/utils/rendering/state.py +204 -0
  137. glaip_sdk/utils/rendering/step_tree_state.py +1 -3
  138. glaip_sdk/utils/rendering/steps/__init__.py +34 -0
  139. glaip_sdk/utils/rendering/{steps.py → steps/event_processor.py} +76 -517
  140. glaip_sdk/utils/rendering/steps/format.py +176 -0
  141. glaip_sdk/utils/rendering/steps/manager.py +387 -0
  142. glaip_sdk/utils/rendering/timing.py +36 -0
  143. glaip_sdk/utils/rendering/viewer/__init__.py +21 -0
  144. glaip_sdk/utils/rendering/viewer/presenter.py +184 -0
  145. glaip_sdk/utils/resource_refs.py +29 -26
  146. glaip_sdk/utils/runtime_config.py +425 -0
  147. glaip_sdk/utils/serialization.py +32 -46
  148. glaip_sdk/utils/sync.py +142 -0
  149. glaip_sdk/utils/tool_detection.py +33 -0
  150. glaip_sdk/utils/validation.py +20 -28
  151. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/METADATA +42 -4
  152. glaip_sdk-0.6.10.dist-info/RECORD +159 -0
  153. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/WHEEL +1 -1
  154. glaip_sdk/models.py +0 -259
  155. glaip_sdk-0.1.0.dist-info/RECORD +0 -82
  156. {glaip_sdk-0.1.0.dist-info → glaip_sdk-0.6.10.dist-info}/entry_points.txt +0 -0
@@ -39,9 +39,7 @@ class ResourceValidator:
39
39
  if len(found_tools) == 1:
40
40
  return str(found_tools[0].id)
41
41
  elif len(found_tools) > 1:
42
- raise AmbiguousResourceError(
43
- f"Multiple tools found with name '{tool_name}': {[t.id for t in found_tools]}"
44
- )
42
+ raise AmbiguousResourceError(f"Multiple tools found with name '{tool_name}': {[t.id for t in found_tools]}")
45
43
  else:
46
44
  raise NotFoundError(f"Tool not found: {tool_name}")
47
45
 
@@ -51,9 +49,7 @@ class ResourceValidator:
51
49
  if len(found_tools) == 1:
52
50
  return str(found_tools[0].id)
53
51
  elif len(found_tools) > 1:
54
- raise AmbiguousResourceError(
55
- f"Multiple tools found with name '{tool.name}': {[t.id for t in found_tools]}"
56
- )
52
+ raise AmbiguousResourceError(f"Multiple tools found with name '{tool.name}': {[t.id for t in found_tools]}")
57
53
  else:
58
54
  raise NotFoundError(f"Tool not found: {tool.name}")
59
55
 
@@ -73,9 +69,7 @@ class ResourceValidator:
73
69
  elif hasattr(tool, "name") and tool.name is not None:
74
70
  return self._resolve_tool_by_name_attribute(tool, client)
75
71
  else:
76
- raise ValidationError(
77
- f"Invalid tool reference: {tool} - must have 'id' or 'name' attribute"
78
- )
72
+ raise ValidationError(f"Invalid tool reference: {tool} - must have 'id' or 'name' attribute")
79
73
 
80
74
  def _process_single_tool(self, tool: str | Tool, client: Any) -> str:
81
75
  """Process a single tool reference and return its ID."""
@@ -99,22 +93,14 @@ class ResourceValidator:
99
93
  try:
100
94
  tool_id = cls()._process_single_tool(tool, client)
101
95
  tool_ids.append(tool_id)
102
- except (AmbiguousResourceError, NotFoundError) as e:
96
+ except (AmbiguousResourceError, NotFoundError) as err:
103
97
  # Determine the tool name for the error message
104
- tool_name = (
105
- tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
106
- )
107
- raise ValidationError(
108
- f"Failed to resolve tool name '{tool_name}' to ID: {e}"
109
- )
110
- except Exception as e:
98
+ tool_name = tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
99
+ raise ValidationError(f"Failed to resolve tool name '{tool_name}' to ID: {err}") from err
100
+ except Exception as err:
111
101
  # For other exceptions, wrap them appropriately
112
- tool_name = (
113
- tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
114
- )
115
- raise ValidationError(
116
- f"Failed to resolve tool name '{tool_name}' to ID: {e}"
117
- )
102
+ tool_name = tool if isinstance(tool, str) else getattr(tool, "name", str(tool))
103
+ raise ValidationError(f"Failed to resolve tool name '{tool_name}' to ID: {err}") from err
118
104
 
119
105
  return tool_ids
120
106
 
@@ -158,9 +144,7 @@ class ResourceValidator:
158
144
  elif hasattr(agent, "name") and agent.name is not None:
159
145
  return self._resolve_agent_by_name_attribute(agent, client)
160
146
  else:
161
- raise ValidationError(
162
- f"Invalid agent reference: {agent} - must have 'id' or 'name' attribute"
163
- )
147
+ raise ValidationError(f"Invalid agent reference: {agent} - must have 'id' or 'name' attribute")
164
148
 
165
149
  def _process_single_agent(self, agent: str | Any, client: Any) -> str:
166
150
  """Process a single agent reference and return its ID."""
@@ -184,26 +168,14 @@ class ResourceValidator:
184
168
  try:
185
169
  agent_id = cls()._process_single_agent(agent, client)
186
170
  agent_ids.append(agent_id)
187
- except (AmbiguousResourceError, NotFoundError) as e:
171
+ except (AmbiguousResourceError, NotFoundError) as err:
188
172
  # Determine the agent name for the error message
189
- agent_name = (
190
- agent
191
- if isinstance(agent, str)
192
- else getattr(agent, "name", str(agent))
193
- )
194
- raise ValidationError(
195
- f"Failed to resolve agent name '{agent_name}' to ID: {e}"
196
- )
197
- except Exception as e:
173
+ agent_name = agent if isinstance(agent, str) else getattr(agent, "name", str(agent))
174
+ raise ValidationError(f"Failed to resolve agent name '{agent_name}' to ID: {err}") from err
175
+ except Exception as err:
198
176
  # For other exceptions, wrap them appropriately
199
- agent_name = (
200
- agent
201
- if isinstance(agent, str)
202
- else getattr(agent, "name", str(agent))
203
- )
204
- raise ValidationError(
205
- f"Failed to resolve agent name '{agent_name}' to ID: {e}"
206
- )
177
+ agent_name = agent if isinstance(agent, str) else getattr(agent, "name", str(agent))
178
+ raise ValidationError(f"Failed to resolve agent name '{agent_name}' to ID: {err}") from err
207
179
 
208
180
  return agent_ids
209
181
 
@@ -213,8 +185,8 @@ class ResourceValidator:
213
185
  for tool_id in tool_ids:
214
186
  try:
215
187
  client.get_tool_by_id(tool_id)
216
- except NotFoundError:
217
- raise ValidationError(f"Tool not found: {tool_id}")
188
+ except NotFoundError as err:
189
+ raise ValidationError(f"Tool not found: {tool_id}") from err
218
190
 
219
191
  @classmethod
220
192
  def validate_agents_exist(cls, agent_ids: list[str], client: Any) -> None:
@@ -222,5 +194,5 @@ class ResourceValidator:
222
194
  for agent_id in agent_ids:
223
195
  try:
224
196
  client.get_agent_by_id(agent_id)
225
- except NotFoundError:
226
- raise ValidationError(f"Agent not found: {agent_id}")
197
+ except NotFoundError as err:
198
+ raise ValidationError(f"Agent not found: {agent_id}") from err
@@ -39,3 +39,14 @@ DEFAULT_MCP_TRANSPORT = "stdio"
39
39
 
40
40
  # Default error messages
41
41
  DEFAULT_ERROR_MESSAGE = "Unknown error"
42
+
43
+ # Agent configuration fields used for CLI args and payload building
44
+ AGENT_CONFIG_FIELDS = (
45
+ "name",
46
+ "instruction",
47
+ "model",
48
+ "tools",
49
+ "agents",
50
+ "mcps",
51
+ "timeout",
52
+ )
glaip_sdk/exceptions.py CHANGED
@@ -107,9 +107,7 @@ class AgentTimeoutError(TimeoutError):
107
107
  agent_name: Optional name of the agent that timed out
108
108
  """
109
109
  agent_info = f" for agent '{agent_name}'" if agent_name else ""
110
- message = (
111
- f"Agent execution timed out after {timeout_seconds} seconds{agent_info}"
112
- )
110
+ message = f"Agent execution timed out after {timeout_seconds} seconds{agent_info}"
113
111
  super().__init__(message)
114
112
  self.timeout_seconds = timeout_seconds
115
113
  self.agent_name = agent_name
@@ -0,0 +1,21 @@
1
+ """MCP (Model Context Protocol) package for GL AIP platform.
2
+
3
+ This package provides the MCP class and MCPRegistry for managing
4
+ Model Context Protocol configurations on the GL AIP platform.
5
+
6
+ Example:
7
+ >>> from glaip_sdk.mcps import MCP, get_mcp_registry
8
+ >>> mcp = MCP.from_native("arxiv-search")
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from glaip_sdk.mcps.base import MCP, MCPConfigValue
14
+ from glaip_sdk.registry.mcp import MCPRegistry, get_mcp_registry
15
+
16
+ __all__ = [
17
+ "MCP",
18
+ "MCPConfigValue",
19
+ "MCPRegistry",
20
+ "get_mcp_registry",
21
+ ]
glaip_sdk/mcps/base.py ADDED
@@ -0,0 +1,345 @@
1
+ """MCP (Model Context Protocol) helper for glaip_sdk.
2
+
3
+ Provides a simple, migration-ready way to declare and resolve MCPs with
4
+ in-memory caching and create-on-missing functionality.
5
+
6
+ The MCP class also supports runtime operations (update, delete, get_tools)
7
+ when retrieved from the API via client.mcps.get().
8
+
9
+ Authors:
10
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
11
+
12
+ Example - Lazy Reference:
13
+ >>> from glaip_sdk.mcps import MCP
14
+ >>>
15
+ >>> # Create from known ID
16
+ >>> mcp = MCP.from_id("mcp_abc123")
17
+ >>>
18
+ >>> # Create lookup-only by name (error if not found)
19
+ >>> mcp = MCP.from_native("arxiv-search")
20
+ >>>
21
+ >>> # Create for lookup/creation by name (create if missing)
22
+ >>> mcp = MCP(name="my-filesystem-mcp", transport="sse", config={"url": "..."})
23
+
24
+ Example - Runtime Operations:
25
+ >>> from glaip_sdk import Glaip
26
+ >>>
27
+ >>> client = Glaip()
28
+ >>> mcp = client.mcps.get("mcp-123")
29
+ >>> tools = mcp.get_tools() # Get tools from MCP
30
+ >>> mcp.update(description="Updated description")
31
+ >>> mcp.delete()
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ from typing import TYPE_CHECKING, Any
37
+
38
+ if TYPE_CHECKING:
39
+ from glaip_sdk.models import MCPResponse
40
+
41
+ # Type alias for MCP configuration values
42
+ MCPConfigValue = str | int | bool | list[str] | dict[str, str]
43
+
44
+ _MCP_NOT_DEPLOYED_MSG = "MCP not available on platform. No ID set."
45
+ _CLIENT_NOT_AVAILABLE_MSG = "Client not available. Use client.mcps.get() to get a client-connected MCP."
46
+
47
+
48
+ class MCP:
49
+ """MCP reference helper for declaring MCPs in Agent definitions.
50
+
51
+ Supports both lazy references and runtime operations:
52
+ - Lazy reference: Created via from_native() or from_id()
53
+ - Runtime: Created via from_response() or client.mcps.get()
54
+
55
+ Attributes:
56
+ name: Human-readable MCP name (used for lookup/creation).
57
+ id: Backend MCP ID (used for direct fetch if known).
58
+ transport: Transport type (e.g., "sse", "stdio", "websocket").
59
+ config: Transport configuration dict (URLs, args, env vars).
60
+ description: Optional description for the MCP.
61
+ metadata: Optional additional metadata dict.
62
+ authentication: Authentication configuration.
63
+
64
+ Example - Lazy Reference:
65
+ >>> # Create from known ID
66
+ >>> mcp = MCP.from_id("mcp_abc123")
67
+ >>>
68
+ >>> # Create lookup-only by name (error if not found)
69
+ >>> mcp = MCP.from_native("arxiv-search")
70
+ >>>
71
+ >>> # Create for lookup/creation by name (create if missing)
72
+ >>> mcp = MCP(name="my-filesystem-mcp", transport="sse", config={"url": "..."})
73
+
74
+ Example - Runtime Operations:
75
+ >>> mcp = client.mcps.get("mcp-123")
76
+ >>> mcp.update(description="New description")
77
+ >>> mcp.delete()
78
+ """
79
+
80
+ def __init__(
81
+ self,
82
+ name: str | None = None,
83
+ *,
84
+ id: str | None = None, # noqa: A002 - Allow shadowing builtin for API compat
85
+ transport: str | None = None,
86
+ config: dict[str, MCPConfigValue] | None = None,
87
+ description: str | None = None,
88
+ metadata: dict[str, Any] | None = None,
89
+ authentication: dict[str, Any] | None = None,
90
+ _lookup_only: bool = False,
91
+ _client: Any = None,
92
+ ) -> None:
93
+ """Initialize an MCP.
94
+
95
+ Args:
96
+ name: Human-readable MCP name.
97
+ id: Backend MCP ID.
98
+ transport: Transport type (e.g., "sse", "stdio").
99
+ config: Transport configuration dict.
100
+ description: Optional description.
101
+ metadata: Optional metadata dict.
102
+ authentication: Authentication configuration.
103
+ _lookup_only: If True, don't create if not found.
104
+ _client: Internal client reference.
105
+
106
+ Raises:
107
+ ValueError: If neither name nor id is provided.
108
+ """
109
+ if not name and not id:
110
+ raise ValueError("At least one of 'name' or 'id' must be provided")
111
+
112
+ self.name = name
113
+ self._id = id
114
+ self.transport = transport
115
+ self.config = config
116
+ self.description = description
117
+ self.metadata = metadata
118
+ self.authentication = authentication
119
+ self._lookup_only = _lookup_only
120
+ self._client = _client
121
+
122
+ @property
123
+ def id(self) -> str | None: # noqa: A003 - Allow shadowing builtin for API compat
124
+ """MCP ID on the platform."""
125
+ return self._id
126
+
127
+ @id.setter
128
+ def id(self, value: str | None) -> None: # noqa: A003
129
+ """Set the MCP ID."""
130
+ self._id = value
131
+
132
+ def __repr__(self) -> str:
133
+ """Return string representation."""
134
+ if self._id:
135
+ return f"MCP(id={self._id!r}, name={self.name!r})"
136
+ if self._lookup_only:
137
+ return f"MCP.from_native({self.name!r})"
138
+ return f"MCP(name={self.name!r})"
139
+
140
+ def __eq__(self, other: object) -> bool:
141
+ """Check equality based on id if available, else name."""
142
+ if not isinstance(other, MCP):
143
+ return NotImplemented
144
+ if self._id and other._id:
145
+ return self._id == other._id
146
+ return self.name == other.name
147
+
148
+ def __hash__(self) -> int:
149
+ """Hash based on id if available, else name."""
150
+ if self._id:
151
+ return hash(self._id)
152
+ return hash(self.name)
153
+
154
+ def model_dump(self, *, exclude_none: bool = False) -> dict[str, Any]:
155
+ """Return a dict representation of the MCP.
156
+
157
+ Provides Pydantic-style serialization for backward compatibility.
158
+
159
+ Args:
160
+ exclude_none: If True, exclude None values from the output.
161
+
162
+ Returns:
163
+ Dictionary containing MCP attributes.
164
+ """
165
+ data = {
166
+ "id": self._id,
167
+ "name": self.name,
168
+ "transport": self.transport,
169
+ "config": self.config,
170
+ "description": self.description,
171
+ "metadata": self.metadata,
172
+ "authentication": self.authentication,
173
+ }
174
+ if exclude_none:
175
+ return {k: v for k, v in data.items() if v is not None}
176
+ return data
177
+
178
+ @classmethod
179
+ def from_native(cls, name: str) -> MCP:
180
+ """Create a lookup-only MCP reference by name.
181
+
182
+ Use this when referencing an MCP that already exists on the platform.
183
+ Resolution will NOT create the MCP if not found - it will raise an error.
184
+
185
+ Args:
186
+ name: The name of the existing MCP.
187
+
188
+ Returns:
189
+ MCP instance configured for lookup-only resolution.
190
+
191
+ Raises:
192
+ ValueError: If name is empty.
193
+
194
+ Example:
195
+ >>> mcp = MCP.from_native("arxiv-search")
196
+ >>> # Registry will find by name, error if not found or ambiguous
197
+ """
198
+ if not name:
199
+ raise ValueError("Name cannot be empty")
200
+ return cls(name=name, _lookup_only=True)
201
+
202
+ @classmethod
203
+ def from_id(cls, mcp_id: str) -> MCP:
204
+ """Create an MCP helper for lookup-only by ID.
205
+
206
+ This creates a minimal MCP reference that will be resolved
207
+ from the backend using the ID. Use this when you know the
208
+ backend MCP ID but don't have the full configuration.
209
+
210
+ Args:
211
+ mcp_id: The backend MCP ID.
212
+
213
+ Returns:
214
+ An MCP instance with only the ID set, marked for lookup-only.
215
+
216
+ Raises:
217
+ ValueError: If mcp_id is empty.
218
+
219
+ Example:
220
+ >>> mcp = MCP.from_id("550e8400-e29b-41d4-a716-446655440000")
221
+ >>> # Registry will fetch directly by ID
222
+ """
223
+ if not mcp_id:
224
+ raise ValueError("ID cannot be empty")
225
+ return cls(id=mcp_id, _lookup_only=True)
226
+
227
+ # ─────────────────────────────────────────────────────────────────
228
+ # Runtime Methods (require client connection)
229
+ # ─────────────────────────────────────────────────────────────────
230
+
231
+ def _set_client(self, client: Any) -> MCP:
232
+ """Set the client reference for this MCP.
233
+
234
+ Args:
235
+ client: The Glaip client instance.
236
+
237
+ Returns:
238
+ Self for method chaining.
239
+ """
240
+ self._client = client
241
+ return self
242
+
243
+ def get_tools(self) -> list[dict[str, Any]]:
244
+ """Get tools available from this MCP.
245
+
246
+ Returns:
247
+ List of tool definitions from the MCP.
248
+
249
+ Raises:
250
+ ValueError: If the MCP has no ID.
251
+ RuntimeError: If client is not available.
252
+ """
253
+ if not self._id:
254
+ raise ValueError(_MCP_NOT_DEPLOYED_MSG)
255
+ if not self._client:
256
+ raise RuntimeError(_CLIENT_NOT_AVAILABLE_MSG)
257
+
258
+ # Delegate to the client's MCP tools endpoint
259
+ return self._client.mcps.get_tools(mcp_id=self._id)
260
+
261
+ def update(self, **kwargs: Any) -> MCP:
262
+ """Update the MCP with new configuration.
263
+
264
+ Args:
265
+ **kwargs: MCP properties to update (name, description, config, etc.).
266
+
267
+ Returns:
268
+ Self with updated properties.
269
+
270
+ Raises:
271
+ ValueError: If the MCP has no ID.
272
+ RuntimeError: If client is not available.
273
+ """
274
+ if not self._id:
275
+ raise ValueError(_MCP_NOT_DEPLOYED_MSG)
276
+ if not self._client:
277
+ raise RuntimeError(_CLIENT_NOT_AVAILABLE_MSG)
278
+
279
+ response = self._client.mcps.update(mcp_id=self._id, **kwargs)
280
+
281
+ # Update local properties from response
282
+ if hasattr(response, "name") and response.name:
283
+ self.name = response.name
284
+ if hasattr(response, "description"):
285
+ self.description = response.description
286
+ if hasattr(response, "config"):
287
+ self.config = response.config
288
+ if hasattr(response, "transport"):
289
+ self.transport = response.transport
290
+
291
+ return self
292
+
293
+ def delete(self) -> None:
294
+ """Delete the MCP from the platform.
295
+
296
+ Raises:
297
+ ValueError: If the MCP has no ID.
298
+ RuntimeError: If client is not available.
299
+ """
300
+ if not self._id:
301
+ raise ValueError(_MCP_NOT_DEPLOYED_MSG)
302
+ if not self._client:
303
+ raise RuntimeError(_CLIENT_NOT_AVAILABLE_MSG)
304
+
305
+ self._client.mcps.delete(mcp_id=self._id)
306
+ self._id = None
307
+ self._client = None
308
+
309
+ @classmethod
310
+ def from_response(
311
+ cls,
312
+ response: MCPResponse,
313
+ client: Any = None,
314
+ ) -> MCP:
315
+ """Create an MCP instance from an API response.
316
+
317
+ This allows you to work with MCPs retrieved from the API
318
+ as full MCP instances with all methods available.
319
+
320
+ Args:
321
+ response: The MCPResponse from an API call.
322
+ client: The Glaip client instance for API operations.
323
+
324
+ Returns:
325
+ An MCP instance initialized from the response.
326
+
327
+ Example:
328
+ >>> response = client.mcps.get("mcp-123")
329
+ >>> mcp = MCP.from_response(response, client)
330
+ >>> tools = mcp.get_tools()
331
+ """
332
+ mcp = cls(
333
+ name=response.name,
334
+ id=response.id,
335
+ description=getattr(response, "description", None),
336
+ transport=getattr(response, "transport", None),
337
+ config=getattr(response, "config", None),
338
+ metadata=getattr(response, "metadata", None),
339
+ authentication=getattr(response, "authentication", None),
340
+ )
341
+
342
+ if client:
343
+ mcp._set_client(client)
344
+
345
+ return mcp
@@ -0,0 +1,90 @@
1
+ """Models package for AIP SDK.
2
+
3
+ This package provides Pydantic models for API responses.
4
+
5
+ For the public runtime API with methods like run(), deploy(), update(), delete():
6
+ - glaip_sdk.agents.Agent
7
+ - glaip_sdk.tools.Tool
8
+ - glaip_sdk.mcps.MCP
9
+
10
+ The Agent, Tool, and MCP exports from this module are DEPRECATED.
11
+ They redirect to glaip_sdk.agents.Agent, glaip_sdk.tools.Tool, glaip_sdk.mcps.MCP
12
+ respectively with deprecation warnings.
13
+
14
+ Authors:
15
+ Raymond Christopher (raymond.christopher@gdplabs.id)
16
+ """
17
+
18
+ import warnings
19
+
20
+ # Pure Pydantic models for API responses (no runtime methods)
21
+ from glaip_sdk.models.agent import AgentResponse
22
+ from glaip_sdk.models.agent_runs import (
23
+ RunOutputChunk,
24
+ RunsPage,
25
+ RunSummary,
26
+ RunWithOutput,
27
+ )
28
+ from glaip_sdk.models.common import LanguageModelResponse, TTYRenderer
29
+ from glaip_sdk.models.mcp import MCPResponse
30
+ from glaip_sdk.models.tool import ToolResponse
31
+
32
+
33
+ def __getattr__(name: str) -> type:
34
+ """Deprecation warnings for backward compatibility."""
35
+ if name == "Agent":
36
+ warnings.warn(
37
+ "Importing Agent from glaip_sdk.models is deprecated. "
38
+ "Use 'from glaip_sdk.agents import Agent' instead. "
39
+ "This will be removed in v1.0.0",
40
+ DeprecationWarning,
41
+ stacklevel=2,
42
+ )
43
+ from glaip_sdk.agents import Agent # noqa: PLC0415
44
+
45
+ return Agent
46
+
47
+ if name == "Tool":
48
+ warnings.warn(
49
+ "Importing Tool from glaip_sdk.models is deprecated. "
50
+ "Use 'from glaip_sdk.tools import Tool' instead. "
51
+ "This will be removed in v1.0.0",
52
+ DeprecationWarning,
53
+ stacklevel=2,
54
+ )
55
+ from glaip_sdk.tools import Tool # noqa: PLC0415
56
+
57
+ return Tool
58
+
59
+ if name == "MCP":
60
+ warnings.warn(
61
+ "Importing MCP from glaip_sdk.models is deprecated. "
62
+ "Use 'from glaip_sdk.mcps import MCP' instead. "
63
+ "This will be removed in v1.0.0",
64
+ DeprecationWarning,
65
+ stacklevel=2,
66
+ )
67
+ from glaip_sdk.mcps import MCP # noqa: PLC0415
68
+
69
+ return MCP
70
+
71
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
72
+
73
+
74
+ __all__ = [
75
+ # Pure Pydantic response models (recommended for type hints)
76
+ "AgentResponse",
77
+ "ToolResponse",
78
+ "MCPResponse",
79
+ # Deprecated aliases (redirect to runtime classes with warning)
80
+ "Agent",
81
+ "Tool",
82
+ "MCP",
83
+ # Other models
84
+ "LanguageModelResponse",
85
+ "TTYRenderer",
86
+ "RunSummary",
87
+ "RunsPage",
88
+ "RunWithOutput",
89
+ "RunOutputChunk",
90
+ ]
@@ -0,0 +1,47 @@
1
+ """Agent response model for AIP SDK.
2
+
3
+ This module contains the Pydantic model for Agent API responses.
4
+ This is a pure data model with no runtime behavior.
5
+
6
+ For the runtime Agent class with deploy/run methods, use glaip_sdk.agents.Agent.
7
+
8
+ Authors:
9
+ Raymond Christopher (raymond.christopher@gdplabs.id)
10
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
11
+ """
12
+
13
+ from datetime import datetime
14
+ from typing import Any
15
+
16
+ from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
17
+ from pydantic import BaseModel
18
+
19
+
20
+ class AgentResponse(BaseModel):
21
+ """Pydantic model for Agent API responses.
22
+
23
+ This is a pure data model for deserializing API responses.
24
+ It does NOT have runtime methods (run, update, delete).
25
+
26
+ For the runtime Agent class, use glaip_sdk.agents.Agent.
27
+ """
28
+
29
+ id: str
30
+ name: str
31
+ instruction: str | None = None
32
+ description: str | None = None
33
+ type: str | None = None
34
+ framework: str | None = None
35
+ version: str | None = None
36
+ tools: list[dict[str, Any]] | None = None
37
+ agents: list[dict[str, Any]] | None = None
38
+ mcps: list[dict[str, Any]] | None = None
39
+ tool_configs: dict[str, Any] | None = None
40
+ mcp_configs: dict[str, Any] | None = None
41
+ agent_config: dict[str, Any] | None = None
42
+ timeout: int = DEFAULT_AGENT_RUN_TIMEOUT
43
+ metadata: dict[str, Any] | None = None
44
+ language_model_id: str | None = None
45
+ a2a_profile: dict[str, Any] | None = None
46
+ created_at: datetime | None = None
47
+ updated_at: datetime | None = None