Skip to content

Add openai-agents-submilli, the OpenAI Agents SDK integration - #4

Open
snlamm wants to merge 5 commits into
mainfrom
claude/project-thread-ucxn6e
Open

snlamm wants to merge 5 commits into
mainfrom
claude/project-thread-ucxn6e

Conversation

@snlamm

@snlamm snlamm commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

This adds a second integration package to the workspace, next to langchain-submilli. It lets an OpenAI Agents SDK agent run TypeScript programs on submilli-server. Those programs can call the application's own function tools, and the SDK's approvals apply to each call. Linear SUB-2053.

What it does

SubmilliTools(blueprint=...) gives the agent the server's six submilli__typescript__* tools as FunctionTools. Their descriptions are the ones the server supplies for the Blueprint. Names, arguments, and the result shape match langchain-submilli.

  • Context, not the model. The user's variables, the harness secrets, and the conversation id all come from the run context passed to Runner.run.
  • Sessions. One Submilli session per conversation, reused across turns. Where it is kept is a SessionStore: in memory by default, or SQLiteSessionStore (or your own) to survive a restart. aclose_conversation(id) closes it.
    • A different user in the same conversation is refused before the model runs.
    • An expired session is replaced and the model is told.
    • A restarted server that asks for secrets gets them again once.
  • Idempotency. Each execute sends a key derived from the SDK's tool call id, so a repeated call replays the recorded run instead of running the program again.
  • What the application sees. Execution and session ids reach the application in ToolCallOutputItem.custom_data, which the model never sees.
  • Application tools. ptc=[...] turns existing FunctionTools into the @tools Package for programs.
    • Each call is a real SDK tool call. The run's context, guardrails, timeout, tracing, and needs_approval all apply.
    • An approval pauses the agent run with a ToolApprovalItem for the application's tool. RunState.approve/reject then resumes it, including from to_string()/from_string in another process.
    • The SDK only pauses before a tool call, and a program's calls happen inside the execute tool. So with ptc the execute tool is an Agent.as_tool whose model is the program's run on the server rather than an LLM. NOTES.md explains this; read it before changing _program.py.

What to be aware of

  • Two dependencies on SDK internals. These are private FunctionTool fields set through dataclasses.replace, plus as_tool's _agent_instance. The dependency is therefore pinned to openai-agents>=0.24.0,<0.25. Both paths are covered by tests.
  • Publishing needs setup first. The PyPI trusted publisher has to be registered for the new project openai-agents-submilli before the openai-agents-submilli-v0.1.0 tag is pushed.
  • Approvals cover only programs. Tools given only in ptc are not shown to the model. A tool also given to the agent directly bypasses the Blueprint, which the README says.

Checks

  • mypy --strict, pyright, and pyright --verifytypes at 100%.
  • Ruff passes.
  • 40 unit tests run the SDK's real Runner with a scripted model against a respx fake of the HTTP API.
  • 11 integration tests run on a real server, started by the tests themselves. They pass on submilli-server 0.4.1 and 0.4.3.
  • CI runs both kinds of tests. The build job checks the wheel's metadata.

Verify it yourself

uv sync --all-packages
cd packages/openai-agents-submilli
uv run pytest
SUBMILLI_BIN=/dir/with/submilli-server uv run pytest tests/integration_tests
uv run pyright --verifytypes openai_agents_submilli --ignoreexternal

To see an approval with a real model, follow the "Use application tools" section of the tutorial in the companion submilli-runtime PR, using a tool with needs_approval=True.


Generated by Claude Code

@snlamm snlamm self-assigned this Oct 28, 2026
The execute output now carries `tool_calls` (tool, arguments, outcome) for the
model, without the call ids that stay in `custom_data` for the audit trail. In
a live run with a real model, a program that refunded one order and was then
refused the next left the model unable to say which refund had gone through.
The lost-run message now points at the listed calls too. @submilli/ai-sdk
already shows the calls the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014d2Jufg2VYGnwUi5xQeE1C
@snlamm
snlamm requested a review from somdoron October 28, 2026 19:33
A live run showed that continuing a conversation with the pending execute call
still in the input makes the Responses API refuse the next run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014d2Jufg2VYGnwUi5xQeE1C

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