Helpers
ACP Kit also ships helper packages that are useful around the adapter runtime but are not themselves adapter packages.
Today the main helper packages are:
codex-auth-helperacpremote
Helper docs:
acpremote
acpremote is the transport helper package.
It handles:
- exposing any existing
acp.interfaces.Agentover WebSocket - exposing stdio ACP commands over WebSocket
- mirroring a remote ACP endpoint back into a local ACP agent boundary
- serving
/acpmetadata and/healthzalongside the WebSocket endpoint
Use it when you already have an ACP server and need remote transport, not when you need to adapt a framework runtime into ACP for the first time.
If the runtime is still a Python target, the usual path is:
- resolve it through
acpkit - expose it through
pydantic-acporlangchain-acp - use
acpremoteonly when you need WebSocket transport or a local mirror
Read the full transport guide in acpremote Overview.
codex-auth-helper
codex-auth-helper turns an existing local Codex login into either:
- a
pydantic-aiResponses model - a LangChain
ChatOpenAImodel pinned to the Responses API
It handles:
- reading
~/.codex/auth.json - refreshing expired tokens
- deriving the account id
- constructing Codex-specific OpenAI clients
- returning a ready-to-use
CodexResponsesModel - returning a ready-to-use LangChain
ChatOpenAI
Why It Exists
Codex-backed model usage is easy to get subtly wrong by hand.
The helper centralizes the backend-specific behavior that should stay stable:
- Codex Responses endpoint wiring
- auth refresh flow
openai_store=False- streamed Responses usage even when Pydantic AI takes a non-streaming request path
- real incremental deltas through Pydantic AI and LangChain streaming APIs
- explicit Responses Lite request assembly when selected
OpenAI SDK transport clients and auth-refresh clients have separate ownership.
For OpenAI SDK 3.x, pass openai.DefaultAsyncHttpxClient as http_client= and
a standard httpx.AsyncClient as auth_http_client= when customization is
needed. Closing an owned helper client closes both resources; borrowed clients
remain caller-owned.
Minimal Usage
Pydantic AI:
from codex_auth_helper import create_codex_responses_model
from pydantic_ai import Agent
model = create_codex_responses_model(
"gpt-5.4",
instructions="You are a helpful coding assistant.",
)
agent = Agent(model)
Pass instructions= explicitly to create_codex_responses_model(...). On the
Pydantic path you can also add Agent(instructions=...) when you want
agent-owned instructions on top of the factory default.
Responses Lite
Responses Lite is an explicit transport choice; normal Responses remains the default:
model = create_codex_responses_model(
"gpt-5.6-luna",
instructions="You are a helpful coding assistant.",
transport="responses_lite",
)
At request time the helper emits the Lite input-item form for instructions and
tools, requests all-turn reasoning context, and forces
parallel_tool_calls=False. The current Codex Lite backend does not accept
native top-level parallel tool-call batches. Keep the default
transport="responses" when that form of parallelism is required.
The helper does not automatically select Lite by model name and does not fall back when the backend rejects a model/transport combination.
Responses WebSocket
Install the optional connection dependency with
uv add "codex-auth-helper[websocket]", then select it explicitly on the
Pydantic AI factory:
model = create_codex_responses_model(
"gpt-5.6-luna",
instructions="You are a helpful coding assistant.",
connection="websocket",
fallback="error",
)
async with model.responses_session():
result = await Agent(model).run("Complete the task.")
One session owns one outbound Responses connection. The first request is full;
append-only turns may use previous_response_id and transmit only their new
suffix. The helper resends instructions and resets to full input whenever
history or the effective request contract changes.
A stream consumed through response.completed retains its completed response
even if the caller stops iteration on that event. A socket already known to be
closed before the next model request is replaced with full-history input; changed
handshake headers also require a new connection. Lower-level client calls must
provide full input instead of a previous-response suffix in those cases.
Helper 1.8.0 recovers transient WebSocket send/receive failures for
non-streaming Pydantic requests and LangChain ainvoke. Recovery discards the
incomplete model output, reconnects with full history, and does not rerun completed
local tools. The client max_retries controls reconnects (default two), with
bounded backoff. fallback="http" additionally permits HTTP after those retries,
as well as initial-handshake fallback. Provider usage from lost attempts may be
unreported, and the model may independently choose another tool call.
Raw client calls and exposed streams are not replayed. Cancellation, permanent
auth/permission failures and provider-hosted tools also prevent response replay.
The LangChain WebSocket factory defaults to automatic stream selection: ordinary
ainvoke is buffered, astream is live. Explicit streaming settings/callbacks
can select the non-replaying stream path. HTTP defaults are unchanged.
Responses Lite remains HTTP-only. The LangChain factory supports
connection="websocket", fallback=, and transport_observer= through a
ChatOpenAI subclass. Use async with model.responses_session(): around the
complete async agent/graph run. It retains native message/tool/schema conversion
and continues only unchanged, acknowledged history on its owning connection.
System messages become Codex instructions; HTTP invocation streams internally.
Native synchronous LangChain calls and HTTP response-header capture require
the HTTP connection. Do not supply response IDs or enable unchecked native
use_previous_response_id. Kedi owns isolated sessions at adapter-run boundaries.
An optional transport_observer= receives content-free metadata for submitted,
completed, abandoned, failed, retry, and fallback attempts. It reports physical input
item/byte counts and continuation decisions without exposing request content,
credentials, account IDs, or raw response IDs. Observer failures are isolated
from model execution.
LangChain:
from codex_auth_helper import create_codex_chat_openai
from langchain.agents import create_agent
graph = create_agent(
model=create_codex_chat_openai(
"gpt-5.4",
instructions="You are a helpful coding assistant.",
),
tools=[],
name="codex-graph",
)
create_codex_chat_openai(...) requires instructions=. There is no implicit
default on the LangChain path.
Streaming
The Pydantic model forwards Codex Responses deltas without buffering the full response:
async with agent.run_stream("Summarize the current workspace.") as result:
async for delta in result.stream_text(delta=True):
print(delta, end="", flush=True)
The LangChain factory sets streaming=True by default and works with the normal astream() API:
model = create_codex_chat_openai(
"gpt-5.4",
instructions="You are a concise coding assistant.",
)
async for chunk in model.astream("Summarize the current workspace."):
print(chunk.text, end="", flush=True)
Set streaming=False only for LangChain consumers that require its non-streaming model path. The
Pydantic model still uses the Codex backend's required SSE transport for ordinary request() calls.
ACP-side usage looks the same:
from codex_auth_helper import create_codex_responses_model
from pydantic_ai import Agent
from pydantic_acp import run_acp
agent = Agent(
create_codex_responses_model(
"gpt-5.4",
instructions="You are a helpful ACP coding assistant.",
),
name="codex-agent",
)
run_acp(agent=agent)
What It Does Not Do
- it does not log you into Codex
- it does not create
~/.codex/auth.json - it does not provide generic Chat Completions wiring
- it does not replace Pydantic AI itself
Lower-level Factories
If you want more control, the helper also exposes:
create_codex_async_openai(...)create_codex_openai(...)create_codex_chat_openai(...)CodexAsyncOpenAICodexOpenAICodexResponsesModelCodexResponsesTransportCodexAuthConfigCodexTokenManager
The full API is documented in API Reference.