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/types/results.py ADDED
@@ -0,0 +1,183 @@
1
+ """Result types for task execution."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ from datetime import datetime
7
+ from typing import Any, Generic, TypeVar
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ class Screenshot(BaseModel):
15
+ """
16
+ Browser screenshot with image data and metadata.
17
+
18
+ Capture the current browser state as an image. Use session.screenshot()
19
+ to obtain a screenshot, then save it to disk with the save() method.
20
+
21
+ Example:
22
+ >>> screenshot = session.screenshot()
23
+ >>> screenshot.save("screenshot.png")
24
+ >>> print(f"Size: {screenshot.width}x{screenshot.height}")
25
+ >>> print(f"URL: {screenshot.url}")
26
+ """
27
+
28
+ model_config = ConfigDict(arbitrary_types_allowed=True)
29
+
30
+ data: bytes = Field(description="Screenshot image data")
31
+ format: str = Field(default="png", description="Image format (png, jpg)")
32
+ timestamp: datetime = Field(description="Screenshot timestamp")
33
+ width: int = Field(description="Image width in pixels")
34
+ height: int = Field(description="Image height in pixels")
35
+ url: str = Field(description="Current page URL")
36
+ title: str = Field(description="Current page title")
37
+
38
+ @field_serializer("timestamp")
39
+ def serialize_timestamp(self, value: datetime, _info: Any) -> str:
40
+ """Serialize datetime to ISO format string."""
41
+ return value.isoformat()
42
+
43
+ @field_serializer("data")
44
+ def serialize_data(self, value: bytes, _info: Any) -> str:
45
+ """Serialize bytes to hex string."""
46
+ return value.hex()
47
+
48
+ def save(self, path: str) -> None:
49
+ """Save screenshot to file.
50
+
51
+ Args:
52
+ path: File path to save to (e.g., "screenshot.png")
53
+
54
+ Example:
55
+ >>> screenshot = session.screenshot()
56
+ >>> screenshot.save("page.png")
57
+ """
58
+ with open(path, "wb") as f:
59
+ f.write(self.data)
60
+
61
+ @classmethod
62
+ def from_base64(
63
+ cls, base64_data: str, url: str, title: str, timestamp: datetime | None = None
64
+ ) -> Screenshot:
65
+ """Create Screenshot from base64 data URL.
66
+
67
+ Args:
68
+ base64_data: Base64-encoded data URL (e.g., "data:image/jpeg;base64,...")
69
+ url: Current page URL
70
+ title: Current page title
71
+ timestamp: Screenshot timestamp (defaults to now)
72
+
73
+ Returns:
74
+ Screenshot instance with decoded image data
75
+ """
76
+ # Parse data URL format: "data:image/jpeg;base64,..."
77
+ if "," in base64_data:
78
+ header, encoded = base64_data.split(",", 1)
79
+ else:
80
+ # Assume it's just base64 without header
81
+ encoded = base64_data
82
+ header = "data:image/png;base64"
83
+
84
+ # Decode base64 to bytes
85
+ image_data = base64.b64decode(encoded)
86
+
87
+ # Determine format from header
88
+ if "jpeg" in header.lower() or "jpg" in header.lower():
89
+ fmt = "jpg"
90
+ else:
91
+ fmt = "png"
92
+
93
+ # Extract image dimensions
94
+ width, height = cls._get_image_dimensions(image_data, fmt)
95
+
96
+ return cls(
97
+ data=image_data,
98
+ format=fmt,
99
+ timestamp=timestamp or datetime.now(),
100
+ width=width,
101
+ height=height,
102
+ url=url,
103
+ title=title,
104
+ )
105
+
106
+ @staticmethod
107
+ def _get_image_dimensions(data: bytes, fmt: str) -> tuple[int, int]:
108
+ """Extract width and height from image data.
109
+
110
+ Args:
111
+ data: Raw image bytes
112
+ fmt: Image format (png, jpg)
113
+
114
+ Returns:
115
+ Tuple of (width, height)
116
+ """
117
+ try:
118
+ if fmt == "png":
119
+ # PNG: width/height at bytes 16-24
120
+ if len(data) >= 24:
121
+ width = int.from_bytes(data[16:20], byteorder="big")
122
+ height = int.from_bytes(data[20:24], byteorder="big")
123
+ return width, height
124
+ elif fmt in ("jpg", "jpeg"):
125
+ # JPEG: scan for SOF0 marker (0xFFC0)
126
+ i = 0
127
+ while i < len(data) - 9:
128
+ if data[i] == 0xFF and data[i + 1] == 0xC0:
129
+ height = int.from_bytes(data[i + 5 : i + 7], byteorder="big")
130
+ width = int.from_bytes(data[i + 7 : i + 9], byteorder="big")
131
+ return width, height
132
+ i += 1
133
+ except Exception:
134
+ pass
135
+
136
+ # Fallback if parsing fails
137
+ return 0, 0
138
+
139
+
140
+ class TaskMetadata(BaseModel):
141
+ """
142
+ Task execution metadata and performance metrics.
143
+
144
+ Contains information about how the task was executed, including duration,
145
+ number of steps, and success status. Access via result.metadata.
146
+
147
+ Example:
148
+ >>> result = session.run_task("Find hotels in Paris")
149
+ >>> print(f"Duration: {result.metadata.duration}s")
150
+ >>> print(f"Steps taken: {result.metadata.steps}")
151
+ >>> print(f"Success: {result.metadata.success}")
152
+ """
153
+
154
+ task_id: str | int = Field(description="Unique task identifier")
155
+ session_id: str | None = Field(default=None, description="Session ID if applicable")
156
+ duration: float = Field(description="Execution time in seconds")
157
+ cost: float = Field(default=0.0, description="Task cost in USD (not yet provided by API)")
158
+ timestamp: datetime = Field(description="Task completion timestamp")
159
+ steps: int = Field(description="Number of steps executed")
160
+ success: bool = Field(description="Whether task succeeded")
161
+
162
+ @field_serializer("timestamp")
163
+ def serialize_timestamp(self, value: datetime, _info: Any) -> str:
164
+ """Serialize datetime to ISO format string."""
165
+ return value.isoformat()
166
+
167
+
168
+ class TaskResult(BaseModel, Generic[T]):
169
+ """
170
+ Result of task execution.
171
+
172
+ The data field contains the task output, typically as a dictionary.
173
+ Metadata provides execution information like duration and steps.
174
+
175
+ Example:
176
+ >>> result = session.run_task("Compare flight prices from NYC to London")
177
+ >>> print(result.data) # Dict with flight comparison data
178
+ >>> print(f"Duration: {result.metadata.duration}s")
179
+ >>> print(f"Steps: {result.metadata.steps}")
180
+ """
181
+
182
+ data: T = Field(description="Task output data")
183
+ metadata: TaskMetadata = Field(description="Execution metadata")
agi/types/sessions.py ADDED
@@ -0,0 +1,119 @@
1
+ """Session-related type definitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+ from agi.types.shared import EventType, MessageType, SessionStatus
11
+
12
+ # Request Models
13
+
14
+
15
+ class CreateSessionRequest(BaseModel):
16
+ """Request to create a new session."""
17
+
18
+ agent_name: str = Field(default="agi-0", description="Agent model to use")
19
+ webhook_url: str | None = Field(
20
+ default=None, description="Webhook URL for session event notifications"
21
+ )
22
+ goal: str | None = Field(default=None, description="Task goal (optional, can be set later)")
23
+ max_steps: int = Field(default=100, description="Maximum number of agent steps")
24
+ restore_from_environment_id: str | None = Field(
25
+ default=None, description="Environment UUID to restore from"
26
+ )
27
+
28
+
29
+ class SendMessageRequest(BaseModel):
30
+ """Request to send a message to the agent."""
31
+
32
+ message: str = Field(..., description="Message content")
33
+ start_url: str | None = Field(default=None, description="Optional starting URL")
34
+ config_updates: dict[str, Any] | None = Field(
35
+ default=None, description="Optional configuration updates"
36
+ )
37
+
38
+
39
+ class NavigateRequest(BaseModel):
40
+ """Request to navigate browser to a URL."""
41
+
42
+ url: str = Field(..., description="URL to navigate to")
43
+
44
+
45
+ # Response Models
46
+
47
+
48
+ class SessionResponse(BaseModel):
49
+ """Response containing session information."""
50
+
51
+ session_id: str = Field(..., description="Session UUID")
52
+ vnc_url: str | None = Field(None, description="VNC URL for browser access")
53
+ agent_url: str | None = Field(None, description="Agent service URL (desktop mode)")
54
+ agent_name: str = Field(..., description="Agent name")
55
+ status: str = Field(..., description="Session status")
56
+ created_at: datetime = Field(..., description="Session creation timestamp")
57
+ environment_id: str | None = Field(None, description="Environment UUID for restore")
58
+ goal: str | None = Field(None, description="Task goal")
59
+
60
+
61
+ class ExecuteStatusResponse(BaseModel):
62
+ """Response containing task execution status."""
63
+
64
+ status: SessionStatus = Field(..., description="Current execution status")
65
+
66
+
67
+ class MessageResponse(BaseModel):
68
+ """Single message from agent."""
69
+
70
+ id: int = Field(..., description="Message ID")
71
+ type: MessageType = Field(..., description="Message type")
72
+ content: str | dict[str, Any] | list[dict[str, Any]] = Field(..., description="Message content")
73
+ timestamp: str = Field(..., description="ISO timestamp")
74
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
75
+
76
+
77
+ class MessagesResponse(BaseModel):
78
+ """Response containing message stream and status."""
79
+
80
+ messages: list[MessageResponse] = Field(default_factory=list, description="List of messages")
81
+ status: str = Field(..., description="Current execution status")
82
+ has_agent: bool = Field(True, description="Whether agent is connected")
83
+
84
+
85
+ class SSEEvent(BaseModel):
86
+ """Server-Sent Event from real-time stream."""
87
+
88
+ id: str | None = Field(None, description="Event ID")
89
+ event: EventType = Field(..., description="Event type")
90
+ data: dict[str, Any] = Field(..., description="Event data")
91
+
92
+
93
+ class NavigateResponse(BaseModel):
94
+ """Response from navigation request."""
95
+
96
+ current_url: str = Field(..., description="Current URL after navigation")
97
+
98
+
99
+ class ScreenshotResponse(BaseModel):
100
+ """Response containing screenshot data."""
101
+
102
+ screenshot: str = Field(..., description="Base64-encoded JPEG data URL")
103
+ url: str = Field(..., description="Current page URL")
104
+ title: str = Field(..., description="Current page title")
105
+
106
+
107
+ class SuccessResponse(BaseModel):
108
+ """Generic success response."""
109
+
110
+ success: bool = Field(True, description="Operation success")
111
+ message: str = Field(..., description="Success message")
112
+
113
+
114
+ class DeleteResponse(BaseModel):
115
+ """Response from delete operation."""
116
+
117
+ success: bool = Field(True, description="Operation success")
118
+ deleted: bool = Field(True, description="Whether resource was deleted")
119
+ message: str = Field(..., description="Response message")
agi/types/shared.py ADDED
@@ -0,0 +1,26 @@
1
+ """Shared type definitions."""
2
+
3
+ from typing import Literal
4
+
5
+ # Session status types
6
+ SessionStatus = Literal["ready", "running", "waiting_for_input", "paused", "finished", "error"]
7
+
8
+ # Message types
9
+ MessageType = Literal["THOUGHT", "QUESTION", "USER", "DONE", "ERROR", "LOG"]
10
+
11
+ # SSE Event types
12
+ EventType = Literal[
13
+ "step",
14
+ "thought",
15
+ "question",
16
+ "done",
17
+ "error",
18
+ "log",
19
+ "paused",
20
+ "resumed",
21
+ "heartbeat",
22
+ "user", # User message events
23
+ ]
24
+
25
+ # Snapshot mode types
26
+ SnapshotMode = Literal["none", "memory", "filesystem"]
@@ -0,0 +1,363 @@
1
+ Metadata-Version: 2.4
2
+ Name: agi-python
3
+ Version: 0.0.1
4
+ Summary: Official Python SDK for AGI.tech API
5
+ Author-email: AGI Inc <kaushik@theagi.company>
6
+ Maintainer-email: AGI Inc <kaushik@theagi.company>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/agi-inc/agi-python
9
+ Project-URL: Documentation, https://docs.agi.tech
10
+ Project-URL: Repository, https://github.com/agi-inc/agi-python
11
+ Project-URL: Issues, https://github.com/agi-inc/agi-python/issues
12
+ Project-URL: Changelog, https://github.com/agi-inc/agi-python/releases
13
+ Keywords: agi,automation,browser,ai,agent,api,sdk
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Internet :: WWW/HTTP
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: httpx>=0.27.0
27
+ Requires-Dist: pydantic>=2.0.0
28
+ Requires-Dist: typing-extensions>=4.5.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
31
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
33
+ Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
34
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+ Requires-Dist: pre-commit>=3.0.0; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ <div align="center">
40
+
41
+ <img src="https://cdn.prod.website-files.com/67be56277f6d514dcad939e2/67e48dd1cdab04c17a311669_logo-agi-white.png" alt="AGI" width="120"/>
42
+
43
+ <h1>AGI Python SDK</h1>
44
+
45
+ <p>
46
+ <a href="https://www.theagi.company/">Website</a> •
47
+ <a href="https://docs.agi.tech">Documentation</a> •
48
+ <a href="https://platform.agi.tech">Platform</a> •
49
+ <a href="https://theagi.company/blog">Blog</a>
50
+ </p>
51
+
52
+ ---
53
+
54
+ **AI agent that actually works on the web.**
55
+
56
+ <br />
57
+
58
+ </div>
59
+
60
+ ```python
61
+ from agi import AGIClient
62
+
63
+ client = AGIClient()
64
+
65
+ with client.session("agi-0") as session:
66
+ result = session.run_task(
67
+ "Find three nonstop flights from SFO to JFK next month under $450. "
68
+ "Return flight times, airlines, and booking links."
69
+ )
70
+ print(result)
71
+ ```
72
+
73
+ <br />
74
+
75
+ > Powered by [AGI.tech](https://agi.tech) - the world's most capable computer agent. Trusted by [Visa](https://www.theagi.company/blog/agi-inc-visa) for agentic commerce. Evaluated with [REAL Bench](https://www.theagi.company/blog/introducing-real-bench).
76
+
77
+ <br />
78
+
79
+ ## Installation
80
+
81
+ ```bash
82
+ pip install agi-python
83
+ ```
84
+
85
+ Get your API key at [platform.agi.tech](https://platform.agi.tech/api-keys)
86
+
87
+ ```bash
88
+ export AGI_API_KEY="your-api-key"
89
+ ```
90
+
91
+ ## Quick Start
92
+
93
+ ### Simple Task
94
+
95
+ ```python
96
+ from agi import AGIClient
97
+
98
+ client = AGIClient()
99
+
100
+ with client.session("agi-0") as session:
101
+ result = session.run_task("Find the cheapest iPhone 15 on Amazon")
102
+ print(result)
103
+ ```
104
+
105
+ ### Real-Time Event Streaming
106
+
107
+ ```python
108
+ with client.session("agi-0") as session:
109
+ session.send_message("Research the top 5 AI companies in 2025")
110
+
111
+ for event in session.stream_events():
112
+ if event.event == "thought":
113
+ print(f"💭 Agent: {event.data}")
114
+ elif event.event == "done":
115
+ print(f"✅ Result: {event.data}")
116
+ break
117
+ ```
118
+
119
+ ### Session Control
120
+
121
+ ```python
122
+ with client.session("agi-0") as session:
123
+ session.send_message("Long research task...")
124
+
125
+ # Control execution
126
+ session.pause() # Pause the agent
127
+ session.resume() # Resume later
128
+ session.cancel() # Or cancel
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Core Concepts
134
+
135
+ *Understanding the building blocks of agi*
136
+
137
+ ### Sessions: The Container for Tasks
138
+
139
+ Every task runs inside a **session** - an isolated browser environment:
140
+
141
+ ```python
142
+ # Context manager (recommended) - automatic cleanup
143
+ with client.session("agi-0") as session:
144
+ session.run_task("Find flights...")
145
+
146
+ # Manual management - full control
147
+ session = client.sessions.create(agent_name="agi-0")
148
+ client.sessions.send_message(session.session_id, "task")
149
+ client.sessions.delete(session.session_id)
150
+ ```
151
+
152
+ ▸ Use context managers for most tasks. Use manual management when you need fine-grained control.
153
+
154
+ ### Available Agents
155
+
156
+ - **agi-0** - General purpose agent (recommended)
157
+ - **agi-0-fast** - Faster agent for simple tasks
158
+ - **agi-1** - Advanced agent with enhanced capabilities
159
+
160
+ See [docs.agi.tech](https://docs.agi.tech) for the full list.
161
+
162
+ ---
163
+
164
+ ## Features
165
+
166
+ - **Natural Language** - Describe tasks in plain English, no selectors or scraping code
167
+ - **Real-Time Streaming** - Watch agent execution live with Server-Sent Events
168
+ - **Session Control** - Pause, resume, or cancel long-running tasks
169
+ - **Browser Control** - Navigate and screenshot for visual debugging
170
+ - **Type-Safe** - Full type hints with Pydantic validation
171
+ - **Production-Ready** - Built-in retries, automatic cleanup, comprehensive error handling
172
+
173
+ ---
174
+
175
+ ## Common Use Cases
176
+
177
+ ### Price Monitoring
178
+
179
+ Monitor product prices and availability across retailers.
180
+
181
+ ```python
182
+ with client.session("agi-0") as session:
183
+ result = session.run_task(
184
+ "Go to amazon.com and search for 'Sony WH-1000XM5'. "
185
+ "Get the current price, check if it's in stock, and return the product rating. "
186
+ "Return as JSON with fields: price, in_stock, rating, url."
187
+ )
188
+ ```
189
+
190
+ ### Lead Generation
191
+
192
+ Extract structured data from public sources.
193
+
194
+ ```python
195
+ with client.session("agi-0") as session:
196
+ result = session.run_task(
197
+ "Go to ycombinator.com/companies, find companies in the 'AI' category "
198
+ "from the latest batch. For the first 10 companies, get their name, "
199
+ "description, and website. Return as a JSON array."
200
+ )
201
+ ```
202
+
203
+ ### Flight Booking Research
204
+
205
+ ```python
206
+ with client.session("agi-0") as session:
207
+ result = session.run_task(
208
+ "Find three nonstop SFO→JFK flights next month under $450. "
209
+ "Compare prices on Google Flights, Kayak, and Expedia. "
210
+ "Return flight details and booking links."
211
+ )
212
+ ```
213
+
214
+ <details>
215
+ <summary><b>Browser Control</b> – Navigate and take screenshots for visual debugging</summary>
216
+
217
+ <br />
218
+
219
+ ```python
220
+ with client.session("agi-0") as session:
221
+ # Navigate to specific URL
222
+ session.navigate("https://amazon.com")
223
+
224
+ # Get screenshot for debugging
225
+ screenshot = session.screenshot()
226
+ print(screenshot.url) # Current page URL
227
+ print(screenshot.title) # Page title
228
+ # screenshot.screenshot contains base64 JPEG data
229
+ ```
230
+
231
+ </details>
232
+
233
+ <details>
234
+ <summary><b>Session Snapshots</b> – Preserve authentication and browser state</summary>
235
+
236
+ <br />
237
+
238
+ ```python
239
+ # Create session and save environment
240
+ session1 = client.sessions.create(agent_name="agi-0")
241
+ # ... do some work ...
242
+ client.sessions.delete(session1.session_id, save_snapshot_mode="filesystem")
243
+
244
+ # Later, restore from saved environment
245
+ session2 = client.sessions.create(
246
+ agent_name="agi-0",
247
+ restore_from_environment_id=session1.environment_id
248
+ )
249
+ # Authentication state and cookies preserved!
250
+ ```
251
+
252
+ </details>
253
+
254
+ <details>
255
+ <summary><b>Advanced Session Management</b> – Full control over session lifecycle</summary>
256
+
257
+ <br />
258
+
259
+ ```python
260
+ # Create session manually
261
+ session = client.sessions.create(
262
+ agent_name="agi-0-fast",
263
+ webhook_url="https://yourapp.com/webhook",
264
+ max_steps=200
265
+ )
266
+
267
+ # Send message
268
+ client.sessions.send_message(
269
+ session.session_id,
270
+ "Find flights from SFO to JFK under $450"
271
+ )
272
+
273
+ # Check status
274
+ status = client.sessions.get_status(session.session_id)
275
+ print(status.status) # "running", "finished", etc.
276
+
277
+ # List all sessions
278
+ sessions = client.sessions.list()
279
+
280
+ # Delete when done
281
+ client.sessions.delete(session.session_id)
282
+ ```
283
+
284
+ </details>
285
+
286
+ <details>
287
+ <summary><b>Webhooks</b> – Get notified when tasks complete</summary>
288
+
289
+ <br />
290
+
291
+ ```python
292
+ session = client.sessions.create(
293
+ agent_name="agi-0",
294
+ webhook_url="https://yourapp.com/webhook"
295
+ )
296
+
297
+ # Your webhook will receive events:
298
+ # POST https://yourapp.com/webhook
299
+ # {
300
+ # "event": "done",
301
+ # "session_id": "sess_...",
302
+ # "data": {...}
303
+ # }
304
+ ```
305
+
306
+ </details>
307
+
308
+ ---
309
+
310
+ ## Error Handling
311
+
312
+ <details>
313
+ <summary><b>Robust error handling with detailed debugging</b></summary>
314
+
315
+ <br />
316
+
317
+ ```python
318
+ from agi import (
319
+ AGIClient,
320
+ AuthenticationError,
321
+ NotFoundError,
322
+ RateLimitError,
323
+ AgentExecutionError
324
+ )
325
+
326
+ client = AGIClient()
327
+
328
+ try:
329
+ with client.session("agi-0") as session:
330
+ result = session.run_task("Find flights...")
331
+ except AuthenticationError:
332
+ print("Invalid API key")
333
+ except NotFoundError:
334
+ print("Session not found")
335
+ except RateLimitError:
336
+ print("Rate limit exceeded - please retry")
337
+ except AgentExecutionError as e:
338
+ print(f"Task failed: {e}")
339
+ # Debug at VNC URL if available
340
+ except AGIError as e:
341
+ print(f"API error: {e}")
342
+ ```
343
+
344
+ </details>
345
+
346
+ ---
347
+
348
+ ## Documentation & Resources
349
+
350
+ **Learn More**
351
+ - [API Reference](https://docs.agi.tech) – Complete API documentation
352
+ - [Code Examples](./examples) – Working examples for common tasks
353
+ - [GitHub Issues](https://github.com/agi-inc/agi-python/issues) – Report bugs or request features
354
+
355
+ **Get Help**
356
+ - [Platform](https://platform.agi.tech) – Manage API keys and monitor usage
357
+ - [Documentation](https://docs.agi.tech) – Guides and tutorials
358
+
359
+ ---
360
+
361
+ ## License
362
+
363
+ MIT License - see [LICENSE](LICENSE) for details.