Repository navigation
Add a Pydantic AI harness adapter - #71
Closed
jacobprall wants to merge 2 commits into
Closed
jacobprall wants to merge 2 commits into
jacobprall wants to merge 2 commits into
Conversation
An agent now names a harness value built by an adapter in app/harness/ from its library's own configuration, instead of a model tier string. The pi adapter takes a typed model from Pi's catalog, so a mistyped model ID fails typecheck. config.models, ModelTier, and modelFor are gone; the built-in choices live in app/agents/models.ts. A harness is the runtime the runner calls in the Sandbox (run and selfTest, app/runner/core/harness.ts) plus what the host needs before a run (a label and the credential env var, app/harness/types.ts). What a harness must do beyond those signatures, such as giving the model only the factory's tools and submit, is pinned by the conformance suite. The runner is split into a library-neutral core (versioned contract, emitter, submit checks, input parsing, keys), shared factory tools with a library-neutral view, and the pi loop. Agent definitions are bundled into the runner, which runs the agent named in its input on that agent's harness. Each Sandbox replaces a snapshot's runner bundle with this deploy's when their hashes differ, before verify.sh, so harness and model changes need no snapshot refresh. Keys come from the harnesses: runAgent, doctor, and the workflows env group derive them from each harness's credential. agent.finished records the model the runner reports. Tests run a conformance suite over the runner contract against the pi harness and a scripted harness with no agent library, and check that the bundle imports no host-only code. Refs render-examples#67
pydantic({ module, credential }) runs an agent on a Pydantic AI agent
built in python/agents/<module> and handed over with serve(agent). The
runner starts python -m factory_harness as the runner user and passes
the input and key on stdin; the wrapper makes itself non-dumpable before
reading the key and sets it under the credential's name for the
provider. The factory supplies the prompt as run instructions, the
submit output tool checked like core/submit.ts, the turn and time
limits, and its own tools: runner.mjs --mcp-tools serves exactly the
agent's factory tools over MCP stdio, with the same environment and
read-only policy as pi. Events are revalidated before they're forwarded,
and the self-test reports the installed Pydantic AI version.
A harness declares its Sandbox needs with provision (app/harness/types.ts):
files shipped beside the bundle and a root command keyed by a hash. The
runner bundle check becomes a payload check (bundle plus provisioned
files), swapped in root-owned when it differs, and provisions run at
snapshot build and on open before the self-test. The pydantic adapter
provisions its Python sources and builds a root-owned venv from
hash-pinned requirements with the base image's python3 and pip.
createEmitter's terminated getter was copied as a value by Object.assign
and always read false; it now reflects the stream.
The conformance suite runs the pydantic harness when python/.venv
exists, and CI gets a Python job.
Refs render-examples#69
jacobprall
force-pushed
the
harness-pydantic
branch
from
October 27, 2026 23:10
a7b9d0f to
206e3d7
Compare
Collaborator
Author
|
Consolidated into #73. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #69. Depends on #70; its commit is included here until #70 merges. Review the two commits after it: the adapter, and the telemetry fix.
pydantic({ module, key })runs an agent on a Pydantic AI agent built in Python:The module supplies the model and library options. The factory supplies the prompt (as the run's instructions), its own tools, the
submittool, and the limits, as for every harness.How it runs
python -m factory_harnessas the unprivileged runner user and passes the input and key on stdin.submitis an output function whose parameter schema is the agent's output schema. It checks each submission withjsonschemaand raisesModelRetry, following the same three-attempt rule and messages ascore/submit.ts. The host still revalidates with Zod.max_turnsmaps toUsageLimits(request_limit), and the time budget to the run timeout.runner.mjs --mcp-toolsserves exactly the agent's factory tools over MCP stdio, using the same implementations, scrubbed environment, and read-only policy as pi. It never receives the key.Sandbox setup:
provisionInstead of
app/sandbox/learning what Python is, a harness now declares its Sandbox setup:hashdiffers from the one the Sandbox recorded, at snapshot build and when a Sandbox opens, before the self-test.python/factory_harness, the agent modules, and the hash-pinnedrequirements.txt. Its command builds a root-owned virtual environment with the base image's Python 3.11 and pip, keyed on the requirements hash. Deployments without a pydantic agent get no Python.Python dependencies are locked with
uvinpython/.npm run python:lockregeneratesuv.lockandrequirements.txt.Also in this PR
createEmitterattached itsterminatedgetter withObject.assign, which copied the value (false) once, soemit.terminatedwas always false. It now reflects the stream. This made a successful pydantic run report exit code 1.agent.finishedrecords the model the runner reports instarted, since a pydantic harness's model lives in Python.Tests
python/.venvexists. A new CI job syncs the locked environment, checks thatrequirements.txtmatchesuv.lock, and runs it.app/sandbox/is checked generically: declared files ship, the command runs after the swap and before verification, and both are skipped when hashes match.pyproject.toml.Verification
npm run checkpasses: 162 tests, including the pydantic leg.lsandreadover MCP, then submitted valid output. No key appeared in the output.Deploying
Deploy
factory-workflows. To run an agent on Pydantic AI, point its harness at a module inpython/agents/, then refresh snapshots so Sandboxes don't each rebuild the environment.