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.
Files changed (26) hide show
  1. prefactor_http-0.1.0/.gitignore +71 -0
  2. prefactor_http-0.1.0/PKG-INFO +271 -0
  3. prefactor_http-0.1.0/README.md +260 -0
  4. prefactor_http-0.1.0/pyproject.toml +21 -0
  5. prefactor_http-0.1.0/src/prefactor_http/__init__.py +92 -0
  6. prefactor_http-0.1.0/src/prefactor_http/client.py +275 -0
  7. prefactor_http-0.1.0/src/prefactor_http/config.py +54 -0
  8. prefactor_http-0.1.0/src/prefactor_http/endpoints/__init__.py +11 -0
  9. prefactor_http-0.1.0/src/prefactor_http/endpoints/agent_instance.py +155 -0
  10. prefactor_http-0.1.0/src/prefactor_http/endpoints/agent_span.py +135 -0
  11. prefactor_http-0.1.0/src/prefactor_http/endpoints/bulk.py +115 -0
  12. prefactor_http-0.1.0/src/prefactor_http/exceptions.py +81 -0
  13. prefactor_http-0.1.0/src/prefactor_http/models/__init__.py +41 -0
  14. prefactor_http-0.1.0/src/prefactor_http/models/agent_instance.py +158 -0
  15. prefactor_http-0.1.0/src/prefactor_http/models/agent_span.py +92 -0
  16. prefactor_http-0.1.0/src/prefactor_http/models/base.py +97 -0
  17. prefactor_http-0.1.0/src/prefactor_http/models/bulk.py +75 -0
  18. prefactor_http-0.1.0/src/prefactor_http/models/types.py +6 -0
  19. prefactor_http-0.1.0/src/prefactor_http/retry.py +89 -0
  20. prefactor_http-0.1.0/tests/__init__.py +1 -0
  21. prefactor_http-0.1.0/tests/test_bulk.py +259 -0
  22. prefactor_http-0.1.0/tests/test_client.py +294 -0
  23. prefactor_http-0.1.0/tests/test_config.py +104 -0
  24. prefactor_http-0.1.0/tests/test_endpoints.py +256 -0
  25. prefactor_http-0.1.0/tests/test_models.py +350 -0
  26. 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"]