lime-agents-sdk 0.2.0__tar.gz
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.
- lime_agents_sdk-0.2.0/.github/workflows/ci.yml +64 -0
- lime_agents_sdk-0.2.0/.gitignore +38 -0
- lime_agents_sdk-0.2.0/LICENSE +21 -0
- lime_agents_sdk-0.2.0/PKG-INFO +434 -0
- lime_agents_sdk-0.2.0/README.md +415 -0
- lime_agents_sdk-0.2.0/pyproject.toml +59 -0
- lime_agents_sdk-0.2.0/src/lime_agents/__init__.py +25 -0
- lime_agents_sdk-0.2.0/src/lime_agents/_agent.py +96 -0
- lime_agents_sdk-0.2.0/src/lime_agents/_client.py +151 -0
- lime_agents_sdk-0.2.0/src/lime_agents/_errors.py +52 -0
- lime_agents_sdk-0.2.0/src/lime_agents/_pow.py +30 -0
- lime_agents_sdk-0.2.0/src/lime_agents/_types.py +53 -0
- lime_agents_sdk-0.2.0/src/lime_agents/py.typed +0 -0
- lime_agents_sdk-0.2.0/tests/__init__.py +0 -0
- lime_agents_sdk-0.2.0/tests/conftest.py +33 -0
- lime_agents_sdk-0.2.0/tests/integration/__init__.py +1 -0
- lime_agents_sdk-0.2.0/tests/integration/bootstrap.py +149 -0
- lime_agents_sdk-0.2.0/tests/integration/test_full_cycle.py +133 -0
- lime_agents_sdk-0.2.0/tests/test_agent.py +281 -0
- lime_agents_sdk-0.2.0/tests/test_client.py +236 -0
- lime_agents_sdk-0.2.0/tests/test_pow.py +33 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main, master]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
quality:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install package (dev)
|
|
27
|
+
run: pip install -e ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Ruff
|
|
30
|
+
run: ruff check src tests
|
|
31
|
+
|
|
32
|
+
- name: Mypy
|
|
33
|
+
if: matrix.python-version == '3.12'
|
|
34
|
+
run: mypy src/lime_agents
|
|
35
|
+
|
|
36
|
+
- name: Pytest (100% coverage)
|
|
37
|
+
run: pytest --cov=lime_agents --cov-fail-under=100
|
|
38
|
+
|
|
39
|
+
publish:
|
|
40
|
+
needs: quality
|
|
41
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
permissions:
|
|
44
|
+
id-token: write
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
- name: Set up Python
|
|
50
|
+
uses: actions/setup-python@v5
|
|
51
|
+
with:
|
|
52
|
+
python-version: "3.12"
|
|
53
|
+
|
|
54
|
+
- name: Install build tools
|
|
55
|
+
run: pip install build hatchling
|
|
56
|
+
|
|
57
|
+
- name: Build package
|
|
58
|
+
run: python -m build
|
|
59
|
+
|
|
60
|
+
- name: Publish to PyPI
|
|
61
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
62
|
+
with:
|
|
63
|
+
attestations: false
|
|
64
|
+
skip-existing: true
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Virtual environments
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
env/
|
|
11
|
+
|
|
12
|
+
# Integration test token cache (never commit)
|
|
13
|
+
tests/integration/.tokens.env
|
|
14
|
+
|
|
15
|
+
# Test / type / lint caches
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
coverage.xml
|
|
20
|
+
.mypy_cache/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
.hypothesis/
|
|
23
|
+
|
|
24
|
+
# Build artifacts
|
|
25
|
+
dist/
|
|
26
|
+
build/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
*.egg
|
|
29
|
+
|
|
30
|
+
# IDE
|
|
31
|
+
.idea/
|
|
32
|
+
.vscode/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
|
|
36
|
+
# OS
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LIME
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lime-agents-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for LIME 2.0 agent authentication. Zero-config, Proof-of-Work auto-solve, async-first.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Mawyxx/lime-agents-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/Mawyxx/lime-agents-sdk
|
|
7
|
+
Project-URL: Documentation, https://lime.pics/docs
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: httpx<1,>=0.27.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# LIME Agents SDK
|
|
21
|
+
|
|
22
|
+
[](https://pypi.org/project/lime-agents-sdk/)
|
|
23
|
+
[](https://pypi.org/project/lime-agents-sdk/)
|
|
24
|
+
[](LICENSE)
|
|
25
|
+
[](https://github.com/Mawyxx/lime-agents-sdk/actions/workflows/ci.yml)
|
|
26
|
+
|
|
27
|
+
Official Python SDK for [LIME](https://lime.pics) agent workers. Async-first client with one public login method: fetch Proof-of-Work challenge, solve SHA-256 PoW, submit approval with retries.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install lime-agents-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Install the latest commit from GitHub:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install git+https://github.com/Mawyxx/lime-agents-sdk.git
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Requirements:** Python 3.10+
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
Examples use readable names (`Lime`, `login`) on top of the shipped API (`LimeAgent`, `login`). See [API reference](#api-reference) for exact types and parameters.
|
|
46
|
+
|
|
47
|
+
### Minimal
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from lime_agents import LimeAgent as _LimeAgent
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Lime(_LimeAgent):
|
|
54
|
+
"""aiogram-style client: token first, login() entrypoint."""
|
|
55
|
+
|
|
56
|
+
def __init__(self, token: str):
|
|
57
|
+
super().__init__(agent_token=token)
|
|
58
|
+
|
|
59
|
+
async def login(self, request_id: str):
|
|
60
|
+
return await self.login(request_id)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
AGENT_TOKEN = "at_..." # LIME Owner Portal → agent token (copy once)
|
|
64
|
+
|
|
65
|
+
async def login_to_site(request_id: str) -> str:
|
|
66
|
+
"""Agent confirms sign-in to a site. Returns status."""
|
|
67
|
+
lime = Lime(AGENT_TOKEN)
|
|
68
|
+
try:
|
|
69
|
+
result = await lime.login(request_id)
|
|
70
|
+
return result.status # "DELIVERED"
|
|
71
|
+
finally:
|
|
72
|
+
await lime.aclose()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Production
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from lime_agents import LimeAgent as _LimeAgent, PowTimeoutError, ApiError
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Lime(_LimeAgent):
|
|
82
|
+
def __init__(self, token: str):
|
|
83
|
+
super().__init__(agent_token=token)
|
|
84
|
+
|
|
85
|
+
async def login(self, request_id: str):
|
|
86
|
+
return await self.login(request_id)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
AGENT_TOKEN = "at_..."
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Agent:
|
|
93
|
+
"""Autonomous worker that signs in to sites when asked."""
|
|
94
|
+
|
|
95
|
+
def __init__(self):
|
|
96
|
+
self.lime = Lime(AGENT_TOKEN)
|
|
97
|
+
|
|
98
|
+
async def on_login_required(self, request_id: str) -> str | None:
|
|
99
|
+
"""Site requires login — agent confirms."""
|
|
100
|
+
try:
|
|
101
|
+
result = await self.lime.login(request_id)
|
|
102
|
+
return result.status
|
|
103
|
+
except PowTimeoutError:
|
|
104
|
+
# Proof-of-Work exceeded pow_timeout (default 10s) — retry once
|
|
105
|
+
try:
|
|
106
|
+
result = await self.lime.login(request_id)
|
|
107
|
+
return result.status
|
|
108
|
+
except PowTimeoutError:
|
|
109
|
+
return None
|
|
110
|
+
except ApiError as exc:
|
|
111
|
+
print(f"[{exc.code}] {exc.message}")
|
|
112
|
+
return None
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Authentication
|
|
116
|
+
|
|
117
|
+
The SDK authenticates agent HTTP calls with the `X-Agent-Token` header.
|
|
118
|
+
|
|
119
|
+
**Resolution order:**
|
|
120
|
+
|
|
121
|
+
1. Constructor argument `agent_token="at_..."`
|
|
122
|
+
2. Environment variable `LIME_AGENT_TOKEN`
|
|
123
|
+
|
|
124
|
+
If neither is set (or the value is empty after trimming), construction raises `AuthenticationError`:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from lime_agents import LimeAgent, AuthenticationError
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
agent = LimeAgent()
|
|
131
|
+
except AuthenticationError as exc:
|
|
132
|
+
print(exc.message)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Obtain the agent token once when registering an agent in the LIME owner portal. Store it as a server-side secret in your worker environment.
|
|
136
|
+
|
|
137
|
+
## Integration pattern: Headless agent
|
|
138
|
+
|
|
139
|
+
Typical embedding: hold one `LimeAgent` per worker process and call `login()` when a login request arrives.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from lime_agents import LimeAgent
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class TradingAgent:
|
|
146
|
+
def __init__(self, token: str):
|
|
147
|
+
self.lime = LimeAgent(agent_token=token)
|
|
148
|
+
|
|
149
|
+
async def on_login_required(self, request_id: str) -> str:
|
|
150
|
+
result = await self.lime.login(request_id)
|
|
151
|
+
return result.status
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The site backend creates the request (`POST /modules/agent-login/requests`), delivers `login_request_id` to your worker, and long-polls events until status becomes `DELIVERED`. Your worker only runs the login step above.
|
|
155
|
+
|
|
156
|
+
## Production deployment
|
|
157
|
+
|
|
158
|
+
For agent workers handling many login jobs, create **one** `LimeAgent` per worker process at startup. Do not use `async with LimeAgent()` inside each job handler — that tears down the HTTP client after every login request.
|
|
159
|
+
|
|
160
|
+
- Instantiate `LimeAgent` once when the worker starts.
|
|
161
|
+
- Reuse the same instance for all `login()` and `get_profile()` calls.
|
|
162
|
+
- Optionally pass a shared `httpx.AsyncClient` via `http_client` for connection pooling.
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from lime_agents import LimeAgent
|
|
166
|
+
|
|
167
|
+
# Created once at worker startup
|
|
168
|
+
agent = LimeAgent()
|
|
169
|
+
|
|
170
|
+
# Reused in every task
|
|
171
|
+
async def handle_login(request_id: str) -> str:
|
|
172
|
+
result = await agent.login(request_id)
|
|
173
|
+
return result.status
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Call `agent.aclose()` only on worker shutdown (or close your injected `http_client` yourself).
|
|
177
|
+
|
|
178
|
+
## API reference
|
|
179
|
+
|
|
180
|
+
### `LimeAgent`
|
|
181
|
+
|
|
182
|
+
Async client for agent-runtime operations (login confirmation, read profile).
|
|
183
|
+
|
|
184
|
+
#### Constructor
|
|
185
|
+
|
|
186
|
+
All arguments are keyword-only.
|
|
187
|
+
|
|
188
|
+
| Parameter | Type | Default | Description |
|
|
189
|
+
|-----------|------|---------|-------------|
|
|
190
|
+
| `agent_token` | `str \| None` | `None` | Agent secret. Falls back to `LIME_AGENT_TOKEN`. |
|
|
191
|
+
| `base_url` | `str \| None` | `None` | API root including `/api/v1`. Falls back to `LIME_API_BASE`, then `https://lime.pics/api/v1`. |
|
|
192
|
+
| `timeout` | `float` | `30.0` | Per-request HTTP timeout in seconds (httpx). |
|
|
193
|
+
| `max_retries` | `int` | `3` | Maximum retries on transient network errors and HTTP 408/429/5xx. |
|
|
194
|
+
| `pow_timeout` | `float` | `10.0` | Wall-clock budget in seconds for the PoW solver loop. |
|
|
195
|
+
| `http_client` | `httpx.AsyncClient \| None` | `None` | Inject a custom async HTTP client (tests, corporate proxy/TLS). When omitted, the SDK creates and owns a client. |
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
agent = LimeAgent(
|
|
199
|
+
agent_token="at_live_...",
|
|
200
|
+
base_url="https://lime.pics/api/v1",
|
|
201
|
+
timeout=60.0,
|
|
202
|
+
max_retries=5,
|
|
203
|
+
pow_timeout=15.0,
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Context manager:** `async with LimeAgent() as agent:` calls `aclose()` on exit. Call `await agent.aclose()` manually when not using a context manager.
|
|
208
|
+
|
|
209
|
+
#### `async login(request_id: str) -> ApprovalResult`
|
|
210
|
+
|
|
211
|
+
Confirms a site login request on behalf of the agent.
|
|
212
|
+
|
|
213
|
+
**Internal steps:**
|
|
214
|
+
|
|
215
|
+
1. `GET /auth/requests/{request_id}` (public, no auth) — read `pow_challenge`, `pow_difficulty`
|
|
216
|
+
2. Solve PoW: find `nonce` such that `int(SHA256(challenge + nonce), 16) < 2**(256 - difficulty)`
|
|
217
|
+
3. `POST /modules/agent-login/requests/{request_id}/approve` with `X-Agent-Token` and body `{"pow_nonce": "<nonce>"}`
|
|
218
|
+
|
|
219
|
+
**Parameters:**
|
|
220
|
+
|
|
221
|
+
| Name | Type | Description |
|
|
222
|
+
|------|------|-------------|
|
|
223
|
+
| `request_id` | `str` | Login request ID from the site backend (`login_request_id` from create). |
|
|
224
|
+
|
|
225
|
+
**Returns:** `ApprovalResult` with FSM status (typically `DELIVERED` after successful login confirmation).
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from lime_agents import LimeAgent, PowTimeoutError, ApiError
|
|
229
|
+
|
|
230
|
+
async with LimeAgent() as agent:
|
|
231
|
+
try:
|
|
232
|
+
result = await agent.login("550e8400-e29b-41d4-a716-446655440000")
|
|
233
|
+
print(result.status, result.approved_agent_id)
|
|
234
|
+
except PowTimeoutError:
|
|
235
|
+
print("PoW not solved in time; increase pow_timeout or retry")
|
|
236
|
+
except ApiError as exc:
|
|
237
|
+
print(exc.code, exc.http_status, exc.message)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### `async get_profile() -> AgentProfile`
|
|
241
|
+
|
|
242
|
+
Returns the authenticated agent's Core profile.
|
|
243
|
+
|
|
244
|
+
**HTTP:** `GET /core/agents/me/profile` with `X-Agent-Token`.
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
async with LimeAgent() as agent:
|
|
248
|
+
profile = await agent.get_profile()
|
|
249
|
+
print(profile.agent_id)
|
|
250
|
+
print(profile.owner_kyc_level)
|
|
251
|
+
print(profile.agent_reputation)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Types
|
|
255
|
+
|
|
256
|
+
### `ApprovalResult`
|
|
257
|
+
|
|
258
|
+
Frozen dataclass returned by `login()`.
|
|
259
|
+
|
|
260
|
+
| Field | Type | Description |
|
|
261
|
+
|-------|------|-------------|
|
|
262
|
+
| `request_id` | `str` | Login request ID |
|
|
263
|
+
| `site_id` | `str` | Site that created the request |
|
|
264
|
+
| `status` | `str` | FSM value, e.g. `APPROVED`, `DELIVERED` |
|
|
265
|
+
| `expires_at` | `datetime` | Request expiry (timezone-aware when API sends offset) |
|
|
266
|
+
| `approved_agent_id` | `str \| None` | Agent that approved the request |
|
|
267
|
+
|
|
268
|
+
### `AgentProfile`
|
|
269
|
+
|
|
270
|
+
Frozen dataclass returned by `get_profile()`. Matches `GET /core/agents/me/profile` response fields.
|
|
271
|
+
|
|
272
|
+
| Field | Type | Description |
|
|
273
|
+
|-------|------|-------------|
|
|
274
|
+
| `agent_id` | `str` | Agent identifier |
|
|
275
|
+
| `owner_id` | `str` | Owning LIME user |
|
|
276
|
+
| `display_name` | `str \| None` | Public display name |
|
|
277
|
+
| `avatar_url` | `str \| None` | Avatar URL |
|
|
278
|
+
| `description` | `str \| None` | Public description |
|
|
279
|
+
| `owner_kyc_level` | `int \| None` | Owner KYC level synced from Foundation |
|
|
280
|
+
| `agent_reputation` | `int \| None` | Reputation score |
|
|
281
|
+
|
|
282
|
+
## Error handling
|
|
283
|
+
|
|
284
|
+
All SDK exceptions inherit from `LimeError`. Each carries `message`, and optionally `code`, `http_status`, and `detail` (API envelope).
|
|
285
|
+
|
|
286
|
+
| Exception | When |
|
|
287
|
+
|-----------|------|
|
|
288
|
+
| `LimeError` | Base class; transport failures after retries, malformed JSON |
|
|
289
|
+
| `AuthenticationError` | Missing/empty token at construct; HTTP 401; `MISSING_AGENT_TOKEN`, `INVALID_AGENT_TOKEN` |
|
|
290
|
+
| `PowTimeoutError` | PoW solver exceeded `pow_timeout` |
|
|
291
|
+
| `RateLimitError` | HTTP 429 / `RATE_LIMIT_EXCEEDED` |
|
|
292
|
+
| `ApiError` | Other API errors (`ok: false` envelope) |
|
|
293
|
+
|
|
294
|
+
`ApiError` attributes: `code`, `message`, `http_status`, `detail`.
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
import asyncio
|
|
298
|
+
|
|
299
|
+
from lime_agents import (
|
|
300
|
+
LimeAgent,
|
|
301
|
+
LimeError,
|
|
302
|
+
AuthenticationError,
|
|
303
|
+
PowTimeoutError,
|
|
304
|
+
RateLimitError,
|
|
305
|
+
ApiError,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
async def run() -> None:
|
|
309
|
+
try:
|
|
310
|
+
async with LimeAgent() as agent:
|
|
311
|
+
await agent.login("lr_abc123")
|
|
312
|
+
except AuthenticationError as exc:
|
|
313
|
+
print("auth:", exc.message)
|
|
314
|
+
except PowTimeoutError as exc:
|
|
315
|
+
print("pow:", exc.message)
|
|
316
|
+
except RateLimitError as exc:
|
|
317
|
+
print("rate limit:", exc.http_status)
|
|
318
|
+
except ApiError as exc:
|
|
319
|
+
print(f"api [{exc.http_status}] {exc.code}: {exc.message}")
|
|
320
|
+
except LimeError as exc:
|
|
321
|
+
print("sdk:", exc.message)
|
|
322
|
+
|
|
323
|
+
asyncio.run(run())
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Non-retried HTTP statuses:** 400, 401, 403, 404, 409 (e.g. `INVALID_POW`, `SITE_LOGIN_CONFLICT`).
|
|
327
|
+
|
|
328
|
+
## Configuration
|
|
329
|
+
|
|
330
|
+
### Environment variables
|
|
331
|
+
|
|
332
|
+
| Variable | Required | Description |
|
|
333
|
+
|----------|----------|-------------|
|
|
334
|
+
| `LIME_AGENT_TOKEN` | Yes (unless `agent_token=` passed) | Agent secret (`at_...`) |
|
|
335
|
+
| `LIME_API_BASE` | No | API root, e.g. `https://lime.pics/api/v1` |
|
|
336
|
+
|
|
337
|
+
### Constructor tuning
|
|
338
|
+
|
|
339
|
+
| Use case | Suggestion |
|
|
340
|
+
|----------|------------|
|
|
341
|
+
| Slow network | Increase `timeout` (e.g. `60.0`) |
|
|
342
|
+
| Flaky upstream | Increase `max_retries` (e.g. `5`) |
|
|
343
|
+
| High PoW difficulty / slow CPU | Increase `pow_timeout` (e.g. `30.0`) |
|
|
344
|
+
| Staging / self-hosted API | Set `base_url` or `LIME_API_BASE` |
|
|
345
|
+
|
|
346
|
+
### Logging
|
|
347
|
+
|
|
348
|
+
HTTP and retry events are logged under the **`lime`** logger (not `lime_agents`):
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
import logging
|
|
352
|
+
|
|
353
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
354
|
+
logging.getLogger("lime").setLevel(logging.DEBUG)
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
At `DEBUG`, the client logs request method and URL. Tokens, `pow_challenge`, and `pow_nonce` are never logged.
|
|
358
|
+
|
|
359
|
+
## Advanced usage
|
|
360
|
+
|
|
361
|
+
### Custom `httpx.AsyncClient`
|
|
362
|
+
|
|
363
|
+
Inject a client for custom TLS, proxies, or tests. **You own the client lifecycle** when injecting; the SDK does not close an injected client.
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
import httpx
|
|
367
|
+
from lime_agents import LimeAgent
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
async def login_with_proxy() -> None:
|
|
371
|
+
client = httpx.AsyncClient(
|
|
372
|
+
timeout=60.0,
|
|
373
|
+
verify="/path/to/corporate-ca.pem",
|
|
374
|
+
proxy="http://proxy.corp.example:8080",
|
|
375
|
+
)
|
|
376
|
+
agent = LimeAgent(agent_token="at_...", http_client=client)
|
|
377
|
+
try:
|
|
378
|
+
await agent.login("lr_abc123")
|
|
379
|
+
finally:
|
|
380
|
+
await client.aclose()
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Retries and timeouts
|
|
384
|
+
|
|
385
|
+
Retries use exponential backoff with jitter on connection errors, timeouts, and HTTP 408, 429, 500, 502, 503, 504. Each retry attempt is bounded by `max_retries` (default 3).
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
agent = LimeAgent(
|
|
389
|
+
agent_token="at_...",
|
|
390
|
+
max_retries=5,
|
|
391
|
+
timeout=45.0,
|
|
392
|
+
pow_timeout=20.0,
|
|
393
|
+
)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### PoW debugging
|
|
397
|
+
|
|
398
|
+
PoW runs in a thread pool (`asyncio.to_thread`) so the event loop stays responsive. To observe HTTP flow (not nonce values):
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
import logging
|
|
402
|
+
|
|
403
|
+
logging.getLogger("lime").setLevel(logging.DEBUG)
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
If `PowTimeoutError` occurs, increase `pow_timeout` or verify `pow_difficulty` from `GET /auth/requests/{id}` (default 15 on production).
|
|
407
|
+
|
|
408
|
+
## Development
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
pip install -e ".[dev]"
|
|
412
|
+
ruff check src tests
|
|
413
|
+
mypy src/lime_agents
|
|
414
|
+
pytest --cov=lime_agents --cov-fail-under=100
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Live integration
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
pip install lime-agents-sdk lime-sites-sdk
|
|
421
|
+
LIME_INTEGRATION=1 pytest tests/integration -v
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Full cycle (both SDKs):** see [lime-sait-sdk `tests/integration/test_full_cycle_both_sdks.py`](https://github.com/Mawyxx/lime-sait-sdk/blob/main/tests/integration/test_full_cycle_both_sdks.py) — site `LimeSite` + agent `LimeAgent` against `https://lime.pics/api/v1`. From the LIME monorepo, run on the production VPS (SSE-safe): `python scripts/_run_both_sdks_integration_remote.py`.
|
|
425
|
+
|
|
426
|
+
## Links
|
|
427
|
+
|
|
428
|
+
- **SDK repository:** [github.com/Mawyxx/lime-agents-sdk](https://github.com/Mawyxx/lime-agents-sdk)
|
|
429
|
+
- **LIME API docs:** [lime.pics/docs](https://lime.pics/docs)
|
|
430
|
+
- **LIME platform:** [github.com/Mawyxx/Lime](https://github.com/Mawyxx/Lime)
|
|
431
|
+
|
|
432
|
+
## License
|
|
433
|
+
|
|
434
|
+
MIT — see [LICENSE](LICENSE).
|