glaip-sdk 0.0.1b5__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.
@@ -0,0 +1,28 @@
1
+ """Configuration constants for the AIP SDK.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ # Default language model configuration
8
+ DEFAULT_MODEL = "gpt-4.1"
9
+ DEFAULT_MODEL_PROVIDER = "openai"
10
+
11
+ # Default timeout values
12
+ DEFAULT_TIMEOUT = 30.0
13
+ DEFAULT_AGENT_TIMEOUT = 300.0
14
+
15
+ # User agent
16
+ SDK_NAME = "glaip-sdk"
17
+ SDK_VERSION = "0.1.0"
18
+
19
+ # Reserved names that cannot be used for agents/tools
20
+ RESERVED_NAMES = {
21
+ "system",
22
+ "admin",
23
+ "root",
24
+ "test",
25
+ "example",
26
+ "demo",
27
+ "sample",
28
+ }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env python3
2
+ """Custom exceptions for AIP SDK.
3
+
4
+ Authors:
5
+ Raymond Christopher (raymond.christopher@gdplabs.id)
6
+ """
7
+
8
+ from typing import Any
9
+
10
+
11
+ class AIPError(Exception):
12
+ """Base exception for AIP SDK."""
13
+
14
+ pass
15
+
16
+
17
+ class APIError(AIPError):
18
+ """Base API exception with rich context."""
19
+
20
+ def __init__(
21
+ self,
22
+ message: str,
23
+ *,
24
+ status_code: int | None = None,
25
+ error_type: str | None = None,
26
+ payload: Any = None,
27
+ request_id: str | None = None,
28
+ ):
29
+ super().__init__(message)
30
+ self.status_code = status_code
31
+ self.error_type = error_type
32
+ self.payload = payload
33
+ self.request_id = request_id
34
+
35
+
36
+ class AuthenticationError(APIError):
37
+ """Authentication failed."""
38
+
39
+ pass
40
+
41
+
42
+ class ValidationError(APIError):
43
+ """Validation failed."""
44
+
45
+ pass
46
+
47
+
48
+ class ForbiddenError(APIError):
49
+ """Access forbidden."""
50
+
51
+ pass
52
+
53
+
54
+ class NotFoundError(APIError):
55
+ """Resource not found."""
56
+
57
+ pass
58
+
59
+
60
+ class ConflictError(APIError):
61
+ """Resource conflict."""
62
+
63
+ pass
64
+
65
+
66
+ class AmbiguousResourceError(APIError):
67
+ """Multiple resources match the query."""
68
+
69
+ pass
70
+
71
+
72
+ class ServerError(APIError):
73
+ """Server error."""
74
+
75
+ pass
76
+
77
+
78
+ class RateLimitError(APIError):
79
+ """Rate limit exceeded."""
80
+
81
+ pass
82
+
83
+
84
+ class TimeoutError(APIError):
85
+ """Request timeout."""
86
+
87
+ pass
88
+
89
+
90
+ class ClientError(APIError):
91
+ """Client-side error (e.g., invalid request format, missing parameters)."""
92
+
93
+ pass
glaip_sdk/models.py ADDED
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env python3
2
+ """Data models for AIP SDK.
3
+
4
+ Authors:
5
+ Raymond Christopher (raymond.christopher@gdplabs.id)
6
+ """
7
+
8
+ from typing import Any
9
+
10
+ from pydantic import BaseModel
11
+
12
+
13
+ class Agent(BaseModel):
14
+ """Agent model for API responses."""
15
+
16
+ id: str
17
+ name: str
18
+ instruction: str | None = None
19
+ description: str | None = None # Add missing description field
20
+ type: str | None = None
21
+ framework: str | None = None
22
+ version: str | None = None
23
+ tools: list[dict[str, Any]] | None = None # Backend returns ToolReference objects
24
+ agents: list[dict[str, Any]] | None = None # Backend returns AgentReference objects
25
+ agent_config: dict[str, Any] | None = None
26
+ timeout: int = 300
27
+ _client: Any = None # Will hold client reference
28
+
29
+ def _set_client(self, client):
30
+ """Set the client reference for this resource."""
31
+ self._client = client
32
+ return self
33
+
34
+ def run(self, message: str, **kwargs) -> str:
35
+ """Run the agent with a message."""
36
+ if not self._client:
37
+ raise RuntimeError(
38
+ "No client available. Use client.get_agent_by_id() to get a client-connected agent."
39
+ )
40
+ # Automatically pass the agent name for better renderer display
41
+ kwargs.setdefault("agent_name", self.name)
42
+ return self._client.run_agent(self.id, message, **kwargs)
43
+
44
+ def update(self, **kwargs) -> "Agent":
45
+ """Update agent attributes."""
46
+ if not self._client:
47
+ raise RuntimeError(
48
+ "No client available. Use client.get_agent_by_id() to get a client-connected agent."
49
+ )
50
+ updated_agent = self._client.update_agent(self.id, **kwargs)
51
+ # Update current instance with new data
52
+ for key, value in updated_agent.model_dump().items():
53
+ if hasattr(self, key):
54
+ setattr(self, key, value)
55
+ return self
56
+
57
+ def delete(self) -> None:
58
+ """Delete the agent."""
59
+ if not self._client:
60
+ raise RuntimeError(
61
+ "No client available. Use client.get_agent_by_id() to get a client-connected agent."
62
+ )
63
+ self._client.delete_agent(self.id)
64
+
65
+
66
+ class Tool(BaseModel):
67
+ """Tool model for API responses."""
68
+
69
+ id: str
70
+ name: str
71
+ tool_type: str | None = None
72
+ description: str | None = None
73
+ framework: str | None = None
74
+ version: str | None = None
75
+ tool_script: str | None = None
76
+ tool_file: str | None = None
77
+ _client: Any = None # Will hold client reference
78
+
79
+ def _set_client(self, client):
80
+ """Set the client reference for this resource."""
81
+ self._client = client
82
+ return self
83
+
84
+ def get_script(self) -> str:
85
+ """Get the tool script content."""
86
+ if self.tool_script:
87
+ return self.tool_script
88
+ elif self.tool_file:
89
+ return f"Script content from file: {self.tool_file}"
90
+ else:
91
+ return "No script content available"
92
+
93
+ def update(self, **kwargs) -> "Tool":
94
+ """Update tool attributes."""
95
+ if not self._client:
96
+ raise RuntimeError(
97
+ "No client available. Use client.get_tool_by_id() to get a client-connected tool."
98
+ )
99
+ updated_tool = self._client.tools.update_tool(self.id, **kwargs)
100
+ # Update current instance with new data
101
+ for key, value in updated_tool.model_dump().items():
102
+ if hasattr(self, key):
103
+ setattr(self, key, value)
104
+ return self
105
+
106
+ def delete(self) -> None:
107
+ """Delete the tool."""
108
+ if not self._client:
109
+ raise RuntimeError(
110
+ "No client available. Use client.get_tool_by_id() to get a client-connected tool."
111
+ )
112
+ self._client.tools.delete_tool(self.id)
113
+
114
+
115
+ class MCP(BaseModel):
116
+ """MCP model for API responses."""
117
+
118
+ id: str
119
+ name: str
120
+ description: str | None = None
121
+ config: dict[str, Any] | None = None
122
+ framework: str | None = None
123
+ version: str | None = None
124
+ transport: str | None = None # "sse" or "http"
125
+ authentication: dict[str, Any] | None = None
126
+ metadata: dict[str, Any] | None = None
127
+ _client: Any = None # Will hold client reference
128
+
129
+ def _set_client(self, client):
130
+ """Set the client reference for this resource."""
131
+ self._client = client
132
+ return self
133
+
134
+ def get_tools(self) -> list[dict[str, Any]]:
135
+ """Get tools available from this MCP."""
136
+ if not self._client:
137
+ raise RuntimeError(
138
+ "No client available. Use client.get_mcp_by_id() to get a client-connected MCP."
139
+ )
140
+ # This would delegate to the client's MCP tools endpoint
141
+ # For now, return empty list as placeholder
142
+ return []
143
+
144
+ def update(self, **kwargs) -> "MCP":
145
+ """Update MCP attributes."""
146
+ if not self._client:
147
+ raise RuntimeError(
148
+ "No client available. Use client.get_mcp_by_id() to get a client-connected MCP."
149
+ )
150
+ updated_mcp = self._client.update_mcp(self.id, **kwargs)
151
+ # Update current instance with new data
152
+ for key, value in updated_mcp.model_dump().items():
153
+ if hasattr(self, key):
154
+ setattr(self, key, value)
155
+ return self
156
+
157
+ def delete(self) -> None:
158
+ """Delete the MCP."""
159
+ if not self._client:
160
+ raise RuntimeError(
161
+ "No client available. Use client.get_mcp_by_id() to get a client-connected MCP."
162
+ )
163
+ self._client.delete_mcp(self.id)
164
+
165
+
166
+ class LanguageModelResponse(BaseModel):
167
+ """Language model response model."""
168
+
169
+ name: str
170
+ provider: str
171
+ description: str | None = None
172
+ capabilities: list[str] | None = None
173
+ max_tokens: int | None = None
174
+ supports_streaming: bool = False
175
+
176
+
177
+ class TTYRenderer:
178
+ """Simple TTY renderer for non-Rich environments."""
179
+
180
+ def __init__(self, use_color: bool = True):
181
+ self.use_color = use_color
182
+
183
+ def render_message(self, message: str, event_type: str = "message"):
184
+ """Render a message with optional color."""
185
+ if event_type == "error":
186
+ print(f"ERROR: {message}", flush=True)
187
+ elif event_type == "done":
188
+ print(f"\n✅ {message}", flush=True)
189
+ else:
190
+ print(message, flush=True)
@@ -0,0 +1,95 @@
1
+ """Utility modules for AIP SDK.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ import re
8
+ from uuid import UUID
9
+
10
+ from .run_renderer import (
11
+ RichStreamRenderer,
12
+ RunStats,
13
+ Step,
14
+ StepManager,
15
+ )
16
+
17
+ __all__ = [
18
+ "RichStreamRenderer",
19
+ "RunStats",
20
+ "Step",
21
+ "StepManager",
22
+ "is_uuid",
23
+ "sanitize_name",
24
+ "format_file_size",
25
+ "progress_bar",
26
+ ]
27
+
28
+
29
+ def is_uuid(value: str) -> bool:
30
+ """Check if a string is a valid UUID.
31
+
32
+ Args:
33
+ value: String to check
34
+
35
+ Returns:
36
+ True if value is a valid UUID, False otherwise
37
+ """
38
+ try:
39
+ UUID(value)
40
+ return True
41
+ except (ValueError, TypeError):
42
+ return False
43
+
44
+
45
+ def sanitize_name(name: str) -> str:
46
+ """Sanitize a name for resource creation.
47
+
48
+ Args:
49
+ name: Raw name input
50
+
51
+ Returns:
52
+ Sanitized name suitable for resource creation
53
+ """
54
+ # Remove special characters and normalize
55
+ sanitized = re.sub(r"[^a-zA-Z0-9\-_]", "-", name.strip())
56
+ sanitized = re.sub(r"-+", "-", sanitized) # Collapse multiple dashes
57
+ return sanitized.lower().strip("-")
58
+
59
+
60
+ def format_file_size(size_bytes: int) -> str:
61
+ """Format file size in human readable format.
62
+
63
+ Args:
64
+ size_bytes: Size in bytes
65
+
66
+ Returns:
67
+ Human readable size string (e.g., "1.5 MB")
68
+ """
69
+ for unit in ["B", "KB", "MB", "GB"]:
70
+ if size_bytes < 1024.0:
71
+ return f"{size_bytes:.1f} {unit}"
72
+ size_bytes /= 1024.0
73
+ return f"{size_bytes:.1f} TB"
74
+
75
+
76
+ def progress_bar(iterable, description: str = "Processing"):
77
+ """Simple progress bar using click.
78
+
79
+ Args:
80
+ iterable: Iterable to process
81
+ description: Progress description
82
+
83
+ Yields:
84
+ Items from iterable with progress display
85
+ """
86
+ try:
87
+ import click
88
+
89
+ with click.progressbar(iterable, label=description) as bar:
90
+ for item in bar:
91
+ yield item
92
+ except ImportError:
93
+ # Fallback if click not available
94
+ for item in iterable:
95
+ yield item