prefactor-http 0.1.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.
- prefactor_http-0.1.0/.gitignore +71 -0
- prefactor_http-0.1.0/PKG-INFO +271 -0
- prefactor_http-0.1.0/README.md +260 -0
- prefactor_http-0.1.0/pyproject.toml +21 -0
- prefactor_http-0.1.0/src/prefactor_http/__init__.py +92 -0
- prefactor_http-0.1.0/src/prefactor_http/client.py +275 -0
- prefactor_http-0.1.0/src/prefactor_http/config.py +54 -0
- prefactor_http-0.1.0/src/prefactor_http/endpoints/__init__.py +11 -0
- prefactor_http-0.1.0/src/prefactor_http/endpoints/agent_instance.py +155 -0
- prefactor_http-0.1.0/src/prefactor_http/endpoints/agent_span.py +135 -0
- prefactor_http-0.1.0/src/prefactor_http/endpoints/bulk.py +115 -0
- prefactor_http-0.1.0/src/prefactor_http/exceptions.py +81 -0
- prefactor_http-0.1.0/src/prefactor_http/models/__init__.py +41 -0
- prefactor_http-0.1.0/src/prefactor_http/models/agent_instance.py +158 -0
- prefactor_http-0.1.0/src/prefactor_http/models/agent_span.py +92 -0
- prefactor_http-0.1.0/src/prefactor_http/models/base.py +97 -0
- prefactor_http-0.1.0/src/prefactor_http/models/bulk.py +75 -0
- prefactor_http-0.1.0/src/prefactor_http/models/types.py +6 -0
- prefactor_http-0.1.0/src/prefactor_http/retry.py +89 -0
- prefactor_http-0.1.0/tests/__init__.py +1 -0
- prefactor_http-0.1.0/tests/test_bulk.py +259 -0
- prefactor_http-0.1.0/tests/test_client.py +294 -0
- prefactor_http-0.1.0/tests/test_config.py +104 -0
- prefactor_http-0.1.0/tests/test_endpoints.py +256 -0
- prefactor_http-0.1.0/tests/test_models.py +350 -0
- prefactor_http-0.1.0/tests/test_retry.py +102 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# mise
|
|
2
|
+
.mise.local.toml
|
|
3
|
+
.mise.*.local.toml
|
|
4
|
+
|
|
5
|
+
# lefthook
|
|
6
|
+
lefthook-local.yml
|
|
7
|
+
|
|
8
|
+
# direnv
|
|
9
|
+
.direnv
|
|
10
|
+
|
|
11
|
+
# Python
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
*$py.class
|
|
15
|
+
*.so
|
|
16
|
+
.Python
|
|
17
|
+
build/
|
|
18
|
+
develop-eggs/
|
|
19
|
+
dist/
|
|
20
|
+
downloads/
|
|
21
|
+
eggs/
|
|
22
|
+
.eggs/
|
|
23
|
+
lib/
|
|
24
|
+
lib64/
|
|
25
|
+
parts/
|
|
26
|
+
sdist/
|
|
27
|
+
var/
|
|
28
|
+
wheels/
|
|
29
|
+
pip-wheel-metadata/
|
|
30
|
+
share/python-wheels/
|
|
31
|
+
*.egg-info/
|
|
32
|
+
.installed.cfg
|
|
33
|
+
*.egg
|
|
34
|
+
MANIFEST
|
|
35
|
+
|
|
36
|
+
# Virtual environments
|
|
37
|
+
.venv/
|
|
38
|
+
venv/
|
|
39
|
+
ENV/
|
|
40
|
+
env/
|
|
41
|
+
|
|
42
|
+
# uv
|
|
43
|
+
.uv/
|
|
44
|
+
uv.lock
|
|
45
|
+
|
|
46
|
+
# Type checkers
|
|
47
|
+
.mypy_cache/
|
|
48
|
+
.dmypy.json
|
|
49
|
+
dmypy.json
|
|
50
|
+
.pytype/
|
|
51
|
+
.pyre/
|
|
52
|
+
.ty_cache/
|
|
53
|
+
|
|
54
|
+
# Ruff
|
|
55
|
+
.ruff_cache/
|
|
56
|
+
|
|
57
|
+
# IDEs
|
|
58
|
+
.vscode/
|
|
59
|
+
.idea/
|
|
60
|
+
*.swp
|
|
61
|
+
*.swo
|
|
62
|
+
*~
|
|
63
|
+
|
|
64
|
+
# Testing
|
|
65
|
+
.pytest_cache/
|
|
66
|
+
.coverage
|
|
67
|
+
htmlcov/
|
|
68
|
+
|
|
69
|
+
# Env
|
|
70
|
+
.env
|
|
71
|
+
mise.local.toml
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prefactor-http
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: HTTP client library for Prefactor API
|
|
5
|
+
Author-email: Prefactor Pty Ltd <josh@prefactor.tech>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: <4.0.0,>=3.11.0
|
|
8
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
9
|
+
Requires-Dist: pydantic>=2.0.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Prefactor HTTP Client
|
|
13
|
+
|
|
14
|
+
A low-level async HTTP client for the Prefactor API.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Typed Endpoint Clients**: Dedicated clients for agent instances, agent spans, and bulk operations
|
|
19
|
+
- **Automatic Retries**: Exponential backoff with jitter for transient failures
|
|
20
|
+
- **Type Safety**: Full Pydantic models for all request/response data
|
|
21
|
+
- **Clear Error Hierarchy**: Specific exception types for different failure modes
|
|
22
|
+
- **Idempotency**: Built-in support for idempotency keys
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install prefactor-http
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import asyncio
|
|
34
|
+
from prefactor_http import PrefactorHttpClient, HttpClientConfig
|
|
35
|
+
|
|
36
|
+
async def main():
|
|
37
|
+
config = HttpClientConfig(
|
|
38
|
+
api_url="https://api.prefactor.ai",
|
|
39
|
+
api_token="your-api-token",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
async with PrefactorHttpClient(config) as client:
|
|
43
|
+
instance = await client.agent_instances.register(
|
|
44
|
+
agent_id="agent_123",
|
|
45
|
+
agent_version={"name": "My Agent", "external_identifier": "v1.0.0"},
|
|
46
|
+
agent_schema_version={
|
|
47
|
+
"external_identifier": "v1.0.0",
|
|
48
|
+
"span_type_schemas": [
|
|
49
|
+
{
|
|
50
|
+
"name": "agent:llm",
|
|
51
|
+
"title": "LLM Call",
|
|
52
|
+
"description": "A call to a language model",
|
|
53
|
+
"params_schema": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"model": {"type": "string"},
|
|
57
|
+
"prompt": {"type": "string"},
|
|
58
|
+
},
|
|
59
|
+
"required": ["model", "prompt"],
|
|
60
|
+
},
|
|
61
|
+
"result_schema": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {"response": {"type": "string"}},
|
|
64
|
+
},
|
|
65
|
+
"template": "{{model}}: {{prompt}} → {{response}}",
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
print(f"Registered instance: {instance.id}")
|
|
71
|
+
|
|
72
|
+
asyncio.run(main())
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Endpoints
|
|
76
|
+
|
|
77
|
+
### Agent Instances (`client.agent_instances`)
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# Register a new agent instance
|
|
81
|
+
instance = await client.agent_instances.register(
|
|
82
|
+
agent_id="agent_123",
|
|
83
|
+
agent_version={
|
|
84
|
+
"name": "My Agent",
|
|
85
|
+
"external_identifier": "v1.0.0",
|
|
86
|
+
"description": "Optional description",
|
|
87
|
+
},
|
|
88
|
+
agent_schema_version={
|
|
89
|
+
"external_identifier": "schema-v1",
|
|
90
|
+
"span_type_schemas": [
|
|
91
|
+
{
|
|
92
|
+
"name": "agent:llm",
|
|
93
|
+
"title": "LLM Call", # Optional
|
|
94
|
+
"description": "A call to a language model", # Optional
|
|
95
|
+
"params_schema": {"type": "object", "properties": {...}},
|
|
96
|
+
"result_schema": {"type": "object", "properties": {...}}, # Optional
|
|
97
|
+
"template": "{{model}}: {{prompt}} → {{response}}", # Optional
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
# Alternatively, use flat maps for simpler cases:
|
|
101
|
+
# "span_schemas": {"agent:llm": {"type": "object", ...}},
|
|
102
|
+
# "span_result_schemas": {"agent:llm": {"type": "object", ...}},
|
|
103
|
+
},
|
|
104
|
+
id=None, # Optional: pre-assign an ID
|
|
105
|
+
idempotency_key=None, # Optional: idempotency key
|
|
106
|
+
update_current_version=True, # Optional: update the agent's current version
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Start an instance
|
|
110
|
+
instance = await client.agent_instances.start(
|
|
111
|
+
agent_instance_id=instance.id,
|
|
112
|
+
timestamp=None, # Optional: override start time
|
|
113
|
+
idempotency_key=None,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Finish an instance
|
|
117
|
+
instance = await client.agent_instances.finish(
|
|
118
|
+
agent_instance_id=instance.id,
|
|
119
|
+
status=None, # Optional: "complete" | "failed" | "cancelled"
|
|
120
|
+
timestamp=None, # Optional: override finish time
|
|
121
|
+
idempotency_key=None,
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The `AgentInstance` response includes: `id`, `agent_id`, `status`, `started_at`, `finished_at`, `span_counts`, and more.
|
|
126
|
+
|
|
127
|
+
### Agent Spans (`client.agent_spans`)
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# Create a span
|
|
131
|
+
span = await client.agent_spans.create(
|
|
132
|
+
agent_instance_id="instance_123",
|
|
133
|
+
schema_name="agent:llm",
|
|
134
|
+
status="active",
|
|
135
|
+
payload={"model": "gpt-4", "prompt": "Hello"}, # Optional
|
|
136
|
+
result_payload=None, # Optional
|
|
137
|
+
id=None, # Optional: pre-assign an ID
|
|
138
|
+
parent_span_id=None, # Optional: parent for nesting
|
|
139
|
+
started_at=None, # Optional: override start time
|
|
140
|
+
finished_at=None,
|
|
141
|
+
idempotency_key=None,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Finish a span
|
|
145
|
+
span = await client.agent_spans.finish(
|
|
146
|
+
agent_span_id=span.id,
|
|
147
|
+
status=None, # Optional: "complete" | "failed" | "cancelled"
|
|
148
|
+
result_payload=None, # Optional: final result data
|
|
149
|
+
timestamp=None, # Optional: override finish time
|
|
150
|
+
idempotency_key=None,
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The `AgentSpan` response includes: `id`, `agent_instance_id`, `schema_name`, `status`, `payload`, `result_payload`, `parent_span_id`, `started_at`, `finished_at`, and more.
|
|
155
|
+
|
|
156
|
+
### Bulk Operations (`client.bulk`)
|
|
157
|
+
|
|
158
|
+
Execute multiple POST actions in a single HTTP request.
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from prefactor_http import BulkRequest, BulkItem
|
|
162
|
+
|
|
163
|
+
request = BulkRequest(
|
|
164
|
+
items=[
|
|
165
|
+
BulkItem(
|
|
166
|
+
_type="agent_instances/register",
|
|
167
|
+
idempotency_key="register-instance-001",
|
|
168
|
+
agent_id="agent_123",
|
|
169
|
+
agent_version={"name": "My Agent", "external_identifier": "v1.0.0"},
|
|
170
|
+
agent_schema_version={
|
|
171
|
+
"external_identifier": "v1.0.0",
|
|
172
|
+
"span_type_schemas": [
|
|
173
|
+
{
|
|
174
|
+
"name": "agent:llm",
|
|
175
|
+
"title": "LLM Call",
|
|
176
|
+
"params_schema": {
|
|
177
|
+
"type": "object",
|
|
178
|
+
"properties": {
|
|
179
|
+
"model": {"type": "string"},
|
|
180
|
+
"prompt": {"type": "string"},
|
|
181
|
+
},
|
|
182
|
+
"required": ["model", "prompt"],
|
|
183
|
+
},
|
|
184
|
+
"result_schema": {
|
|
185
|
+
"type": "object",
|
|
186
|
+
"properties": {"response": {"type": "string"}},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
),
|
|
192
|
+
BulkItem(
|
|
193
|
+
_type="agent_spans/create",
|
|
194
|
+
idempotency_key="create-span-001",
|
|
195
|
+
agent_instance_id="instance_123",
|
|
196
|
+
schema_name="agent:llm",
|
|
197
|
+
status="active",
|
|
198
|
+
),
|
|
199
|
+
]
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
response = await client.bulk.execute(request)
|
|
203
|
+
|
|
204
|
+
for key, output in response.outputs.items():
|
|
205
|
+
print(f"{key}: {output.status}") # "success" or "error"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Validation rules:**
|
|
209
|
+
- Each item must have a unique `idempotency_key` (8–64 characters)
|
|
210
|
+
- The request must contain at least one item
|
|
211
|
+
|
|
212
|
+
## Error Handling
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from prefactor_http import (
|
|
216
|
+
PrefactorHttpError,
|
|
217
|
+
PrefactorApiError,
|
|
218
|
+
PrefactorAuthError,
|
|
219
|
+
PrefactorNotFoundError,
|
|
220
|
+
PrefactorValidationError,
|
|
221
|
+
PrefactorRetryExhaustedError,
|
|
222
|
+
PrefactorClientError,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
async with PrefactorHttpClient(config) as client:
|
|
227
|
+
instance = await client.agent_instances.register(...)
|
|
228
|
+
except PrefactorValidationError as e:
|
|
229
|
+
print(f"Validation error: {e.errors}")
|
|
230
|
+
except PrefactorAuthError:
|
|
231
|
+
print("Authentication failed - check your API token")
|
|
232
|
+
except PrefactorNotFoundError:
|
|
233
|
+
print("Resource not found")
|
|
234
|
+
except PrefactorRetryExhaustedError as e:
|
|
235
|
+
print(f"Request failed after retries: {e.last_error}")
|
|
236
|
+
except PrefactorApiError as e:
|
|
237
|
+
print(f"API error {e.status_code}: {e.code}")
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Configuration
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
config = HttpClientConfig(
|
|
244
|
+
# Required
|
|
245
|
+
api_url="https://api.prefactor.ai",
|
|
246
|
+
api_token="your-token",
|
|
247
|
+
|
|
248
|
+
# Retry behavior
|
|
249
|
+
max_retries=3,
|
|
250
|
+
initial_retry_delay=1.0,
|
|
251
|
+
max_retry_delay=60.0,
|
|
252
|
+
retry_multiplier=2.0,
|
|
253
|
+
|
|
254
|
+
# Timeouts
|
|
255
|
+
request_timeout=30.0,
|
|
256
|
+
connect_timeout=10.0,
|
|
257
|
+
)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Types
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
from prefactor_http import AgentStatus, FinishStatus
|
|
264
|
+
|
|
265
|
+
# AgentStatus = Literal["pending", "active", "complete", "failed", "cancelled"]
|
|
266
|
+
# FinishStatus = Literal["complete", "failed", "cancelled"]
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## License
|
|
270
|
+
|
|
271
|
+
MIT
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Prefactor HTTP Client
|
|
2
|
+
|
|
3
|
+
A low-level async HTTP client for the Prefactor API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Typed Endpoint Clients**: Dedicated clients for agent instances, agent spans, and bulk operations
|
|
8
|
+
- **Automatic Retries**: Exponential backoff with jitter for transient failures
|
|
9
|
+
- **Type Safety**: Full Pydantic models for all request/response data
|
|
10
|
+
- **Clear Error Hierarchy**: Specific exception types for different failure modes
|
|
11
|
+
- **Idempotency**: Built-in support for idempotency keys
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install prefactor-http
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import asyncio
|
|
23
|
+
from prefactor_http import PrefactorHttpClient, HttpClientConfig
|
|
24
|
+
|
|
25
|
+
async def main():
|
|
26
|
+
config = HttpClientConfig(
|
|
27
|
+
api_url="https://api.prefactor.ai",
|
|
28
|
+
api_token="your-api-token",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
async with PrefactorHttpClient(config) as client:
|
|
32
|
+
instance = await client.agent_instances.register(
|
|
33
|
+
agent_id="agent_123",
|
|
34
|
+
agent_version={"name": "My Agent", "external_identifier": "v1.0.0"},
|
|
35
|
+
agent_schema_version={
|
|
36
|
+
"external_identifier": "v1.0.0",
|
|
37
|
+
"span_type_schemas": [
|
|
38
|
+
{
|
|
39
|
+
"name": "agent:llm",
|
|
40
|
+
"title": "LLM Call",
|
|
41
|
+
"description": "A call to a language model",
|
|
42
|
+
"params_schema": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"model": {"type": "string"},
|
|
46
|
+
"prompt": {"type": "string"},
|
|
47
|
+
},
|
|
48
|
+
"required": ["model", "prompt"],
|
|
49
|
+
},
|
|
50
|
+
"result_schema": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"properties": {"response": {"type": "string"}},
|
|
53
|
+
},
|
|
54
|
+
"template": "{{model}}: {{prompt}} → {{response}}",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
print(f"Registered instance: {instance.id}")
|
|
60
|
+
|
|
61
|
+
asyncio.run(main())
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Endpoints
|
|
65
|
+
|
|
66
|
+
### Agent Instances (`client.agent_instances`)
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# Register a new agent instance
|
|
70
|
+
instance = await client.agent_instances.register(
|
|
71
|
+
agent_id="agent_123",
|
|
72
|
+
agent_version={
|
|
73
|
+
"name": "My Agent",
|
|
74
|
+
"external_identifier": "v1.0.0",
|
|
75
|
+
"description": "Optional description",
|
|
76
|
+
},
|
|
77
|
+
agent_schema_version={
|
|
78
|
+
"external_identifier": "schema-v1",
|
|
79
|
+
"span_type_schemas": [
|
|
80
|
+
{
|
|
81
|
+
"name": "agent:llm",
|
|
82
|
+
"title": "LLM Call", # Optional
|
|
83
|
+
"description": "A call to a language model", # Optional
|
|
84
|
+
"params_schema": {"type": "object", "properties": {...}},
|
|
85
|
+
"result_schema": {"type": "object", "properties": {...}}, # Optional
|
|
86
|
+
"template": "{{model}}: {{prompt}} → {{response}}", # Optional
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
# Alternatively, use flat maps for simpler cases:
|
|
90
|
+
# "span_schemas": {"agent:llm": {"type": "object", ...}},
|
|
91
|
+
# "span_result_schemas": {"agent:llm": {"type": "object", ...}},
|
|
92
|
+
},
|
|
93
|
+
id=None, # Optional: pre-assign an ID
|
|
94
|
+
idempotency_key=None, # Optional: idempotency key
|
|
95
|
+
update_current_version=True, # Optional: update the agent's current version
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Start an instance
|
|
99
|
+
instance = await client.agent_instances.start(
|
|
100
|
+
agent_instance_id=instance.id,
|
|
101
|
+
timestamp=None, # Optional: override start time
|
|
102
|
+
idempotency_key=None,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Finish an instance
|
|
106
|
+
instance = await client.agent_instances.finish(
|
|
107
|
+
agent_instance_id=instance.id,
|
|
108
|
+
status=None, # Optional: "complete" | "failed" | "cancelled"
|
|
109
|
+
timestamp=None, # Optional: override finish time
|
|
110
|
+
idempotency_key=None,
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The `AgentInstance` response includes: `id`, `agent_id`, `status`, `started_at`, `finished_at`, `span_counts`, and more.
|
|
115
|
+
|
|
116
|
+
### Agent Spans (`client.agent_spans`)
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
# Create a span
|
|
120
|
+
span = await client.agent_spans.create(
|
|
121
|
+
agent_instance_id="instance_123",
|
|
122
|
+
schema_name="agent:llm",
|
|
123
|
+
status="active",
|
|
124
|
+
payload={"model": "gpt-4", "prompt": "Hello"}, # Optional
|
|
125
|
+
result_payload=None, # Optional
|
|
126
|
+
id=None, # Optional: pre-assign an ID
|
|
127
|
+
parent_span_id=None, # Optional: parent for nesting
|
|
128
|
+
started_at=None, # Optional: override start time
|
|
129
|
+
finished_at=None,
|
|
130
|
+
idempotency_key=None,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Finish a span
|
|
134
|
+
span = await client.agent_spans.finish(
|
|
135
|
+
agent_span_id=span.id,
|
|
136
|
+
status=None, # Optional: "complete" | "failed" | "cancelled"
|
|
137
|
+
result_payload=None, # Optional: final result data
|
|
138
|
+
timestamp=None, # Optional: override finish time
|
|
139
|
+
idempotency_key=None,
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The `AgentSpan` response includes: `id`, `agent_instance_id`, `schema_name`, `status`, `payload`, `result_payload`, `parent_span_id`, `started_at`, `finished_at`, and more.
|
|
144
|
+
|
|
145
|
+
### Bulk Operations (`client.bulk`)
|
|
146
|
+
|
|
147
|
+
Execute multiple POST actions in a single HTTP request.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from prefactor_http import BulkRequest, BulkItem
|
|
151
|
+
|
|
152
|
+
request = BulkRequest(
|
|
153
|
+
items=[
|
|
154
|
+
BulkItem(
|
|
155
|
+
_type="agent_instances/register",
|
|
156
|
+
idempotency_key="register-instance-001",
|
|
157
|
+
agent_id="agent_123",
|
|
158
|
+
agent_version={"name": "My Agent", "external_identifier": "v1.0.0"},
|
|
159
|
+
agent_schema_version={
|
|
160
|
+
"external_identifier": "v1.0.0",
|
|
161
|
+
"span_type_schemas": [
|
|
162
|
+
{
|
|
163
|
+
"name": "agent:llm",
|
|
164
|
+
"title": "LLM Call",
|
|
165
|
+
"params_schema": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"properties": {
|
|
168
|
+
"model": {"type": "string"},
|
|
169
|
+
"prompt": {"type": "string"},
|
|
170
|
+
},
|
|
171
|
+
"required": ["model", "prompt"],
|
|
172
|
+
},
|
|
173
|
+
"result_schema": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"properties": {"response": {"type": "string"}},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
),
|
|
181
|
+
BulkItem(
|
|
182
|
+
_type="agent_spans/create",
|
|
183
|
+
idempotency_key="create-span-001",
|
|
184
|
+
agent_instance_id="instance_123",
|
|
185
|
+
schema_name="agent:llm",
|
|
186
|
+
status="active",
|
|
187
|
+
),
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
response = await client.bulk.execute(request)
|
|
192
|
+
|
|
193
|
+
for key, output in response.outputs.items():
|
|
194
|
+
print(f"{key}: {output.status}") # "success" or "error"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Validation rules:**
|
|
198
|
+
- Each item must have a unique `idempotency_key` (8–64 characters)
|
|
199
|
+
- The request must contain at least one item
|
|
200
|
+
|
|
201
|
+
## Error Handling
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from prefactor_http import (
|
|
205
|
+
PrefactorHttpError,
|
|
206
|
+
PrefactorApiError,
|
|
207
|
+
PrefactorAuthError,
|
|
208
|
+
PrefactorNotFoundError,
|
|
209
|
+
PrefactorValidationError,
|
|
210
|
+
PrefactorRetryExhaustedError,
|
|
211
|
+
PrefactorClientError,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
async with PrefactorHttpClient(config) as client:
|
|
216
|
+
instance = await client.agent_instances.register(...)
|
|
217
|
+
except PrefactorValidationError as e:
|
|
218
|
+
print(f"Validation error: {e.errors}")
|
|
219
|
+
except PrefactorAuthError:
|
|
220
|
+
print("Authentication failed - check your API token")
|
|
221
|
+
except PrefactorNotFoundError:
|
|
222
|
+
print("Resource not found")
|
|
223
|
+
except PrefactorRetryExhaustedError as e:
|
|
224
|
+
print(f"Request failed after retries: {e.last_error}")
|
|
225
|
+
except PrefactorApiError as e:
|
|
226
|
+
print(f"API error {e.status_code}: {e.code}")
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Configuration
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
config = HttpClientConfig(
|
|
233
|
+
# Required
|
|
234
|
+
api_url="https://api.prefactor.ai",
|
|
235
|
+
api_token="your-token",
|
|
236
|
+
|
|
237
|
+
# Retry behavior
|
|
238
|
+
max_retries=3,
|
|
239
|
+
initial_retry_delay=1.0,
|
|
240
|
+
max_retry_delay=60.0,
|
|
241
|
+
retry_multiplier=2.0,
|
|
242
|
+
|
|
243
|
+
# Timeouts
|
|
244
|
+
request_timeout=30.0,
|
|
245
|
+
connect_timeout=10.0,
|
|
246
|
+
)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Types
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from prefactor_http import AgentStatus, FinishStatus
|
|
253
|
+
|
|
254
|
+
# AgentStatus = Literal["pending", "active", "complete", "failed", "cancelled"]
|
|
255
|
+
# FinishStatus = Literal["complete", "failed", "cancelled"]
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "prefactor-http"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "HTTP client library for Prefactor API"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Prefactor Pty Ltd", email = "josh@prefactor.tech" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.11.0, <4.0.0"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"aiohttp>=3.9.0",
|
|
13
|
+
"pydantic>=2.0.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
build-backend = "hatchling.build"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/prefactor_http"]
|