Skip to content

feat(agents): add the Pydantic AI agent - #338

Open
mpfaffenberger wants to merge 2 commits into
scaleapi:mainfrom
mpfaffenberger:pydantic-agent
Open

mpfaffenberger wants to merge 2 commits into
scaleapi:mainfrom
mpfaffenberger:pydantic-agent

Conversation

@mpfaffenberger

@mpfaffenberger mpfaffenberger commented Oct 26, 2026 •

Copy link
Copy Markdown

Ship a Pydantic AI agent in the agents bundle, beside Claude Code and the others, at
@local/agentenv-framework/agents/pydantic-ai (alias pydantic-ai).

The agent-env protocol SDK serves the A2A endpoint and its extensions; each turn runs a Pydantic AI
Agent in process with the Pydantic AI Harness Coder tools (shell, file tools, sub-agents) in a
LocalWorkspace, the task's MCP servers as MCPToolsets and installed skills through the harness's
Skills. A context's message history is persisted, so later turns continue it, its messages are the
live trajectory, and snapshots carry it with the files attached in it. The model is called through
the [model] endpoint as an OpenAI-compatible one; a vllm: prefix picks Pydantic AI's vLLM provider.

Pydantic AI's own Agent.to_a2a is not used: it serves plain A2A, without the agent-env extensions
the validator checks.

Also: the fake OpenAI endpoint answers streamed requests with server-sent chunks, which Pydantic AI
always asks for.

Verified: unit tests (tst/unit/agents/pydantic_ai, 34), tst/integration/cli/run_bundle_pydantic_ai_test.py (6, real Docker against the fake OpenAI endpoint), ruff check, and agent-env a2a-agent validate locally against an OpenAI-compatible vLLM endpoint: every extension the agent advertises passed (agent-config, mcp-config, trajectory, skill-config, snapshot, peer-agents, install/v1); the snapshot changelog is not implemented, as for OpenHands.

RetriggerView in GreptileConfidence Score: 4/5

The PR still needs protection against a second cancellation while a threaded write finishes.

Summary

Adds a Pydantic AI coding agent to the shipped bundle.

  • Pydantic AI runs tasks as a deployable agent-env agent.
  • The fake OpenAI endpoint returns streamed replies when a request asks for them.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[A2A task] --> B[Pydantic AI agent]
  B --> C[Model endpoint]
  B --> D[Coding tools, MCP tools, and skills]
  B --> E[Saved conversation and attachments]
  E --> F[Snapshot save and restore]
Loading

Reviews (2) · Last reviewed commit: "fix(agents): validate a snapshot's Pydan..." · Reviewed by Greptile

Ship a Pydantic AI agent in the agents bundle, beside Claude Code and the others, at
@local/agentenv-framework/agents/pydantic-ai (alias `pydantic-ai`).

The agent-env protocol SDK serves the A2A endpoint and its extensions; each turn runs a Pydantic AI
Agent in process with the Pydantic AI Harness Coder tools (shell, file tools, sub-agents) in a
LocalWorkspace, the task's MCP servers as MCPToolsets and installed skills through the harness's
Skills. A context's message history is persisted, so later turns continue it, its messages are the
live trajectory, and snapshots carry it with the files attached in it. The model is called through
the [model] endpoint as an OpenAI-compatible one; a `vllm:` prefix picks Pydantic AI's vLLM provider.

Pydantic AI's own Agent.to_a2a is not used: it serves plain A2A, without the agent-env extensions
the validator checks.

Also: the fake OpenAI endpoint answers streamed requests with server-sent chunks, which Pydantic AI
always asks for.
@mpfaffenberger
mpfaffenberger requested a review from a team as a code owner October 26, 2026 21:47
Comment thread src/agent_env/agents/agents/pydantic-ai/pydantic_ai_agent.py Outdated
Comment thread src/agent_env/agents/agents/pydantic-ai/pydantic_ai_runtime.py Outdated
…hreaded writes before a cancel lets go of the locks

A snapshot whose messages.json was JSON but not Pydantic AI messages replaced the conversation and
workspace, then failed the next turn; the runtime now validates the messages before anything is
replaced. A turn or snapshot cancelled while a thread wrote its files released its locks before the
write ended; to_completion waits for the thread before re-raising.
@earakely-scale

Copy link
Copy Markdown
Collaborator

🤖 Automated Claude routine — Env Pod PR Context Review. This is a bot, not Edgar Arakelyan; not a human review.

Context from a sibling PR and from main, not about this diff's own logic.

#345 adds a second agent (letta-code) and lands on the same three shared registration points this PR does. Two of them are exhaustive assertions, so they don't merge additively:

  • tst/unit/bundle/shipped_bundles_test.py:45 — assert {alias: … for …} == { … } over the whole alias table. This PR's expected dict is the six on main plus pydantic-ai; feat(agents): add a Letta Code agent #345's is the same six plus letta-code. Once both land the real table holds eight, so whichever expected dict survives the merge is short one entry and the test fails. It needs both names, not whichever side wins the conflict.
  • src/agent_env/agents/aliases.toml — main ends at openhands = ["openhands"] and both PRs append a line after it, so that's a textual conflict either way.
  • .github/scripts/infra_build.py — both add an _IMAGES entry and a _SMOKES entry; those are dict keys, so they do merge additively.

Separately, this branch looks behind main (a4ae4f6) on the third one. Your hunk for tst/unit/test_infra_images_script.py:62 shows the variable as agents and the expected list ending "agentenv-pi", "agentenv-proxy"], while main now reads:

    bundles = [image.name for image in BUNDLE_IMAGES]
    …
    assert "agentenv-gateway" in planned["infra"] and bundles == [
        "agentenv-claude-code", "agentenv-codex", "agentenv-gemini", "agentenv-opencode", "agentenv-openhands",
        "agentenv-pi", "agentenv-proxy", "agentenv-mail-mcp"]

So after a rebase the list also needs agentenv-mail-mcp, and the name is bundles. Worth noting the order is BUNDLE_IMAGES declaration order rather than alphabetical (agentenv-mail-mcp sits last, after proxy), so where agentenv-pydantic-ai belongs follows wherever the image gets declared.

Each reviewer sees one diff at a time, so none of this shows up on either PR on its own. Offering the comparison, not a verdict on merge order.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants