soev_agents.entrypoints.http
Serve agents over HTTP: the threads API and the OpenAI Responses API, in one FastAPI app.
create_app returns a FastAPI app that runs the agents on the server and keeps each thread in a store. The threads
API opens, continues, reads, follows, forks, cancels and deletes threads. The Responses API serves the same threads
to clients of the OpenAI SDK. The two HTTP APIs are public as specifications: the guide and the OpenAPI document
are in the docs/ folder of this package.
The framework has no access-control model. The server gives its own rules through hooks: who calls, whether a call is allowed, and which resources each agent gets for an HTTP request.
The app
create_app serves agents over a store. A server adds its own routes and middleware to the app. A
Mount names the routes of the threads API that a deployment serves.
create_app
functioncreate_app(store, agents, *, version, hooks=None, resources=no_resources, models=(), default_model=None, shutdown_grace=30.0, lease_ttl=30.0, keepalive=15.0, lifespan=None, observer=None, mount=Mount(thread_parameter='thread_id', capabilities='/v1/capabilities', health='/healthz'), list_threads=None, list_tools=None, list_models=None, complete_task=None, features=(), responses=False) -> FastAPIMake the FastAPI app that serves agents over store.
The app serves the threads API at the routes of mount. With the default mount, the threads API is under
/threads, with GET /v1/capabilities and GET /healthz. The hooks apply to the routes of the threads API,
except the health route.
With responses, the app also serves the OpenAI Responses API under /v1/responses, and GET /v1/models. The
Responses API is experimental. It calls no hook, so it does not apply the server's access rules. Use it only for
a server without access rules. resources applies to both APIs.
The four functions list_threads, list_tools, list_models and complete_task serve the optional routes of
the public profile (Mount.public()). A route that mount names, but whose function is None, responds with
501. When mount.models is /v1/models, the threads API serves that route, also with responses.
The app maps the runtime's refusals to status codes, as DefaultHooks.error does. An input that the agent
refuses, or a log position outside the thread, gets 422. A fresh input while a turn has not ended, a cancel
with nothing to cancel, a thread id in use, or a thread that another process runs, gets 409.
At shutdown, the app waits for the running turns, then closes the observer, then leaves lifespan. So the
server's clients stay open while the turns finish.
Example
from soev_agents.adapters import InMemoryTraceStore
from soev_agents.entrypoints.http import ListQuery, Mount, RequestContext, ThreadList, create_app
from soev_agents.toolkit import ReActAgent
# config is a ReActConfig of your own.
async def list_threads(ctx: RequestContext, query: ListQuery) -> ThreadList:
# `index` is the server's own index of threads, which its hooks fill
return ThreadList(data=await index.threads_of(ctx.identity.subject, limit=query.limit))
app = create_app(
InMemoryTraceStore(),
{"assistant": ReActAgent(config)},
version="1.4.0",
mount=Mount.public(),
list_threads=list_threads,
)Run it with any ASGI server, for example uvicorn.
paramstoreTraceStoreThe store of the threads.
paramagentsMapping[str, Agent[Any, Any]]paramversionstrparamresourcesResourcesForparammodelsSequence[str]= ()The models that an input can name. An empty sequence accepts each model. The threads API refuses
another model with 422, and the Responses API with 400. With responses, the GET /v1/models route of
the Responses API lists these models.
paramdefault_modelstr | Noneparamshutdown_gracefloatparamkeepalivefloat= 15.0How many seconds an event stream can stay idle before the app sends a : keepalive comment.
paramlifespanLifespan[FastAPI] | None= NoneThe server's own lifespan, for example to open and close the store's connections.
paramobserverObserver | None= NoneThe observer of each HTTP request, run, event and call. None observes nothing.
parammountMount= Mount(thread_parameter='thread_id', capabilities='/v1/capabilities', health='/healthz')The routes of the threads API. The health route at mount.health gives the contract version and
calls no hook. The default serves the threads below /threads, with the thread id in the path
parameter thread_id, the capabilities at /v1/capabilities and the health route at /healthz.
paramlist_threadsListFor | None= NoneThe function that gives the thread list of GET <mount.threads>, when mount.list_threads
is on. None responds with 501.
paramlist_toolsToolsFor | None= NoneThe function that gives the tool list at mount.tools. None responds with 501.
paramlist_modelsModelsFor | None= NoneThe function that gives the model list at mount.models. None responds with 501.
paramcomplete_taskTaskFor | None= NoneThe function that gives the task completion at mount.tasks. None responds with 501.
paramfeaturesSequence[str]= ()The names of the features that GET <mount.capabilities> reports, for the clients of the
server. The app does not read them.
paramresponsesbool= FalseWhether the app also serves the experimental OpenAI Responses API. It calls no hook.
Returns
FastAPIThe FastAPI app. A server can add its own routes and middleware to it.
Raises:
ValueError:keepaliveis not a positive, finite number.
CONTRACT
constantCONTRACT: Final = '0.3.0'ResourcesFor
type aliasResourcesFor = Callable[[Request, Agent[Any, Any]], Awaitable[Any]]An async function that returns the resources of an agent for one HTTP request.
The app calls it with the HTTP request and the agent each time that it builds a graph, never for a read, a cancel
or a fork. Its result goes to the agent's build as resources, in a type that the server chooses. Use it to give
each agent the clients of the caller, for example a model client with the caller's credentials. The function gets
the HTTP request, not the turn's input: the input goes to the agent only.
no_resources
functionno_resourcesasync (request, agent) -> NoneMount
dataclassMount(threads='/threads', thread_parameter='id', tools=None, models=None, tasks=None, capabilities=None, health=None, list_threads=False, thread_status=True, force_sse=False, thread_id_header=False)The routes of the threads API that a deployment serves, and how they behave.
The defaults serve the threads below /threads, with the status route and without the optional routes.
Mount.public() gives the routes of the public profile. The conformance suite reads the same fields to find the
routes that it checks.
The app of create_app serves the routes of its mount argument. The default is
Mount(thread_parameter="thread_id", capabilities="/v1/capabilities", health="/healthz").
paramthreadsstrparamthread_parameterLiteral['id', 'thread_id']= 'id'The name of the path parameter that holds the thread id: "id" or "thread_id". With
"thread_id", each operation id in the OpenAPI document also holds the path and the HTTP method.
paramtoolsstr | None= NoneThe path of the tool list, or None to not serve it.
parammodelsstr | None= NoneThe path of the model list, or None to not serve it.
paramtasksstr | None= NoneThe path of the task completions, or None to not serve them.
paramcapabilitiesstr | None= NoneThe path of the contract version and the features, or None to not serve it.
paramhealthstr | None= NoneThe path of the health route, which gives the contract version, or None to not serve it.
paramlist_threadsbool= FalseWhether GET <threads> serves the thread list.
paramforce_ssebool= FalseWhether Call.streaming is True for each call, whatever its Accept header. A task completion
streams only when its body sets stream.
methodpublic() -> MountReturn the routes of the public profile.
Returns
MountThe threads below /v1/chat/threads, with the thread list and without the status route. The tool list
is at /v1/chat/tools, the model list at /v1/models, the task completions at /v1/completions/task
and the capabilities at /v1/capabilities. There is no health route. force_sse and
thread_id_header are on.
Hooks
Hooks connect the server's authentication, authorization and error format to each HTTP request.
Hooks
protocolHooksThe server's hooks on each HTTP request of the threads API: authentication, authorization and errors.
The framework has no access-control model. The hooks give the means, and the server gives the rules. The app calls the hooks of one HTTP request in this order:
subjectfinds the caller.authorizeallows or refuses the call.extendcan change the call.before_dispatchruns just before the operation.- The operation runs. When it fails,
on_failedruns. on_acceptedruns when the operation accepted the call, andon_openedafter it for a new thread. Foropen,inputsandresume, the turn has not run yet: it runs while the app sends the response.- For an event stream,
on_frameruns for each frame, andstream_errorrenders a failure of the stream.
An error in steps 1 to 6 goes to error, which renders the HTTP response. A hook refuses a call when it
raises, for example fastapi.HTTPException with a status code.
Subclass DefaultHooks and override only the hooks that you need.
Example
from fastapi import HTTPException, Request
from soev_agents.entrypoints.http import DefaultHooks, Identity, create_app
# USERS maps each bearer token to the id of its user. store is a TraceStore, and agents maps each
# agent name to an Agent of your own.
class TokenHooks(DefaultHooks):
async def subject(self, request: Request) -> Identity:
token = request.headers.get("authorization", "").removeprefix("Bearer ")
if token not in USERS:
raise HTTPException(401, "unknown token")
return Identity(USERS[token])
app = create_app(store, agents, version="1.4.0", hooks=TokenHooks())methodsubjectasync (request) -> IdentityFind the caller of an HTTP request.
paramrequestRequestThe HTTP request, before its body is read.
Returns
IdentityThe caller. The other hooks receive it in RequestContext.identity.
Raises:
Exception: Any error refuses the HTTP request.errorrenders it, with an anonymous identity.
methodauthorizeasync (ctx, call) -> NoneAllow or refuse a call before anything runs.
paramctxRequestContextThe HTTP request and its caller.
paramcallCall[CallBody]The call, as the client sent it.
Raises:
Exception: Any error refuses the call, anderrorrenders it. Nothing runs and nothing is recorded.
methodextendasync (ctx, call) -> Call[CallBody]Return the call to run, changed or not.
Use it to fill or override fields of the body, for example to choose the id of a new thread. The app
validates the body of the returned call again, as the operation's body. A body that fails this validation
is an error of the server: DefaultHooks.error does not render it, so the response is 500.
paramctxRequestContextThe HTTP request and its caller.
paramcallCall[CallBody]The call, after authorize allowed it.
Returns
Call[CallBody]The call to run.
methodoutbound_headersasync (ctx, call, outbound) -> Mapping[str, str]paramctxRequestContextparamcallCall[CallBody]paramoutbound_OutboundRequestmethodbefore_dispatchasync (ctx, call) -> NoneRun the server's work just before the operation, for example to record a new thread in an index.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call to run. Its call attribute holds the Call that extend returned.
Raises:
Exception: Any error refuses the call, anderrorrenders it. The operation does not run, andon_failedis not called.
methodon_acceptedasync (ctx, call, reply) -> NoneRun the server's work after the operation accepted the call, before the app sends the response.
For open, inputs and resume, the turn runs after this hook. An error of the turn goes to error, not
to on_failed.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call that ran. Its call attribute holds the Call.
paramreplyBackendReplyThe result of the operation. Its status_code is the HTTP status. A new thread is a
reply.status_code of 201 after open or fork.
Raises:
Exception: Any error becomes the error response, througherror. It does not undo what the operation recorded.
methodon_openedasync (ctx, *, thread_id, agent) -> NoneRun the server's work after open made a new thread, before the response is sent.
The turn of the new thread runs after this hook.
paramctxRequestContextThe HTTP request and its caller.
paramthread_idstrThe id of the new thread.
Raises:
Exception: Any error becomes the error response, througherror. The thread stays.
methodon_frameasync (ctx, call, frame) -> NoneObserve one frame of an event stream, before the app sends it.
Only event streams call it. A JSON response does not.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call that streams. Its call attribute holds the Call.
Raises:
Exception: Any error stops the stream, andstream_errorrenders it.
methodon_failedasync (ctx, call, error) -> NoneRun the server's work when the operation failed, for example to undo the work of before_dispatch.
error renders the failure after this hook. An error of on_accepted or on_opened does not call it.
An error of the turn of open, inputs or resume does not call it either, because the turn runs after
the operation accepted the call.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call that failed. Its call attribute holds the Call.
paramerrorExceptionThe error of the operation.
methoderrorasync (ctx, call, error) -> ErrorReplyRender an error of a call as its HTTP response.
paramctxRequestContextThe HTTP request and its caller. The caller is anonymous when subject failed.
paramcallCall[CallBody]The call, as the client sent it. Its body is None when subject failed.
paramerrorExceptionThe error that a hook or the operation raised.
Returns
ErrorReplyThe HTTP response.
Raises:
Exception: An error that the hook does not render. It raises it again.
methodstream_errorasync (ctx, call, error) -> StreamError | NoneRender a failure of an event stream as its last frame.
The status code was sent when the stream started, so the failure can only end the stream.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call that streams. Its call attribute holds the Call.
paramerrorExceptionThe error of the stream, or of on_frame.
DefaultHooks
classDefaultHooksThe hooks of a server without authentication: each caller is anonymous, and each call is allowed.
create_app uses them when it gets no hooks. Subclass them and override only the hooks that you need. Each
hook that is not listed below does nothing.
subjectreturns an anonymous identity.extendreturns the call unchanged.errorrenders the errors of the framework andfastapi.HTTPException, and raises each other error again.stream_errorraises the error again, so the stream fails without a last frame.
methodsubjectasync (request) -> IdentityReturn an anonymous identity for each HTTP request.
paramrequestRequestThe HTTP request. It is not read.
Returns
IdentityIdentity(None).
methodauthorizeasync (ctx, call) -> NoneAllow each call.
paramctxRequestContextThe HTTP request and its caller.
paramcallCall[CallBody]The call.
methodextendasync (ctx, call) -> Call[CallBody]Return the call unchanged.
paramctxRequestContextThe HTTP request and its caller.
paramcallCall[CallBody]The call.
Returns
Call[CallBody]call.
methodoutbound_headersasync (ctx, call, outbound) -> Mapping[str, str]paramctxRequestContextparamcallCall[CallBody]paramoutbound_OutboundRequestmethodbefore_dispatchasync (ctx, call) -> NoneDo nothing.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call to run.
methodon_acceptedasync (ctx, call, reply) -> NoneDo nothing.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call that ran.
paramreplyBackendReplyThe result of the operation.
methodon_openedasync (ctx, *, thread_id, agent) -> Nonemethodon_frameasync (ctx, call, frame) -> NoneDo nothing.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call that streams.
paramframebytesThe bytes of one server-sent event.
methodon_failedasync (ctx, call, error) -> NoneDo nothing.
paramctxRequestContextThe HTTP request and its caller.
paramcallPreparedCall[CallBody]The call that failed.
paramerrorExceptionThe error of the operation.
methoderrorasync (ctx, call, error) -> ErrorReplyRender the errors of the framework and fastapi.HTTPException as JSON responses.
Each body is a JSON object with a detail field. An HTTPException keeps its status code, its detail and
its headers.
The runtime's refusals get these status codes:
- 422: the agent refuses the input (
InputDeniedError), or a log position is outside the thread (PositionOutOfRangeError). - 409: the thread has nothing to cancel (
NothingToCancelError), its turn has not ended (ThreadActiveError), its id is in use (ThreadExistsError), or another process runs it (LeaseHeldError).
paramctxRequestContextThe HTTP request and its caller.
paramcallCall[CallBody]The call, as the client sent it.
paramerrorExceptionThe error that a hook or the operation raised.
Returns
ErrorReplyThe HTTP response.
Raises:
Exception: Each other error, raised again.
methodstream_errorasync (ctx, call, error) -> StreamError | NoneIdentity
dataclassIdentity(subject, state=None)Who makes an HTTP request, as Hooks.subject finds it.
paramsubjectstr | NoneThe id of the caller, or None for an anonymous caller. The framework does not read it. Only
the server's own hooks read it.
paramstateobject= NoneAny other value that the server keeps about the caller, for its later hooks, for example the caller's roles. The framework does not read it.
RequestContext
dataclassRequestContext(request, identity)One HTTP request of the threads API and its caller, as each hook receives them.
paramrequestRequestThe HTTP request.
paramidentityIdentityThe caller, as Hooks.subject returned it.
Call
dataclassCall[T](operation, thread_id, body, streaming=False, after=0, listing=None)One call of the threads API: the operation, its thread and its parsed body.
The hooks receive the call before it runs. Hooks.extend can return a changed copy, for example with
dataclasses.replace.
paramoperationOperationThe operation of the call.
paramthread_idstr | NoneparambodyTThe parsed body of the HTTP request, or None for an operation without a body. open, inputs,
fork and task have a body, and cancel has an optional one. The hooks get it as a CallBody.
paramstreamingboolparamafterint= 0The log position after which events starts, from the Last-Event-ID header. 0 for each other
operation.
paramlistingListQuery | None= NoneThe query of a thread list, for the list operation only. None for each other operation.
PreparedCall
dataclassPreparedCall[T](call, outbound, headers=dict(), cached_reply=None)A call that is ready to run, as the hooks after extend receive it.
The app of create_app runs each call in its own process, so only call holds information there. The other
fields serve a backend that sends the call to another server.
paramcallCall[T]The call to run, as extend returned it, with its body validated again.
paramoutbound_OutboundRequest | NoneThe HTTP request that a backend sends to another server for the call, or None for a call that
runs in this process.
paramheadersMapping[str, str]= dict()The headers that Hooks.outbound_headers gave for outbound. Empty for a call that runs in this
process.
paramcached_replyBackendReply | None= NoneA reply that a backend kept from an earlier call, to give instead of a new HTTP request, or
None.
BackendReply
dataclassBackendReply(status_code, media_type, content, headers=dict(), thread_id=None)The result of one operation, before the app sends it as the HTTP response.
Hooks.on_accepted receives it. A complete_task function of create_app gives one, inside a ReplyContext.
paramstatus_codeintThe HTTP status code.
parammedia_typestr | NoneThe value of the Content-Type header, or None for a response without a body.
paramcontentBaseModel | bytes | AsyncIterator[bytes] | NoneThe body. The app sends a pydantic model as JSON, and bytes as they are. With the media type
text/event-stream, the app streams an async iterator of bytes, and Hooks.on_frame sees each
complete frame. With another media type, the app joins the bytes of an iterator before it sends
them. None sends an empty body.
paramheadersMapping[str, str]= dict()More headers of the response.
Operation
type aliasOperation = Literal['open', 'inputs', 'resume', 'cancel', 'events', 'fork', 'read', 'export', 'status', 'delete', 'list', 'tools', 'models', 'task', 'capabilities']The name of one operation of the threads API, as a hook receives it in Call.operation.
With the default mount of create_app, the app serves these operations:
open:POST /threadsinputs:POST /threads/{thread_id}/inputsresume:POST /threads/{thread_id}/resumecancel:POST /threads/{thread_id}/cancelfork:POST /threads/{thread_id}/forkread:GET /threads/{thread_id}export:GET /threads/{thread_id}/exportstatus:GET /threads/{thread_id}/statusevents:GET /threads/{thread_id}/eventsdelete:DELETE /threads/{thread_id}capabilities:GET /v1/capabilities
list, tools, models and task name the optional routes of a Mount: GET <threads> with list_threads,
and the paths tools, models and tasks. Mount.public() serves all four.
ErrorReply
dataclassErrorReply(status_code, media_type, body, headers=dict())The HTTP response to an error, as Hooks.error renders it.
paramstatus_codeintThe HTTP status code.
parammedia_typestrThe value of the Content-Type header, for example "application/json".
parambodyMapping[str, JsonValue] | bytesThe body of the response. A mapping is sent as JSON, and bytes are sent as they are.
paramheadersMapping[str, str]= dict()More headers of the response, for example Retry-After.
StreamError
dataclassStreamError(event, data)The last frame of an event stream that failed, as Hooks.stream_error renders it.
The status code is sent when the stream starts, so a later error cannot change it. The app sends this frame
instead, as event: <event> and data: <JSON of data>, and then ends the stream.
parameventstr | NoneThe name of the server-sent event, for example "error", or None to send no event: line.
paramdataMapping[str, JsonValue]The JSON object of the data: line.
Request bodies
A hook finds the body of a call in Call.body, as one of these models. Check its type before you read a field.
CallBody
type aliasCallBody = wire.OpenThread | wire.SendInput | wire.ForkThread | wire.CancelThread | wire.TaskRequest | NoneThe body of a Call that the hooks get, by operation.
open has an OpenThread, inputs a SendInput, fork a ForkThread, and task a TaskRequest. cancel has
a CancelThread, or None when the client sent no body. Each other operation has None. Check the type with
isinstance before you read a field.
OpenThread
modelOpenThreadWhat it takes to open a thread: the agent, your first input, and optionally the id you want.
It is the body of POST /threads, and a hook finds it in Call.body for the open operation.
attributeinputJsonValueThe first input, any JSON value that fits the input schema of the agent.
attributemodelstr | Noneattributethread_idstr | None= NoneThe id that the new thread gets, or None for a generated id. One URL-safe path
segment of 1 to 128 characters: letters, digits, _, . and -. A hook can set it.
SendInput
modelSendInputWhat it takes to continue a thread: the input, and the request it answers if it answers one.
It is the body of POST /threads/{thread_id}/inputs, and a hook finds it in Call.body for the
inputs operation.
attributeinputJsonValueThe input, any JSON value that fits the input schema of the agent.
attributemodelstr | Noneattributeansweringstr | None= NoneThe id (stream:seq) of the open request that the input responds to, or None to
start a new turn.
ForkThread
modelForkThreadCancelThread
modelCancelThreadThe public profile
Four functions of create_app serve the optional routes of Mount.public(): the thread, tool and model lists, and
the task completions.
ListFor
type aliasListFor = Callable[[RequestContext, ListQuery], Awaitable[wire.ThreadList]]An async function that gives the thread list of a caller, the list_threads argument of create_app.
The app calls it for GET <threads> after the hooks allowed the call, with the HTTP request, its caller and the
query. The app keeps no thread list itself: the server keeps one, for example in Hooks.on_opened. An error goes
to Hooks.error.
ListQuery
dataclassListQuery(agent=None, limit=50, before=None, include=frozenset())The query of a thread list, from the query string of GET <threads>.
The app validates the query string before the list function receives it, and refuses an invalid one with 422. The app does not apply the query: the list function does.
paramagentstr | Noneparamlimitint= 50The maximum number of threads in the list, from 1 to 100. 50 when the client gives none.
parambeforestr | None= NoneThe id of the last thread of the page before, so that the list continues after it, or None for
the first page. 1 to 128 characters.
paramincludefrozenset[str]= frozenset()The names in the comma-separated include parameter, for example status. An empty set when the
client gives none.
ToolsFor
type aliasToolsFor = Callable[[RequestContext], Awaitable[wire.ToolList]]An async function that gives the tool list for a caller, the list_tools argument of create_app.
The app calls it for the route Mount.tools, after the hooks allowed the call. An error goes to Hooks.error.
ModelsFor
type aliasModelsFor = Callable[[RequestContext], Awaitable[wire.ModelList]]An async function that gives the model list for a caller, the list_models argument of create_app.
The app calls it for the route Mount.models, after the hooks allowed the call. The list does not change which
models an input can name: the models argument of create_app does. An error goes to Hooks.error.
TaskFor
type aliasTaskFor = Callable[[RequestContext, wire.TaskRequest], ReplyContext]A function that runs one task completion, the complete_task argument of create_app.
The app calls it for POST <Mount.tasks>, after the hooks allowed the call, with the validated body. A task
completion is one model call in the format of the OpenAI Chat Completions API, outside any thread. The app records
nothing in a thread for it. The function returns a ReplyContext. Make its reply with task_completion, or, when
the body sets stream, as a text/event-stream of task_chunk frames.
ReplyContext
type aliasReplyContext = AbstractAsyncContextManager[BackendReply]An async context manager that gives the BackendReply of one call.
The app enters it and reads the reply. For a JSON reply, the app leaves it after it reads the body, before it sends
the response. For a stream, the app leaves it after the last frame, also when the client disconnects. So the
context manager can close what the stream reads, for example an HTTP response from a model server. An error before
the reply goes to Hooks.error.
Lists
The bodies that the three list functions give.
ThreadList
modelThreadListThreadSummary
modelThreadSummaryOne thread in a thread list: its id, its agent, when it was opened, and its title.
A list_threads function of create_app gives these in the data of a ThreadList.
attributethread_idstrThe id of the thread.
attributecreated_atdatetimeWhen the thread was opened.
attributetitlestr | NoneThe title that the opening input named, or None when it named none.
ToolList
modelToolListToolView
modelToolViewOne tool in the tool list: its name, and how a client shows a call of it.
attributenamestrThe name of the tool, as a model calls it.
attributestatusdict[str, JsonValue]How a client shows a call of the tool, as JSON: running while it runs, and done once its
tool_output is recorded. Each has a template with {{param}} placeholders, a fallback, and
params, which names the source of each value. The app does not check this shape.
ModelList
modelModelListThe body of the model list, which a list_models function of create_app gives.
attributedatalist[ModelView]The models.
ModelView
modelModelViewOne model in the model list: what a client shows to people, and the id that an input names.
attributeidstrThe catalog id of the model, as vendor/model. It is not the id of an endpoint.
attributelabelstrThe name to show, which is id unless the deployment gives another one.
attributedescriptionDisplayDescription | NoneThe description to show, or None.
attributevendorstr | NoneThe organization that made the model, or None.
attributeoriginstr | NoneThe country of the vendor, or None.
attributeopen_weightsbool | NoneWhether the weights of the model are open, or None when unknown.
attributelicensestr | NoneThe license of the model, or None.
attributehostingHosting | NoneWhere the model runs and who runs it, or None.
attributecapabilitiesModelCapabilitiesWhat the model can do.
attributestructured_outputStructuredOutputStrategyHow the model gives output in a JSON schema.
attributecontext_windowintThe context window of the model, in tokens.
attributemax_output_tokensint | NoneThe maximum number of output tokens of one model call, or None when unstated.
attributelifecycleLiteral['active', 'superseded', 'deprecated']active, superseded or deprecated.
attributereplaced_bystr | NoneThe catalog id of the model that replaces it, or None.
attributedefaultboolWhether the model is the default model for chat.
ModelCapabilities
classModelCapabilitiesWhat a model can do, each as a CapabilityState.
attributetoolsCapabilityStateWhether the model can call tools.
attributevisionCapabilityStateWhether the model can read images.
attributeaudioCapabilityStateWhether the model can read audio.
attributereasoningCapabilityStateWhether the model reasons before its output.
CapabilityState
type aliasCapabilityState = Literal['unknown', 'supported', 'unsupported']Whether a model has a capability: supported, unsupported, or unknown when nobody stated it.
StructuredOutputStrategy
type aliasStructuredOutputStrategy = Literal['unknown', 'native', 'json_mode', 'unsupported']How a model gives output in a JSON schema.
native: the model takes the schema in the request. json_mode: the model takes a request for JSON, and the
instructions name the schema. unsupported: the model gives no structured output. unknown: nobody stated it.
DisplayDescription
modelDisplayDescriptionA text that a client shows to people, in Dutch and in English.
attributenlstrThe Dutch text.
attributeenstrThe English text.
Hosting
modelHostingWhere the endpoint of a model runs, who runs it, and how long it keeps the data of a call.
attributehosted_bystrThe name of the organization that runs the endpoint of the model.
attributeregionstrThe region where the endpoint runs.
attributeretentionstr | None= NoneHow long the endpoint keeps the data of a call, as its operator states it, or None
when the operator states nothing.
Task completions
A task completion is one model call in the OpenAI Chat Completions format, outside any thread. These are its body and the functions that make its response.
TaskRequest
modelTaskRequestThe body of a task completion, in the OpenAI Chat Completions format, without a model.
The app validates it and gives it to the complete_task function of create_app. Validation refuses a field
that the class does not declare.
attributemessageslist[TaskMessage]The messages for the model, at least one.
attributestreambool= FalseWhether the client asks for a text/event-stream response. It sets Call.streaming too.
attributetemperaturefloat | None= NoneThe sampling temperature, from 0 to 2, or None.
attributemax_tokensint | None= NoneThe maximum number of output tokens, more than 0, or None.
attributetop_pfloat | None= NoneThe nucleus sampling probability, from 0 to 1, or None.
attributeresponse_formatAnnotated[TextFormat | ObjectFormat | SchemaFormat, Field(discriminator=type)] | None= NoneThe format of the output, or None for text.
attributestopstr | list[str] | None= NoneOne stop sequence or a list of them, or None.
TaskMessage
modelTaskMessageOne message of a task completion, in the OpenAI Chat Completions format.
Validation refuses a message that breaks these rules:
- The roles
system,developerandassistanttake text content only. - Each message has content, except a message of the role
assistantwithtool_calls. - Only the role
assistanthastool_calls. - The role
toolhas atool_call_id, and only that role has one.
attributeroleLiteral['system', 'developer', 'user', 'assistant', 'tool']Who wrote the message.
attributecontentContent | None= NoneThe content, or None for a message of the role assistant that only calls tools.
attributetool_call_idstr | None= NoneThe id of the tool call whose result a message of the role tool holds, or None.
attributetool_callslist[HistoryToolCall] | None= NoneThe tool calls of a message of the role assistant, or None.
Content
type aliasContent = str | list[Annotated[TextContent | ImageContent, Field(discriminator=type)]]The content of a TaskMessage: a text, or a list of text and image parts.
TextContent
modelTextContentA text part of the content of a TaskMessage.
attributetypeLiteral['text']Always "text".
attributetextstrThe text.
ImageContent
modelImageContentAn image part of the content of a TaskMessage.
attributetypeLiteral['image_url']Always "image_url".
attributeimage_urlImageURLThe address of the image.
ImageURL
modelImageURLThe address of an image in an ImageContent.
attributeurlstrThe URL of the image.
HistoryToolCall
modelHistoryToolCallFunctionCall
modelFunctionCallTextFormat
modelTextFormatThe response_format of a task completion that asks for text output.
attributetypeLiteral['text']Always "text".
ObjectFormat
modelObjectFormatThe response_format of a task completion that asks for a JSON object, without a schema.
attributetypeLiteral['json_object']Always "json_object".
SchemaFormat
modelSchemaFormatThe response_format of a task completion that asks for JSON output in a schema.
attributetypeLiteral['json_schema']Always "json_schema".
attributejson_schemaSchemaDefinitionThe schema.
SchemaDefinition
modelSchemaDefinitionThe JSON schema of a SchemaFormat.
attributenamestrThe name of the schema.
attributeschema_dict[str, JsonValue]The JSON schema of the output. On the wire, its field name is schema.
attributestrictbool= FalseWhether the output must follow the schema exactly.
attributedescriptionstr | None= NoneWhat the output is, for the model, or None.
task_completion
functiontask_completion(answer, *, model, response_id, created, finish='stop', usage=None) -> bytesMake the JSON body of a task completion that does not stream, in the OpenAI Chat Completions format.
The body is a chat.completion object with one choice, at index 0, whose message is answer. The message has
the role assistant, and it leaves out reasoning_content when it is None, and tool_calls when it is
None or empty. Give the body to the app in a BackendReply with the media type application/json.
paramanswerTaskAnswerThe message of the model.
parammodelstrThe id of the model that made the message.
paramresponse_idstrThe id of the completion.
paramcreatedintWhen the completion was made, in seconds since the Unix epoch.
paramfinishstr= 'stop'The finish_reason of the choice, for example "stop", "length" or "tool_calls".
paramusageTaskUsage | None= NoneThe token counts of the model call, or None to leave out usage.
Returns
bytesThe JSON body, encoded as UTF-8.
task_chunk
functiontask_chunk(model, response_id, created, delta, *, finish=None, usage=None) -> bytesMake one frame of a task completion that streams, in the OpenAI Chat Completions format.
The frame is a server-sent event without an event name: data: <JSON> and a blank line. Its JSON is a
chat.completion.chunk object with one choice, at index 0, that holds delta. With usage, the frame has no
choice and gives the token counts only. This function does not make the data: [DONE] frame that ends an
OpenAI stream. Send data: [DONE] yourself as the last frame.
parammodelstrThe id of the model that makes the message.
paramcreatedintWhen the completion was made, in seconds since the Unix epoch.
paramdeltaMapping[str, JsonValue]The next part of the message, for example {"content": "Hello"}, or {"role": "assistant"} in the
first frame.
paramfinishstr | None= NoneThe finish_reason of the choice in the last frame of the message, or None before it.
paramusageTaskUsage | None= NoneThe token counts of the model call, for a frame after the message, or None.
Returns
bytesThe frame, encoded as UTF-8.
TaskAnswer
modelTaskAnswerThe message that the model gave for a task completion, as task_completion takes it.
attributeroleLiteral['assistant']= 'assistant'Always "assistant".
attributecontentstrThe text of the message.
attributereasoning_contentstr | None= NoneThe reasoning of the model, or None.
attributetool_callslist[HistoryToolCall] | None= NoneThe tool calls of the model, or None.
TaskUsage
modelTaskUsageThe token counts of the model call of a task completion.
attributeprompt_tokensintThe number of input tokens.
attributecompletion_tokensintThe number of output tokens.
attributetotal_tokensintThe number of input and output tokens together.
Client models
A body of the threads API as a pydantic model, for Python clients and tools that read threads.
ThreadExport
modelThreadExportOne thread's stored events, every field, in log order: a trace file.
It is the body of GET /threads/{id}/export. Unlike a read, it leaves out nothing that only the model sees, such
as the images of a tool result. The app reads it from the store directly, so a thread whose agent is gone
exports too.
attributeformatLiteral['soev.trace/1']= …The format of the document, always "soev.trace/1". Validation refuses a document of another
format.
attributethread_idstrThe id of the thread.
attributeexported_atdatetimeWhen the thread was exported, in UTC.
methodof(thread_id, entries) -> ThreadExport