agi-python 0.0.1__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.
agi/client.py ADDED
@@ -0,0 +1,142 @@
1
+ """Main AGI API client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Any
7
+
8
+ from agi._http import HTTPClient
9
+ from agi._session_context import SessionContext
10
+ from agi.resources.sessions import SessionsResource
11
+
12
+
13
+ class AGIClient:
14
+ """Official Python client for the AGI.tech API.
15
+
16
+ The AGIClient provides access to the AGI API for creating and managing
17
+ AI agent sessions that can perform complex web tasks.
18
+
19
+ Example:
20
+ Simple usage with context manager (recommended):
21
+
22
+ >>> from agi import AGIClient
23
+ >>> client = AGIClient(api_key="your_api_key")
24
+ >>>
25
+ >>> with client.session("agi-0") as session:
26
+ ... result = session.run_task("Find cheapest iPhone 15 on Amazon")
27
+ ... print(result)
28
+
29
+ Advanced usage with direct API access:
30
+
31
+ >>> session = client.sessions.create(
32
+ ... agent_name="agi-0",
33
+ ... webhook_url="https://yourapp.com/webhook"
34
+ ... )
35
+ >>> client.sessions.send_message(session.session_id, "Find flights...")
36
+ >>>
37
+ >>> for event in client.sessions.stream_events(session.session_id):
38
+ ... if event.event == "thought":
39
+ ... print(f"Agent: {event.data}")
40
+ ... elif event.event == "done":
41
+ ... break
42
+ >>>
43
+ >>> client.sessions.delete(session.session_id)
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ api_key: str | None = None,
49
+ base_url: str = "https://api.agi.tech",
50
+ timeout: int = 60,
51
+ max_retries: int = 3,
52
+ ):
53
+ """Initialize the AGI API client.
54
+
55
+ Args:
56
+ api_key: API key for authentication. If not provided, will look
57
+ for AGI_API_KEY environment variable.
58
+ base_url: Base URL for the API (default: https://api.agi.tech)
59
+ timeout: Request timeout in seconds (default: 60)
60
+ max_retries: Maximum number of retry attempts for 5xx errors (default: 3)
61
+
62
+ Raises:
63
+ ValueError: If api_key is not provided and AGI_API_KEY env var is not set
64
+
65
+ Example:
66
+ >>> client = AGIClient(api_key="your_api_key")
67
+ >>> # Or use environment variable:
68
+ >>> import os
69
+ >>> os.environ["AGI_API_KEY"] = "your_api_key"
70
+ >>> client = AGIClient()
71
+ """
72
+ self.api_key = api_key or os.getenv("AGI_API_KEY")
73
+ if not self.api_key:
74
+ raise ValueError(
75
+ "api_key is required. Either pass it as a parameter or set "
76
+ "the AGI_API_KEY environment variable."
77
+ )
78
+
79
+ self._http = HTTPClient(
80
+ api_key=self.api_key,
81
+ base_url=base_url,
82
+ timeout=timeout,
83
+ max_retries=max_retries,
84
+ )
85
+
86
+ self.sessions = SessionsResource(self._http)
87
+
88
+ def session(
89
+ self,
90
+ agent_name: str = "agi-0",
91
+ **kwargs: Any,
92
+ ) -> SessionContext:
93
+ """Create a session context manager for easy session lifecycle management.
94
+
95
+ This is the recommended way to use the SDK, matching the pattern shown
96
+ in the documentation. The context manager automatically creates and
97
+ deletes the session.
98
+
99
+ Args:
100
+ agent_name: Agent model to use (e.g., "agi-0", "agi-0-fast")
101
+ **kwargs: Additional session creation parameters:
102
+ - webhook_url (str): URL for session event notifications
103
+ - goal (str): Task goal to set upfront
104
+ - max_steps (int): Maximum number of agent steps (default: 100)
105
+ - restore_from_environment_id (str): Environment UUID to restore
106
+
107
+ Returns:
108
+ SessionContext manager
109
+
110
+ Example:
111
+ >>> with client.session("agi-0") as session:
112
+ ... result = session.run_task("Find flights SFO→JFK under $450")
113
+ ... print(result)
114
+ >>>
115
+ >>> # With webhook
116
+ >>> with client.session(
117
+ ... "agi-0",
118
+ ... webhook_url="https://yourapp.com/webhook"
119
+ ... ) as session:
120
+ ... result = session.run_task("Research company XYZ")
121
+ """
122
+ return SessionContext(self, agent_name=agent_name, **kwargs)
123
+
124
+ def close(self) -> None:
125
+ """Close the HTTP client and cleanup resources.
126
+
127
+ Note: Usually not needed as the client will cleanup automatically.
128
+
129
+ Example:
130
+ >>> client = AGIClient(api_key="...")
131
+ >>> # ... use client ...
132
+ >>> client.close()
133
+ """
134
+ self._http.close()
135
+
136
+ def __enter__(self) -> AGIClient:
137
+ """Support using AGIClient as a context manager."""
138
+ return self
139
+
140
+ def __exit__(self, *args: Any) -> None:
141
+ """Cleanup when used as context manager."""
142
+ self.close()
agi/exceptions.py ADDED
@@ -0,0 +1,43 @@
1
+ """AGI SDK exceptions."""
2
+
3
+
4
+ class AGIError(Exception):
5
+ """Base exception for all AGI SDK errors."""
6
+
7
+ pass
8
+
9
+
10
+ class AuthenticationError(AGIError):
11
+ """401 - Invalid API key."""
12
+
13
+ pass
14
+
15
+
16
+ class PermissionError(AGIError):
17
+ """403 - Insufficient permissions."""
18
+
19
+ pass
20
+
21
+
22
+ class NotFoundError(AGIError):
23
+ """404 - Resource not found."""
24
+
25
+ pass
26
+
27
+
28
+ class RateLimitError(AGIError):
29
+ """429 - Rate limit exceeded."""
30
+
31
+ pass
32
+
33
+
34
+ class APIError(AGIError):
35
+ """5xx - Server error."""
36
+
37
+ pass
38
+
39
+
40
+ class AgentExecutionError(AGIError):
41
+ """Agent task execution failed."""
42
+
43
+ pass
agi/py.typed ADDED
File without changes
@@ -0,0 +1,5 @@
1
+ """API resources."""
2
+
3
+ from agi.resources.sessions import SessionsResource
4
+
5
+ __all__ = ["SessionsResource"]
@@ -0,0 +1,358 @@
1
+ """Sessions API resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import builtins
6
+ from collections.abc import Iterator
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from agi.types.sessions import (
10
+ DeleteResponse,
11
+ ExecuteStatusResponse,
12
+ MessagesResponse,
13
+ NavigateResponse,
14
+ ScreenshotResponse,
15
+ SessionResponse,
16
+ SSEEvent,
17
+ SuccessResponse,
18
+ )
19
+ from agi.types.shared import SnapshotMode
20
+
21
+ if TYPE_CHECKING:
22
+ from agi._http import HTTPClient
23
+
24
+
25
+ class SessionsResource:
26
+ """Sessions API resource providing all session-related operations."""
27
+
28
+ def __init__(self, http: HTTPClient):
29
+ """Initialize sessions resource.
30
+
31
+ Args:
32
+ http: HTTP client instance
33
+ """
34
+ self._http = http
35
+
36
+ # ===== SESSION MANAGEMENT (5 endpoints) =====
37
+
38
+ def create(
39
+ self,
40
+ agent_name: str = "agi-0",
41
+ webhook_url: str | None = None,
42
+ goal: str | None = None,
43
+ max_steps: int = 100,
44
+ restore_from_environment_id: str | None = None,
45
+ ) -> SessionResponse:
46
+ """Create a new agent session.
47
+
48
+ Args:
49
+ agent_name: Agent model to use (e.g., "agi-0", "agi-0-fast")
50
+ webhook_url: URL for session event notifications
51
+ goal: Task goal (optional, can be set later via send_message)
52
+ max_steps: Maximum number of agent steps (default: 100)
53
+ restore_from_environment_id: Environment UUID to restore from
54
+
55
+ Returns:
56
+ SessionResponse with session_id, vnc_url, status, etc.
57
+
58
+ Example:
59
+ >>> session = client.sessions.create(
60
+ ... agent_name="agi-0",
61
+ ... goal="Find cheapest iPhone 15 on Amazon"
62
+ ... )
63
+ >>> print(session.session_id)
64
+ """
65
+ payload = {
66
+ "agent_name": agent_name,
67
+ "max_steps": max_steps,
68
+ }
69
+
70
+ if webhook_url is not None:
71
+ payload["webhook_url"] = webhook_url
72
+ if goal is not None:
73
+ payload["goal"] = goal
74
+ if restore_from_environment_id is not None:
75
+ payload["restore_from_environment_id"] = restore_from_environment_id
76
+
77
+ response = self._http.request("POST", "/v1/sessions", json=payload)
78
+ return SessionResponse(**response.json())
79
+
80
+ def list(self) -> list[SessionResponse]:
81
+ """List all sessions for the authenticated user.
82
+
83
+ Returns:
84
+ List of SessionResponse objects
85
+
86
+ Example:
87
+ >>> sessions = client.sessions.list()
88
+ >>> for session in sessions:
89
+ ... print(f"{session.session_id}: {session.status}")
90
+ """
91
+ response = self._http.request("GET", "/v1/sessions")
92
+ return [SessionResponse(**s) for s in response.json()]
93
+
94
+ def get(self, session_id: str) -> SessionResponse:
95
+ """Get details for a specific session.
96
+
97
+ Args:
98
+ session_id: Session UUID
99
+
100
+ Returns:
101
+ SessionResponse with session details
102
+
103
+ Raises:
104
+ NotFoundError: If session doesn't exist
105
+
106
+ Example:
107
+ >>> session = client.sessions.get("123e4567-e89b-12d3-a456-426614174000")
108
+ >>> print(session.status)
109
+ """
110
+ response = self._http.request("GET", f"/v1/sessions/{session_id}")
111
+ return SessionResponse(**response.json())
112
+
113
+ def delete(
114
+ self,
115
+ session_id: str,
116
+ save_snapshot_mode: SnapshotMode = "none",
117
+ ) -> DeleteResponse:
118
+ """Delete a session and cleanup its resources.
119
+
120
+ Args:
121
+ session_id: Session UUID
122
+ save_snapshot_mode: Snapshot mode - "none", "memory", or "filesystem"
123
+
124
+ Returns:
125
+ DeleteResponse confirming deletion
126
+
127
+ Example:
128
+ >>> client.sessions.delete("123e4567-e89b-12d3-a456-426614174000")
129
+ """
130
+ response = self._http.request(
131
+ "DELETE",
132
+ f"/v1/sessions/{session_id}",
133
+ params={"save_snapshot_mode": save_snapshot_mode},
134
+ )
135
+ return DeleteResponse(**response.json())
136
+
137
+ def delete_all(self) -> DeleteResponse:
138
+ """Delete all sessions for the authenticated user.
139
+
140
+ Returns:
141
+ DeleteResponse with count of deleted sessions
142
+
143
+ Example:
144
+ >>> result = client.sessions.delete_all()
145
+ >>> print(result.message)
146
+ """
147
+ response = self._http.request("DELETE", "/v1/sessions")
148
+ return DeleteResponse(**response.json())
149
+
150
+ # ===== AGENT INTERACTION (4 endpoints) =====
151
+
152
+ def send_message(
153
+ self,
154
+ session_id: str,
155
+ message: str,
156
+ start_url: str | None = None,
157
+ config_updates: dict[str, Any] | None = None,
158
+ ) -> SuccessResponse:
159
+ """Send a message to the agent to start a task or respond to questions.
160
+
161
+ Args:
162
+ session_id: Session UUID
163
+ message: Message content (task instruction or response)
164
+ start_url: Optional starting URL for the task
165
+ config_updates: Optional configuration updates
166
+
167
+ Returns:
168
+ SuccessResponse confirming message was sent
169
+
170
+ Example:
171
+ >>> client.sessions.send_message(
172
+ ... session_id="123...",
173
+ ... message="Find flights from SFO to JFK under $450"
174
+ ... )
175
+ """
176
+ payload: dict[str, Any] = {"message": message}
177
+
178
+ if start_url is not None:
179
+ payload["start_url"] = start_url
180
+ if config_updates is not None:
181
+ payload["config_updates"] = config_updates
182
+
183
+ response = self._http.request(
184
+ "POST",
185
+ f"/v1/sessions/{session_id}/message",
186
+ json=payload,
187
+ )
188
+ return SuccessResponse(**response.json())
189
+
190
+ def get_status(self, session_id: str) -> ExecuteStatusResponse:
191
+ """Get the current execution status of a session.
192
+
193
+ Args:
194
+ session_id: Session UUID
195
+
196
+ Returns:
197
+ ExecuteStatusResponse with status ("running", "finished", etc.)
198
+
199
+ Example:
200
+ >>> status = client.sessions.get_status("123...")
201
+ >>> if status.status == "finished":
202
+ ... print("Task completed!")
203
+ """
204
+ response = self._http.request("GET", f"/v1/sessions/{session_id}/status")
205
+ return ExecuteStatusResponse(**response.json())
206
+
207
+ def get_messages(
208
+ self,
209
+ session_id: str,
210
+ after_id: int = 0,
211
+ sanitize: bool = True,
212
+ ) -> MessagesResponse:
213
+ """Poll for messages and updates from the agent.
214
+
215
+ Args:
216
+ session_id: Session UUID
217
+ after_id: Return messages with ID > after_id (for polling)
218
+ sanitize: Filter out system messages, prompts, and images
219
+
220
+ Returns:
221
+ MessagesResponse with messages list and status
222
+
223
+ Example:
224
+ >>> messages = client.sessions.get_messages("123...", after_id=0)
225
+ >>> for msg in messages.messages:
226
+ ... print(f"[{msg.type}] {msg.content}")
227
+ """
228
+ response = self._http.request(
229
+ "GET",
230
+ f"/v1/sessions/{session_id}/messages",
231
+ params={"after_id": after_id, "sanitize": sanitize},
232
+ )
233
+ return MessagesResponse(**response.json())
234
+
235
+ def stream_events(
236
+ self,
237
+ session_id: str,
238
+ event_types: builtins.list[str] | None = None,
239
+ sanitize: bool = True,
240
+ include_history: bool = True,
241
+ ) -> Iterator[SSEEvent]:
242
+ """Stream real-time events from the session via Server-Sent Events.
243
+
244
+ Args:
245
+ session_id: Session UUID
246
+ event_types: Filter specific event types (e.g., ["thought", "done"])
247
+ sanitize: Filter out system messages
248
+ include_history: Include historical messages on connection
249
+
250
+ Yields:
251
+ SSEEvent objects with id, event type, and data
252
+
253
+ Example:
254
+ >>> for event in client.sessions.stream_events("123..."):
255
+ ... if event.event == "thought":
256
+ ... print(f"Agent: {event.data}")
257
+ ... elif event.event == "done":
258
+ ... print(f"Result: {event.data}")
259
+ ... break
260
+ """
261
+ from agi._sse import SSEClient
262
+
263
+ sse = SSEClient(self._http)
264
+ params: dict[str, Any] = {
265
+ "sanitize": sanitize,
266
+ "include_history": include_history,
267
+ }
268
+
269
+ if event_types:
270
+ params["event_types"] = ",".join(event_types)
271
+
272
+ yield from sse.stream(f"/v1/sessions/{session_id}/events", params=params)
273
+
274
+ # ===== SESSION CONTROL (3 endpoints) =====
275
+
276
+ def pause(self, session_id: str) -> SuccessResponse:
277
+ """Temporarily pause task execution.
278
+
279
+ Args:
280
+ session_id: Session UUID
281
+
282
+ Returns:
283
+ SuccessResponse confirming pause
284
+
285
+ Example:
286
+ >>> client.sessions.pause("123...")
287
+ """
288
+ response = self._http.request("POST", f"/v1/sessions/{session_id}/pause")
289
+ return SuccessResponse(**response.json())
290
+
291
+ def resume(self, session_id: str) -> SuccessResponse:
292
+ """Resume a paused task.
293
+
294
+ Args:
295
+ session_id: Session UUID
296
+
297
+ Returns:
298
+ SuccessResponse confirming resume
299
+
300
+ Example:
301
+ >>> client.sessions.resume("123...")
302
+ """
303
+ response = self._http.request("POST", f"/v1/sessions/{session_id}/resume")
304
+ return SuccessResponse(**response.json())
305
+
306
+ def cancel(self, session_id: str) -> SuccessResponse:
307
+ """Cancel task execution.
308
+
309
+ Args:
310
+ session_id: Session UUID
311
+
312
+ Returns:
313
+ SuccessResponse confirming cancellation
314
+
315
+ Example:
316
+ >>> client.sessions.cancel("123...")
317
+ """
318
+ response = self._http.request("POST", f"/v1/sessions/{session_id}/cancel")
319
+ return SuccessResponse(**response.json())
320
+
321
+ # ===== BROWSER CONTROL (2 endpoints) =====
322
+
323
+ def navigate(self, session_id: str, url: str) -> NavigateResponse:
324
+ """Navigate the browser to a specific URL.
325
+
326
+ Args:
327
+ session_id: Session UUID
328
+ url: URL to navigate to
329
+
330
+ Returns:
331
+ NavigateResponse with current URL
332
+
333
+ Example:
334
+ >>> client.sessions.navigate("123...", "https://amazon.com")
335
+ """
336
+ response = self._http.request(
337
+ "POST",
338
+ f"/v1/sessions/{session_id}/navigate",
339
+ json={"url": url},
340
+ )
341
+ return NavigateResponse(**response.json())
342
+
343
+ def screenshot(self, session_id: str) -> ScreenshotResponse:
344
+ """Get a screenshot of the browser.
345
+
346
+ Args:
347
+ session_id: Session UUID
348
+
349
+ Returns:
350
+ ScreenshotResponse with base64-encoded image, URL, and title
351
+
352
+ Example:
353
+ >>> screenshot = client.sessions.screenshot("123...")
354
+ >>> print(screenshot.url)
355
+ >>> # screenshot.screenshot contains base64 JPEG data
356
+ """
357
+ response = self._http.request("GET", f"/v1/sessions/{session_id}/screenshot")
358
+ return ScreenshotResponse(**response.json())
agi/types/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ """Type definitions for AGI SDK."""
2
+
3
+ from agi.types.results import Screenshot, TaskMetadata, TaskResult
4
+ from agi.types.sessions import (
5
+ CreateSessionRequest,
6
+ DeleteResponse,
7
+ ExecuteStatusResponse,
8
+ MessageResponse,
9
+ MessagesResponse,
10
+ NavigateRequest,
11
+ NavigateResponse,
12
+ ScreenshotResponse,
13
+ SendMessageRequest,
14
+ SessionResponse,
15
+ SSEEvent,
16
+ SuccessResponse,
17
+ )
18
+ from agi.types.shared import EventType, MessageType, SessionStatus
19
+
20
+ __all__ = [
21
+ "CreateSessionRequest",
22
+ "DeleteResponse",
23
+ "ExecuteStatusResponse",
24
+ "MessageResponse",
25
+ "MessagesResponse",
26
+ "NavigateRequest",
27
+ "NavigateResponse",
28
+ "Screenshot",
29
+ "ScreenshotResponse",
30
+ "SendMessageRequest",
31
+ "SessionResponse",
32
+ "SSEEvent",
33
+ "SuccessResponse",
34
+ "TaskMetadata",
35
+ "TaskResult",
36
+ "EventType",
37
+ "MessageType",
38
+ "SessionStatus",
39
+ ]