glaip-sdk 0.0.3__py3-none-any.whl → 0.0.5__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 (47) hide show
  1. glaip_sdk/__init__.py +5 -5
  2. glaip_sdk/branding.py +146 -0
  3. glaip_sdk/cli/__init__.py +1 -1
  4. glaip_sdk/cli/agent_config.py +82 -0
  5. glaip_sdk/cli/commands/__init__.py +3 -3
  6. glaip_sdk/cli/commands/agents.py +786 -271
  7. glaip_sdk/cli/commands/configure.py +19 -19
  8. glaip_sdk/cli/commands/mcps.py +151 -141
  9. glaip_sdk/cli/commands/models.py +1 -1
  10. glaip_sdk/cli/commands/tools.py +252 -178
  11. glaip_sdk/cli/display.py +244 -0
  12. glaip_sdk/cli/io.py +106 -0
  13. glaip_sdk/cli/main.py +27 -20
  14. glaip_sdk/cli/resolution.py +59 -0
  15. glaip_sdk/cli/utils.py +372 -213
  16. glaip_sdk/cli/validators.py +235 -0
  17. glaip_sdk/client/__init__.py +3 -224
  18. glaip_sdk/client/agents.py +632 -171
  19. glaip_sdk/client/base.py +66 -4
  20. glaip_sdk/client/main.py +226 -0
  21. glaip_sdk/client/mcps.py +143 -18
  22. glaip_sdk/client/tools.py +327 -104
  23. glaip_sdk/config/constants.py +10 -1
  24. glaip_sdk/models.py +43 -3
  25. glaip_sdk/rich_components.py +29 -0
  26. glaip_sdk/utils/__init__.py +18 -171
  27. glaip_sdk/utils/agent_config.py +181 -0
  28. glaip_sdk/utils/client_utils.py +159 -79
  29. glaip_sdk/utils/display.py +100 -0
  30. glaip_sdk/utils/general.py +94 -0
  31. glaip_sdk/utils/import_export.py +140 -0
  32. glaip_sdk/utils/rendering/formatting.py +6 -1
  33. glaip_sdk/utils/rendering/renderer/__init__.py +67 -8
  34. glaip_sdk/utils/rendering/renderer/base.py +340 -247
  35. glaip_sdk/utils/rendering/renderer/debug.py +3 -2
  36. glaip_sdk/utils/rendering/renderer/panels.py +11 -10
  37. glaip_sdk/utils/rendering/steps.py +1 -1
  38. glaip_sdk/utils/resource_refs.py +192 -0
  39. glaip_sdk/utils/rich_utils.py +29 -0
  40. glaip_sdk/utils/serialization.py +285 -0
  41. glaip_sdk/utils/validation.py +273 -0
  42. {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/METADATA +6 -5
  43. glaip_sdk-0.0.5.dist-info/RECORD +55 -0
  44. glaip_sdk/cli/commands/init.py +0 -177
  45. glaip_sdk-0.0.3.dist-info/RECORD +0 -40
  46. {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/WHEEL +0 -0
  47. {glaip_sdk-0.0.3.dist-info → glaip_sdk-0.0.5.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,235 @@
1
+ """CLI validation utilities that wrap core validation with Click exceptions.
2
+
3
+ This module provides thin wrappers over utils.validation that translate
4
+ ValueError exceptions to click.ClickException for CLI user experience.
5
+
6
+ Authors:
7
+ Raymond Christopher (raymond.christopher@gdplabs.id)
8
+ """
9
+
10
+ from pathlib import Path
11
+
12
+ import click
13
+
14
+ from glaip_sdk.utils.validation import (
15
+ coerce_timeout,
16
+ validate_agent_instruction,
17
+ validate_agent_name,
18
+ validate_api_key,
19
+ validate_directory_path,
20
+ validate_file_path,
21
+ validate_mcp_name,
22
+ validate_timeout,
23
+ validate_tool_name,
24
+ validate_url,
25
+ )
26
+
27
+
28
+ def validate_agent_name_cli(name: str) -> str:
29
+ """Validate agent name and return cleaned version.
30
+
31
+ Args:
32
+ name: Agent name to validate
33
+
34
+ Returns:
35
+ Cleaned agent name
36
+
37
+ Raises:
38
+ click.ClickException: If name is invalid
39
+ """
40
+ try:
41
+ return validate_agent_name(name)
42
+ except ValueError as e:
43
+ raise click.ClickException(str(e))
44
+
45
+
46
+ def validate_agent_instruction_cli(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
+ click.ClickException: If instruction is invalid
57
+ """
58
+ try:
59
+ return validate_agent_instruction(instruction)
60
+ except ValueError as e:
61
+ raise click.ClickException(str(e))
62
+
63
+
64
+ def validate_timeout_cli(timeout: int) -> int:
65
+ """Validate timeout value.
66
+
67
+ Args:
68
+ timeout: Timeout value in seconds
69
+
70
+ Returns:
71
+ Validated timeout value
72
+
73
+ Raises:
74
+ click.ClickException: If timeout is invalid
75
+ """
76
+ try:
77
+ return validate_timeout(timeout)
78
+ except ValueError as e:
79
+ raise click.ClickException(str(e))
80
+
81
+
82
+ def validate_tool_name_cli(name: str) -> str:
83
+ """Validate tool name and return cleaned version.
84
+
85
+ Args:
86
+ name: Tool name to validate
87
+
88
+ Returns:
89
+ Cleaned tool name
90
+
91
+ Raises:
92
+ click.ClickException: If name is invalid
93
+ """
94
+ try:
95
+ return validate_tool_name(name)
96
+ except ValueError as e:
97
+ raise click.ClickException(str(e))
98
+
99
+
100
+ def validate_mcp_name_cli(name: str) -> str:
101
+ """Validate MCP name and return cleaned version.
102
+
103
+ Args:
104
+ name: MCP name to validate
105
+
106
+ Returns:
107
+ Cleaned MCP name
108
+
109
+ Raises:
110
+ click.ClickException: If name is invalid
111
+ """
112
+ try:
113
+ return validate_mcp_name(name)
114
+ except ValueError as e:
115
+ raise click.ClickException(str(e))
116
+
117
+
118
+ def validate_file_path_cli(file_path: str | Path, must_exist: bool = True) -> Path:
119
+ """Validate file path.
120
+
121
+ Args:
122
+ file_path: File path to validate
123
+ must_exist: Whether file must exist
124
+
125
+ Returns:
126
+ Path object
127
+
128
+ Raises:
129
+ click.ClickException: If file path is invalid
130
+ """
131
+ try:
132
+ return validate_file_path(file_path, must_exist)
133
+ except ValueError as e:
134
+ raise click.ClickException(str(e))
135
+
136
+
137
+ def validate_directory_path_cli(dir_path: str | Path, must_exist: bool = True) -> Path:
138
+ """Validate directory path.
139
+
140
+ Args:
141
+ dir_path: Directory path to validate
142
+ must_exist: Whether directory must exist
143
+
144
+ Returns:
145
+ Path object
146
+
147
+ Raises:
148
+ click.ClickException: If directory path is invalid
149
+ """
150
+ try:
151
+ return validate_directory_path(dir_path, must_exist)
152
+ except ValueError as e:
153
+ raise click.ClickException(str(e))
154
+
155
+
156
+ def validate_url_cli(url: str) -> str:
157
+ """Validate URL format.
158
+
159
+ Args:
160
+ url: URL to validate
161
+
162
+ Returns:
163
+ Validated URL
164
+
165
+ Raises:
166
+ click.ClickException: If URL is invalid
167
+ """
168
+ try:
169
+ return validate_url(url)
170
+ except ValueError as e:
171
+ raise click.ClickException(str(e))
172
+
173
+
174
+ def validate_api_key_cli(api_key: str) -> str:
175
+ """Validate API key format.
176
+
177
+ Args:
178
+ api_key: API key to validate
179
+
180
+ Returns:
181
+ Validated API key
182
+
183
+ Raises:
184
+ click.ClickException: If API key is invalid
185
+ """
186
+ try:
187
+ return validate_api_key(api_key)
188
+ except ValueError as e:
189
+ raise click.ClickException(str(e))
190
+
191
+
192
+ def coerce_timeout_cli(value) -> int:
193
+ """Coerce timeout value to integer with CLI-friendly error handling.
194
+
195
+ Args:
196
+ value: The timeout value to coerce (int, float, str, etc.)
197
+
198
+ Returns:
199
+ Integer timeout value
200
+
201
+ Raises:
202
+ click.ClickException: If value cannot be coerced to valid timeout
203
+ """
204
+ try:
205
+ return coerce_timeout(value)
206
+ except ValueError as e:
207
+ raise click.ClickException(str(e))
208
+
209
+
210
+ def validate_name_uniqueness_cli(
211
+ _client, name: str, resource_type: str, finder_func
212
+ ) -> None:
213
+ """Validate that a resource name is unique.
214
+
215
+ Args:
216
+ client: API client
217
+ name: Name to validate
218
+ resource_type: Type of resource (for error messages)
219
+ finder_func: Function to find existing resources by name
220
+
221
+ Raises:
222
+ click.ClickException: If name is not unique
223
+ """
224
+ try:
225
+ existing = finder_func(name=name)
226
+ if existing:
227
+ raise click.ClickException(
228
+ f"A {resource_type.lower()} named '{name}' already exists. "
229
+ "Please choose a unique name."
230
+ )
231
+ except click.ClickException:
232
+ raise
233
+ except Exception:
234
+ # Non-fatal: best-effort duplicate check
235
+ pass
@@ -1,231 +1,10 @@
1
1
  #!/usr/bin/env python3
2
- """Main client for AIP SDK.
2
+ """Client module for AIP SDK.
3
3
 
4
4
  Authors:
5
5
  Raymond Christopher (raymond.christopher@gdplabs.id)
6
6
  """
7
7
 
8
- from typing import Any
8
+ from glaip_sdk.client.main import Client
9
9
 
10
- from glaip_sdk.client.agents import AgentClient
11
- from glaip_sdk.client.base import BaseClient
12
- from glaip_sdk.client.mcps import MCPClient
13
- from glaip_sdk.client.tools import ToolClient
14
- from glaip_sdk.models import MCP, Agent, Tool
15
-
16
-
17
- class Client(BaseClient):
18
- """Main client that composes all specialized clients and shares one HTTP session."""
19
-
20
- def __init__(self, **kwargs):
21
- super().__init__(**kwargs)
22
- # Share the single httpx.Client + config with sub-clients
23
- shared_config = {
24
- "parent_client": self,
25
- "api_url": self.api_url,
26
- "api_key": self.api_key,
27
- "timeout": self._timeout,
28
- }
29
- self.agents = AgentClient(**shared_config)
30
- self.tools = ToolClient(**shared_config)
31
- self.mcps = MCPClient(**shared_config)
32
-
33
- # ---- Agents
34
- def list_agents(self) -> list[Agent]:
35
- agents = self.agents.list_agents()
36
- for agent in agents:
37
- agent._set_client(self)
38
- return agents
39
-
40
- def get_agent_by_id(self, agent_id: str) -> Agent:
41
- agent = self.agents.get_agent_by_id(agent_id)
42
- agent._set_client(self)
43
- return agent
44
-
45
- def find_agents(self, name: str | None = None) -> list[Agent]:
46
- agents = self.agents.find_agents(name)
47
- for agent in agents:
48
- agent._set_client(self)
49
- return agents
50
-
51
- def create_agent(
52
- self,
53
- name: str | None = None,
54
- model: str | None = None,
55
- instruction: str | None = None,
56
- tools: list[str | Tool] | None = None,
57
- agents: list[str | Agent] | None = None,
58
- timeout: int = 300,
59
- **kwargs,
60
- ) -> Agent:
61
- agent = self.agents.create_agent(
62
- name=name,
63
- model=model,
64
- instruction=instruction,
65
- tools=tools,
66
- agents=agents,
67
- timeout=timeout,
68
- **kwargs,
69
- )
70
- agent._set_client(self)
71
- return agent
72
-
73
- def delete_agent(self, agent_id: str) -> None:
74
- """Delete an agent by ID."""
75
- return self.agents.delete_agent(agent_id)
76
-
77
- def update_agent(
78
- self, agent_id: str, update_data: dict | None = None, **kwargs
79
- ) -> Agent:
80
- """Update an agent by ID."""
81
- if update_data:
82
- kwargs.update(update_data)
83
- return self.agents.update_agent(agent_id, **kwargs)
84
-
85
- # ---- Tools
86
- def list_tools(self) -> list[Tool]:
87
- tools = self.tools.list_tools()
88
- for tool in tools:
89
- tool._set_client(self)
90
- return tools
91
-
92
- def get_tool_by_id(self, tool_id: str) -> Tool:
93
- tool = self.tools.get_tool_by_id(tool_id)
94
- tool._set_client(self)
95
- return tool
96
-
97
- def find_tools(self, name: str | None = None) -> list[Tool]:
98
- tools = self.tools.find_tools(name)
99
- for tool in tools:
100
- tool._set_client(self)
101
- return tools
102
-
103
- def create_tool(self, **kwargs) -> Tool:
104
- tool = self.tools.create_tool(**kwargs)
105
- tool._set_client(self)
106
- return tool
107
-
108
- def create_tool_from_code(
109
- self,
110
- name: str,
111
- code: str,
112
- framework: str = "langchain",
113
- *,
114
- description: str | None = None,
115
- tags: list[str] | None = None,
116
- ) -> Tool:
117
- """Create a new tool plugin from code string."""
118
- tool = self.tools.create_tool_from_code(
119
- name, code, framework, description=description, tags=tags
120
- )
121
- tool._set_client(self)
122
- return tool
123
-
124
- def update_tool(self, tool_id: str, **kwargs) -> Tool:
125
- """Update an existing tool."""
126
- return self.tools.update_tool(tool_id, **kwargs)
127
-
128
- def delete_tool(self, tool_id: str) -> None:
129
- """Delete a tool by ID."""
130
- return self.tools.delete_tool(tool_id)
131
-
132
- def get_tool_script(self, tool_id: str) -> str:
133
- """Get tool script content."""
134
- return self.tools.get_tool_script(tool_id)
135
-
136
- def update_tool_via_file(self, tool_id: str, file_path: str, **kwargs) -> Tool:
137
- """Update a tool plugin via file upload."""
138
- tool = self.tools.update_tool_via_file(tool_id, file_path, **kwargs)
139
- tool._set_client(self)
140
- return tool
141
-
142
- # ---- MCPs
143
- def list_mcps(self) -> list[MCP]:
144
- mcps = self.mcps.list_mcps()
145
- for mcp in mcps:
146
- mcp._set_client(self)
147
- return mcps
148
-
149
- def get_mcp_by_id(self, mcp_id: str) -> MCP:
150
- mcp = self.mcps.get_mcp_by_id(mcp_id)
151
- mcp._set_client(self)
152
- return mcp
153
-
154
- def find_mcps(self, name: str | None = None) -> list[MCP]:
155
- mcps = self.mcps.find_mcps(name)
156
- for mcp in mcps:
157
- mcp._set_client(self)
158
- return mcps
159
-
160
- def create_mcp(self, **kwargs) -> MCP:
161
- mcp = self.mcps.create_mcp(**kwargs)
162
- mcp._set_client(self)
163
- return mcp
164
-
165
- def delete_mcp(self, mcp_id: str) -> None:
166
- """Delete an MCP by ID."""
167
- return self.mcps.delete_mcp(mcp_id)
168
-
169
- def update_mcp(self, mcp_id: str, **kwargs) -> MCP:
170
- """Update an MCP by ID."""
171
- return self.mcps.update_mcp(mcp_id, **kwargs)
172
-
173
- def test_mcp_connection(self, config: dict[str, Any]) -> dict[str, Any]:
174
- """Test MCP connection using configuration."""
175
- return self.mcps.test_mcp_connection(config)
176
-
177
- def test_mcp_connection_from_config(self, config: dict[str, Any]) -> dict[str, Any]:
178
- """Test MCP connection using configuration (alias)."""
179
- return self.mcps.test_mcp_connection_from_config(config)
180
-
181
- def get_mcp_tools_from_config(self, config: dict[str, Any]) -> list[dict[str, Any]]:
182
- """Fetch tools from MCP configuration without saving."""
183
- return self.mcps.get_mcp_tools_from_config(config)
184
-
185
- def run_agent(self, agent_id: str, message: str, **kwargs) -> str:
186
- """Run an agent with a message."""
187
- return self.agents.run_agent(agent_id, message, **kwargs)
188
-
189
- # ---- Language Models
190
- def list_language_models(self) -> list[dict]:
191
- """List available language models."""
192
- data = self._request("GET", "/language-models")
193
- return data or []
194
-
195
- # ---- Timeout propagation ----
196
- @property
197
- def timeout(self) -> float: # type: ignore[override]
198
- return super().timeout
199
-
200
- @timeout.setter
201
- def timeout(self, value: float) -> None: # type: ignore[override]
202
- # Rebuild the root http client
203
- BaseClient.timeout.fset(self, value) # call parent setter
204
- # Propagate the new session to sub-clients so they don't hold a closed client
205
- try:
206
- if hasattr(self, "agents"):
207
- self.agents.http_client = self.http_client
208
- if hasattr(self, "tools"):
209
- self.tools.http_client = self.http_client
210
- if hasattr(self, "mcps"):
211
- self.mcps.http_client = self.http_client
212
- except Exception:
213
- pass
214
-
215
- # ---- Aliases (back-compat)
216
- def get_agent(self, agent_id: str) -> Agent:
217
- return self.get_agent_by_id(agent_id)
218
-
219
- def get_tool(self, tool_id: str) -> Tool:
220
- return self.get_tool_by_id(tool_id)
221
-
222
- def get_mcp(self, mcp_id: str) -> MCP:
223
- return self.get_mcp_by_id(mcp_id)
224
-
225
- # ---- Health
226
- def ping(self) -> bool:
227
- try:
228
- self._request("GET", "/health-check")
229
- return True
230
- except Exception:
231
- return False
10
+ __all__ = ["Client"]