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/__init__.py +70 -0
- agi/_http.py +135 -0
- agi/_session_context.py +345 -0
- agi/_sse.py +77 -0
- agi/client.py +142 -0
- agi/exceptions.py +43 -0
- agi/py.typed +0 -0
- agi/resources/__init__.py +5 -0
- agi/resources/sessions.py +358 -0
- agi/types/__init__.py +39 -0
- agi/types/results.py +183 -0
- agi/types/sessions.py +119 -0
- agi/types/shared.py +26 -0
- agi_python-0.0.1.dist-info/METADATA +363 -0
- agi_python-0.0.1.dist-info/RECORD +18 -0
- agi_python-0.0.1.dist-info/WHEEL +5 -0
- agi_python-0.0.1.dist-info/licenses/LICENSE +21 -0
- agi_python-0.0.1.dist-info/top_level.txt +1 -0
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.
|