Types API¶
FileInfo¶
pydantic_ai_backends.types.FileInfo
¶
Bases: _FileInfoFields
Information about a file or directory.
modified_at is an ISO 8601 timestamp, present when the backend can report
one: StateBackend records it on every write, filesystem-backed listings
take st_mtime, and the remote wire carries it end to end. A backend that
derives its listing from shell ls output has no reliable timestamp to
give, so the key is absent there — read it with .get("modified_at") and
treat a missing key as unknown, never as "just now".
Source code in src/pydantic_ai_backends/types.py
from pydantic_ai_backends import FileInfo
# Example
file_info: FileInfo = {
"name": "app.py",
"path": "/workspace/app.py",
"is_dir": False,
"size": 1234,
"modified_at": "2026-08-16T12:00:00+00:00", # absent when the backend cannot report one
}
FileData¶
pydantic_ai_backends.types.FileData
¶
Bases: _FileDataFields
One file as StateBackend stores it.
A dictionary of these is a JSON document, and callers depend on that:
the backend's whole use beyond a single process is that a host can persist
StateBackend.files and hand it back to StateBackend(files=...) later.
That guarantee is what encoding exists for.
Text is stored as content, split on newlines, with no encoding key.
Content that is not valid UTF-8 — a PNG, a zip — is stored base64 in a
single content entry with encoding="base64", and only the backend's own
readers ever see the difference.
It used to be lines of text unconditionally, with bytes decoded using
errors="surrogateescape". That round-tripped correctly in Python, which
is exactly why it survived: the lone surrogates it produces are re-encoded
by read_bytes into the original bytes, and json.dumps emits them without
complaint while json.loads reads them back. Nothing stricter accepts them.
PostgreSQL jsonb rejects an unpaired escape outright, a text column
cannot hold one because it is not valid UTF-8, and any non-Python reader of
the same document refuses it. So a workspace was serialisable right up until
an agent wrote an image into it, and the failure landed at the storage layer
rather than at the write that caused it.
A document written before encoding existed still loads: no key means text,
and the surrogates such a document may contain are encoded back to their
original bytes as they always were.
Source code in src/pydantic_ai_backends/types.py
encoding
instance-attribute
¶
Present only when content holds base64 rather than lines of text.
from pydantic_ai_backends import FileData
# Text: lines, and no encoding key.
file_data: FileData = {
"content": ["line 1", "line 2", "line 3"],
"created_at": "2024-01-15T10:30:00Z",
"modified_at": "2024-01-15T11:00:00Z",
}
# Content that is not valid UTF-8: one base64 entry, marked.
image_data: FileData = {
"content": ["iVBORw0KGgo..."],
"created_at": "2024-01-15T10:30:00Z",
"modified_at": "2024-01-15T11:00:00Z",
"encoding": "base64",
}
A dictionary of these is always a JSON document, which is what lets a host
persist a StateBackend and restore it:
import json
stored = json.dumps(backend.files, ensure_ascii=False)
restored = StateBackend(files=json.loads(stored))
WriteResult¶
pydantic_ai_backends.types.WriteResult
dataclass
¶
from pydantic_ai_backends import WriteResult
# Success
result = WriteResult(path="/workspace/app.py")
# Error
result = WriteResult(error="Permission denied")
EditResult¶
pydantic_ai_backends.types.EditResult
dataclass
¶
from pydantic_ai_backends import EditResult
# Success
result = EditResult(path="/workspace/app.py", occurrences=3)
# Error
result = EditResult(error="String not found")
ExecuteResponse¶
pydantic_ai_backends.types.ExecuteResponse
dataclass
¶
from pydantic_ai_backends import ExecuteResponse
# Example
response = ExecuteResponse(
output="Hello, World!\n",
exit_code=0,
truncated=False,
)
GrepMatch¶
pydantic_ai_backends.types.GrepMatch
¶
from pydantic_ai_backends import GrepMatch
# Example
match: GrepMatch = {
"path": "/workspace/app.py",
"line_number": 42,
"line": "def hello_world():",
}
RuntimeConfig¶
pydantic_ai_backends.types.RuntimeConfig
¶
Bases: BaseModel
Configuration for a Docker runtime environment.
Describes a pre-configured execution environment so a DockerSandbox needs
no manual package installation. Give it either a ready-made image, or a
base_image plus packages to build from.
Example
from pydantic_ai_backends import RuntimeConfig, DockerSandbox
# Custom runtime with ML packages
ml_runtime = RuntimeConfig(
name="ml-env",
description="Machine learning environment",
base_image="python:3.12-slim",
packages=["torch", "transformers", "datasets"],
)
sandbox = DockerSandbox(runtime=ml_runtime)
Source code in src/pydantic_ai_backends/types.py
| Python | |
|---|---|
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
name
instance-attribute
¶
Unique name for the runtime (e.g., "python-datascience").
description = ''
class-attribute
instance-attribute
¶
Human-readable description of the runtime.
image = None
class-attribute
instance-attribute
¶
Ready-to-use Docker image (e.g., "myregistry/python-ds:v1").
base_image = None
class-attribute
instance-attribute
¶
Base image to build upon (e.g., "python:3.12-slim").
packages = []
class-attribute
instance-attribute
¶
Packages to install (e.g., ["pandas", "numpy", "matplotlib"]).
package_manager = 'pip'
class-attribute
instance-attribute
¶
Package manager to use for installation.
setup_commands = []
class-attribute
instance-attribute
¶
Additional setup commands to run (e.g., ["apt-get update"]).
env_vars = {}
class-attribute
instance-attribute
¶
Environment variables to set in the container.
work_dir = '/workspace'
class-attribute
instance-attribute
¶
Working directory inside the container.
cache_image = True
class-attribute
instance-attribute
¶
Whether to cache the built image locally.
run_as_uid = None
class-attribute
instance-attribute
¶
Build the image around an unprivileged user, and run containers as them.
None — the default — runs as root, which is what a container does unless
told otherwise. Naming a uid instead adds three things to the generated
image: a real user with that id (so whoami and anything else calling
getpwuid works), a home directory it owns, and a virtualenv at
:data:VENV_PATH it owns, put first on PATH.
The virtualenv is the part that makes this workable rather than merely
safer. A non-root user cannot write to the interpreter's own
site-packages, so without one an agent's first pip install fails — and
uv, unlike pip, has no --user mode to fall back on, so it fails with no
way forward at all. Owning a virtualenv means both simply work, and console
scripts land somewhere already on PATH.
Only meaningful with base_image: a ready-made image was not built with
this user, so running it unprivileged would leave an agent unable to install
anything system-wide.
The uid has to match whoever owns the workspace mounted into the container, which is why it is a number rather than a name.