How it fits together¶
ApiPi is an HTTP gateway compatible with the OpenAI Agents API. Your app talks to the gateway. The gateway talks to Pi. Pi talks to your model URL. Isolation and the computer are separate choices: where Pi runs, and where files run.
OpenAI SDK / your app
|
| bearer
v
ApiPi HTTP API never inside a guest
store (SQLite or Postgres)
|
| in-process, or a worker lease
v
Pi (+ stdio MCP) none | microvm
|
+-- local files next to Pi (/workspace in a guest)
+-- or remote env self_hosted runner
+-- HTTP MCP e.g. Tavily
+-- model host OPENAI_BASE_URL
A turn is one model loop. The client posts a message. The API
authenticates the bearer, loads the session, and asks execution to
run. Combined apipi serve runs Pi in that process.
apipi serve --api-only leases a worker. Pi
runs in isolation (none or a Firecracker guest).
Public events are written to the store, then SSE. Token deltas are
live only and are not stored. A thinking preview is stored. The full
thinking text is not.
Two knobs:
| Knob | What it controls |
|---|---|
| Run mode | Where Pi and stdio MCP run (APIPI_RUN_MODE) |
| Environment | Where file and shell tools run (environment.type) |
They combine. self_hosted does not replace a microVM around Pi. The
API stays on the host.
Agents, sessions, the computer, and artifacts below are the pieces the durable store keeps (and, for files, disk or object storage). One process uses SQLite. Several processes share Postgres.
Agents¶
An agent is saved configuration: model, instructions, tools, and metadata. You create them; a fresh database has none.
You create agents with POST /v1/agents. They live in the store until
you delete them. A session may pass agent_id or an inline agent.
Inline config is used for that session only. It is not saved unless
you POST /v1/agents. Inline model and instructions are kept on the
session for follow-up turns. Saved agents keep reading the agent row.
Pi always receives a gateway platform prompt after its harness default,
unless an operator or caller replaces that default. Composition order
is: Pi's default, or a replacement system prompt when one is set; then
the main platform prompt (a short built-in text, or an operator
override); then optional additional platform text; then
agent.instructions. Empty or omitted agent instructions skip only
that last block. The platform prompt is operator config, not a
transcript item. A replacement system prompt is
metadata["apipi.system_prompt"] on the session, then the agent, then
[pi].system_prompt. See config.
Changing a saved agent later does not rewrite history on existing sessions.
Sessions¶
A session is one conversation. The durable store holds the session row, the append-only event log, turns, and items. Token deltas are live SSE only and are not stored. Thinking events store a short preview, not the full text. That transcript is the source of truth. Pi's on-disk files are a cache.
Create a session with POST /v1/agents/sessions. A non-empty input
starts the first turn. Follow-up messages go to
POST /v1/agents/sessions/{session_id}/events. Status is idle,
in_progress, requires_action, or failed.
When a none or self_hosted session is idle for APIPI_IDLE_TTL
(default 15 minutes), the process that holds Pi kills it to free RAM.
That follows the environment type, not the run mode. Combined
apipi serve does that in-process. In a split deploy, apipi worker
owns the idle reap; apipi serve --api-only does not. An
openai_hosted computer lasts until
APIPI_SANDBOX_TTL_OPENAI_HOSTED (default 1 hour): one timer stops Pi
and deletes the workspace together. There is no separate guest timeout.
A session or agent idle_ttl replaces that default for that session.
The session row stays. The next message starts
Pi again, rebuilds /workspace from stored config (skills, packages,
setup commands), reloads the cached harness session file so the model
keeps the conversation, and continues the event log.
GET /v1/agents/sessions/{id}/export returns the transcript as JSON.
A session export is enough to leave.
DELETE removes the session for that tenant, including the workspace
directory, artifact metadata, stored bytes, and the harness session
cache.
The computer and files¶
Where file and shell tools run is independent of run mode, which is where Pi itself runs.
environment.type |
Files |
|---|---|
openai_hosted (default) |
A local directory next to Pi. OpenAI's field name; not OpenAI's cloud. hosted is the same. |
none |
No filesystem and no shell. |
self_hosted |
An external runner. Tools go over a WebSocket. |
On openai_hosted, the path is
{APIPI_SESSIONS_DIR}/{tenant_id}/{session_id}. In a microVM the guest
cwd is /workspace. Read, write, edit, and bash run against that
folder. After APIPI_SANDBOX_TTL_OPENAI_HOSTED (default 1 hour) with
no activity, Pi stops and the directory is deleted. The transcript,
published artifacts, and the harness session cache stay. The next turn
rebuilds /workspace from stored config and reloads that cache so Pi
continues the conversation. Scratch files and published artifact bytes
are not copied back into /workspace. That directory is bounded by
APIPI_MAX_WORKSPACE_BYTES (default 1GiB).
On self_hosted, files stay on the runner. The gateway also copies
outputs/ from the runner on turn complete and on Pi stop if the
socket is up. On none, there is no computer.
A crash before publish can lose unpublished files under outputs/.
Artifacts¶
An artifact is a named output the API can fetch after a turn
completes. Metadata is in the store, including turn_id when the file
was published at turn complete, plus key_id and byte size. Bytes
live in the configured object store: local files under
{APIPI_SESSIONS_DIR}/.artifacts/{tenant_id}/{key_id}/{session_id}/{id},
or S3-compatible object storage with the same key layout. Hosted file
and skill uploads share that backend (local or S3) under separate key
namespaces; see config.
GET .../artifacts/{id}/content reads that store in every run mode.
410 if nothing was published. DELETE removes the metadata and the
file. The live file on the computer is unchanged. Artifacts last until
you delete the artifact or the session. The host store for one session
is bounded by APIPI_MAX_ARTIFACT_BYTES (default 512MiB). Publishing
over that cap emits agent.session.error with code
artifact_too_large and does not write the extra bytes.
Ask the agent to write under outputs/ if you need the file after the
workspace expires. Copies are immutable. A later turn that writes the
same path publishes another artifact. Harvest on Pi stop is a safety
net for files written after the last completed turn. Existing stores
may still list rows whose path starts with artifacts/. Those remain
readable. New publishes use outputs/.
Compared with OpenAI¶
OpenAI's Agents API uses the same agent and session shapes. The computer is different.
On OpenAI, openai_hosted is a cloud Linux sandbox. The working
directory is /workspace. Files in that sandbox last across turns
until the sandbox goes idle for about an hour. Files under
/workspace/outputs are published as immutable artifacts when a turn
completes. Those copies remain downloadable after the sandbox expires.
On ApiPi, openai_hosted is a folder on your machine next to Pi. The
working directory in a microVM guest is /workspace. Files last
across turns until the sandbox is idle for
APIPI_SANDBOX_TTL_OPENAI_HOSTED (default 1 hour). Then Pi stops and
scratch files are deleted. When a turn completes, files under
outputs/ are published as immutable artifacts. Those copies remain
downloadable after the sandbox expires. The next
turn recopies skills and re-runs packages and setup commands into a
fresh workspace.
OpenAI's self_hosted files stay with your provider and are not
published through their Artifacts API. Ours stay on the runner the
same way, except we also copy outputs/ from the runner on turn
complete and on Pi stop if the socket is up.
Session conversation state is similar: both keep turns and items so you can continue later. OpenAI stores that on their side. ApiPi stores it in your store. Export is how you take the thread with you.
packages and setup_commands on openai_hosted install dependencies
and run prep commands before the first turn. Other OpenAI environment
fields such as files, env, or network return an error
(not_implemented).