Psych Runtime
A Python library for running AI agents inside your application. It runs an agent's tool calls, saves a Run's progress as it goes, pauses for approval before the calls you choose, and records everything a Run did so you can read it back. You supply the model, the tools and the database.
psychruntime.com · Documentation · Browser demo · Changelog
Status: pre-alpha, 0.1.0. The public API changes between minor releases,
and every breaking change is in CHANGELOG.md before it ships. Names
scheduled for removal warn for at least one minor release first.
What it looks like
One agent, one tool, offline. The test suite runs this file on every commit.
import asyncio
import psych_runtime
from psych_runtime.testing.fake_model import FakeModel
async def lookup_order(order_id: str) -> dict[str, str]:
"""Look up an order by its id."""
return {"order_id": order_id, "status": "shipped"}
spec = psych_runtime.AgentSpec(
name="support",
instructions="Help the customer with their order.",
model=psych_runtime.ModelRef(model="fake-standard"),
tools=(psych_runtime.CodeTool(name="lookup_order"),),
)
async def main() -> None:
model = (
FakeModel()
.turn(text="Checking.", tool_calls=[("lookup_order", {"order_id": "A1"})])
.turn(text="A1 has shipped.")
)
async with psych_runtime.session(model, tools=[lookup_order]) as s:
view = await s.ask(spec, "where is order A1?")
print(view.text) # A1 has shipped.
report = await psych_runtime.report(s.store, view.run_id)
print(report.totals.usage, report.totals.cost)
asyncio.run(main())
A1 has shipped.
input=0 output=0 cache_read=0 cache_write=0 cache_write_1h=0 reasoning=0 None
The tool ran: report.tool_calls says so, from the log. Cost is None
because no price table was passed; it is never rounded to zero.
Run it
Python 3.12 or newer. The package depends on pydantic and httpx.
pip install psych-runtime
psych new demo && cd demo && python main.py
Expected:
No OPENAI_API_KEY set, so this run uses the fake model.
Order A1 has shipped with DHL.
completed | 1 turn, 1 tool call | 0 tokens | cost None
Set OPENAI_API_KEY and the same file calls a real provider. psych new
writes a main.py you own; the command line never runs an agent for you.
- The distribution is
psych-runtime, the import ispsych_runtime, the command ispsych. session()defaults to an in-memory store that dies with the process. Pass aPostgresStore,MySQLStoreorDynamoDBStorefor anything that must survive a restart.- Get started walks through the file above, then a real model, a second tool, and reading a Run back.
What it handles
| Guide | |
|---|---|
Recover a run after a worker stops. Another worker claims the expired lease, replays the log and carries on. A call the dead worker left open is settled unknown, not re-run, unless the tool is registered safe_to_retry=True. Needs a durable store. |
Persist and recover runs |
| Require approval for selected tool calls. By name or by annotation. A matching call suspends the Run with its exact arguments and waits for a decision from any process. | Approvals |
| Connect Python functions, HTTP endpoints, MCP servers and A2A agents as tools, resolved fresh every turn. | Code tools, MCP |
Inspect a run: status for a screen, the answer with its work, a report with tool calls, usage and cost, or the raw log. Every read takes a scope=. |
Reading a Run |
| Track token usage and cost per model call, split by cache state, priced from a table you pass. | Pricing |
Serve multiple tenants from one process: a Scope on every record and every outbound call. |
Multi-tenancy |
Where it runs
Psych is a dependency you import, not a service you deploy. Your request
handler admits a Run and returns; a Worker in a process of yours claims it
and executes it; the two share your database and nothing else.
Psych brings no HTTP server, auth, user model, scheduler, UI, prompt library,
eval framework, vector store, budget enforcement or model router. Your
application already has those, and a library that brought its own would
argue with them. psych new api --template fastapi shows the split.
Limitations worth knowing before you build on it:
- Only what you register is safe to retry. After a crash, a tool call
recorded as started is settled
unknownunless it was registeredsafe_to_retry=True. Psych does not make an external side effect repeatable; it refuses to guess. - The subprocess sandbox limits, it does not isolate. It sets rlimits and drops privileges, but shares the host filesystem, and its network denial is a self-report. The container sandbox is the one with kernel isolation.
- Cost is only as good as the price table. A model with no known price
records
cost=None, and the report counts unpriced calls. - Provider support is the OpenAI-compatible chat API. One client reaches
OpenAI and any gateway that speaks the same protocol; anything else is a
ModelClientyou implement.
The example console
git clone https://github.com/psych-systems/psych-runtime && cd psych-runtime
docker compose up --build # then http://localhost:3000
examples/playground/ is an application
built on the library: publish an agent, talk to it, watch the log, read the
report. It supplies the HTTP server and the UI Psych does not. It is an
example, not a hardened service; do not put it on the public internet.
For coding agents
The repository ships 26 skills, one per feature, in
.agents/skills/. Give your coding agent this instruction:
Read https://raw.githubusercontent.com/psych-systems/psych-runtime/main/.agents/skills/psych/SKILL.md
and follow it to add Psych to this project.
psych skills install copies them into a project.
Documentation
psychruntime.com/docs is generated from
this repository: the API reference from psych_runtime.__all__, the guides
from the skills above, the changelog and design notes copied verbatim. The
build fails when any page disagrees with its source.
DESIGN.md, the settled design.docs/design-notes/, why each area is shaped the way it is.docs/api.md, the public API with a worked example the test suite runs.docs/deployment.md, what a deployment is responsible for: scope, secrets, sandboxes, retention.
Contributing, support and security
CONTRIBUTING.md: what is welcome, how to set up, and what a change has to pass.- Issues for bugs and questions. Most bugs reproduce against the fake model with no provider and no network; a failing test beats a description.
SECURITY.md: report vulnerabilities privately through the advisory form, never as a public issue.
Development
uv sync --all-extras --group dev
scripts/dev-services.sh start # real Postgres, MySQL and DynamoDB Local
scripts/check.sh # ruff, mypy --strict, import-linter, generated docs, three test layers
Licence
Apache-2.0. See LICENSE, and NOTICE for the one third-party attribution the package carries.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file psych_runtime-0.1.1.tar.gz.
File metadata
- Download URL: psych_runtime-0.1.1.tar.gz
- Upload date:
- Size: 2.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c6b71bf4ab7edb49aa3e4d32b6dea083dba8d9456486b8f76eb8357c7df7ff9
|
|
| MD5 |
e78bcfecefe7902790dcddcf54e991f8
|
|
| BLAKE2b-256 |
0394812edbb6eceea6bdd2b20cf76e112b3e33d06858c9468a69173e614a6831
|
Provenance
The following attestation bundles were made for psych_runtime-0.1.1.tar.gz:
Publisher:
release.yml on psych-systems/psych-runtime
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
psych_runtime-0.1.1.tar.gz -
Subject digest:
1c6b71bf4ab7edb49aa3e4d32b6dea083dba8d9456486b8f76eb8357c7df7ff9 - Sigstore transparency entry: 2753982670
- Sigstore integration time:
-
Permalink:
psych-systems/psych-runtime@372595084516e42b8eae7bb2d04024ab89da9a3b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/psych-systems
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@372595084516e42b8eae7bb2d04024ab89da9a3b -
Trigger Event:
push
-
Statement type:
File details
Details for the file psych_runtime-0.1.1-py3-none-any.whl.
File metadata
- Download URL: psych_runtime-0.1.1-py3-none-any.whl
- Upload date:
- Size: 708.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b473bb08f03b4676d914b4febb54e6c4e82eb76549e1f5525f4dbfbe5a9f8e2
|
|
| MD5 |
72ef672e840484479540e416bfb022bd
|
|
| BLAKE2b-256 |
a5ad2069fa99785541daabd8cf5d16c74b53f85c689cabda94495ee4839ff20f
|
Provenance
The following attestation bundles were made for psych_runtime-0.1.1-py3-none-any.whl:
Publisher:
release.yml on psych-systems/psych-runtime
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
psych_runtime-0.1.1-py3-none-any.whl -
Subject digest:
3b473bb08f03b4676d914b4febb54e6c4e82eb76549e1f5525f4dbfbe5a9f8e2 - Sigstore transparency entry: 2753982672
- Sigstore integration time:
-
Permalink:
psych-systems/psych-runtime@372595084516e42b8eae7bb2d04024ab89da9a3b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/psych-systems
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@372595084516e42b8eae7bb2d04024ab89da9a3b -
Trigger Event:
push
-
Statement type: