Prefactor Core
High-level Prefactor SDK with async queue-based processing.
Features
- Queue-Based Processing: Operations are queued and processed asynchronously by a worker pool
- Non-Blocking API: Agent execution is never blocked by observability calls
- Automatic Parent Detection: Nested spans automatically detect their parent from the context stack
- Schema Registry: Compose and register span schemas before instance creation
- Configurable Workers: Tune concurrency and retry behavior for the background queue
Installation
pip install prefactor-core
Quick Start
import asyncio
from prefactor_core import PrefactorCoreClient, PrefactorCoreConfig, SchemaRegistry
from prefactor_http import HttpClientConfig
registry = SchemaRegistry()
registry.register_type(
name="agent:llm",
params_schema={
"type": "object",
"properties": {
"model": {"type": "string"},
"prompt": {"type": "string"},
},
"required": ["model", "prompt"],
},
result_schema={
"type": "object",
"properties": {"response": {"type": "string"}},
},
title="LLM Call",
description="A call to a language model",
template="{{model}}: {{prompt}} → {{response}}",
)
async def main():
config = PrefactorCoreConfig(
http_config=HttpClientConfig(
api_url="https://api.prefactor.ai",
api_token="your-token",
),
schema_registry=registry,
)
async with PrefactorCoreClient(config) as client:
instance = await client.create_agent_instance(
agent_id="my-agent",
agent_version={"name": "My Agent", "external_identifier": "v1.0.0"},
)
await instance.start()
async with instance.span("agent:llm") as span:
await span.start({"model": "gpt-4", "prompt": "Hello"})
result = await call_llm()
await span.complete({"response": result})
await instance.finish()
asyncio.run(main())
create_agent_instance() supports two auth modes:
- Account-scoped token: pass
agent_idand usuallyenvironment_id. - Deployment-scoped token: omit
agent_idandenvironment_id; the API derives both from the token.
API Reference
PrefactorCoreClient
The main entry point. Use as an async context manager or call initialize() / close() manually.
client = PrefactorCoreClient(config)
await client.initialize()
# ... use client ...
await client.close()
create_agent_instance
handle = await client.create_agent_instance(
agent_version={"name": "My Agent", "external_identifier": "v1.0.0"},
agent_schema_version=None, # Optional: auto-generated if schema_registry is configured
agent_id="my-agent", # Optional for deployment-scoped tokens
external_schema_version_id=None, # Optional: reference an existing schema version
) -> AgentInstanceHandle
span (context manager)
async with client.span(
instance_id="instance_123",
schema_name="agent:llm",
parent_span_id=None, # Optional: auto-detected from context stack if omitted
payload=None, # Optional: used as params if span.start() is never called explicitly
) as span:
await span.start({"model": "gpt-4", "prompt": "Hello"})
result = await call_llm()
await span.complete({"response": result})
AgentInstanceHandle
Returned by create_agent_instance. Manages the lifecycle of a single agent instance.
handle.id # -> str
await handle.start()
await handle.finish()
async with handle.span("agent:llm") as span:
...
SpanContext
The object yielded by span context managers. Spans follow a three-phase lifecycle:
- Enter context — span is prepared locally, no HTTP call yet.
await span.start(payload)— POSTs the span to the API asactivewith the given params payload.await span.complete(result)/span.fail(result)/span.cancel()— finishes the span with a terminal status.
If start() or a finish method is not called explicitly, the context manager handles them automatically on exit.
span.id # -> str (API-generated after start())
await span.start(payload: dict) # POST span as active with params payload
await span.complete(result: dict) # finish with status "complete"
await span.fail(result: dict) # finish with status "failed"
await span.cancel() # finish with status "cancelled"
span.set_result(data: dict) # accumulate result data for auto-finish
await span.finish() # finish with current status (default: "complete")
Status note: cancel() can be called before or after start(). If called before start(), the span is posted as pending and immediately cancelled — the only valid pre-active cancellation path the API supports.
Full lifecycle example
async with instance.span("agent:llm") as span:
await span.start({"model": "gpt-4", "prompt": "Hello"})
try:
result = await call_llm()
await span.complete({"response": result})
except Exception as exc:
await span.fail({"error": str(exc)})
# Cancel before starting (e.g. a conditional step that is skipped):
async with instance.span("agent:retrieval") as span:
if not needed:
await span.cancel()
else:
await span.start({"query": "..."})
docs = await retrieve()
await span.complete({"documents": docs, "count": len(docs)})
Configuration
from prefactor_core import PrefactorCoreConfig, QueueConfig
from prefactor_http import HttpClientConfig
config = PrefactorCoreConfig(
http_config=HttpClientConfig(
api_url="https://api.prefactor.ai",
api_token="your-token",
),
queue_config=QueueConfig(
num_workers=3, # Number of background workers
max_retries=3, # Retries per operation
retry_delay_base=1.0, # Base delay (seconds) for exponential backoff
),
schema_registry=None, # Optional: SchemaRegistry instance
)
Schema Registry
Use SchemaRegistry to compose span schemas from multiple sources and auto-generate the agent_schema_version passed to create_agent_instance.
from prefactor_core import SchemaRegistry
registry = SchemaRegistry()
registry.register_type(
name="agent:llm",
params_schema={
"type": "object",
"properties": {
"model": {"type": "string"},
"prompt": {"type": "string"},
},
"required": ["model", "prompt"],
},
result_schema={
"type": "object",
"properties": {"response": {"type": "string"}},
},
title="LLM Call",
description="A call to a language model",
template="{{model}}: {{prompt}} → {{response}}",
)
registry.register_type(
name="agent:tool",
params_schema={"type": "object", "properties": {...}},
result_schema={"type": "object", "properties": {...}},
title="Tool Call",
)
config = PrefactorCoreConfig(
http_config=...,
schema_registry=registry,
)
async with PrefactorCoreClient(config) as client:
# agent_schema_version is generated automatically from the registry
instance = await client.create_agent_instance(
agent_version={"name": "My Agent", "external_identifier": "v1.0.0"},
agent_id="my-agent", # Optional for deployment-scoped tokens
)
Error Handling
from prefactor_core import (
PrefactorCoreError,
ClientNotInitializedError,
ClientAlreadyInitializedError,
OperationError,
InstanceNotFoundError,
SpanNotFoundError,
)
Architecture
The client uses a three-layer design:
- Queue infrastructure:
InMemoryQueue+TaskExecutorworker pool process operations in the background - Managers:
AgentInstanceManagerandSpanManagertranslate high-level calls intoOperationobjects and route them to the HTTP client - Client API:
PrefactorCoreClientexposes the user-facing interface and wires the layers together
All observability operations are enqueued and executed asynchronously — the calling code is never blocked waiting for API responses.
License
MIT
Metadata
Release files for prefactor-core 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| prefactor_core-0.3.0.tar.gz | 37.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| prefactor_core-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.6 kB
Release files / prefactor_core-0.3.0.tar.gz
| Download URL | prefactor_core-0.3.0.tar.gz |
|---|---|
| Size | 37.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9188e4d7531851759e734944b4901eb0f4a144740ace82970df876413dd0d3f3
|
|
BLAKE2b-256 checksum How to use checksums |
45db2d46db3d531221e3c262cdeb7b98e94478c09bc8b34262c26f30c2df7e5c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / prefactor_core-0.3.0-py3-none-any.whl
| Download URL | prefactor_core-0.3.0-py3-none-any.whl |
|---|---|
| Size | 35.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
65a81a54dc9472411493b2d4bd96784d684b4d6dbf5a0607c1bfd04f4a49d7fa
|
|
BLAKE2b-256 checksum How to use checksums |
720c11e8604881d1c568b2653e026593dfbe2033bde1077890374f5343c1f9bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|