Memory for agents on the OpenAI Agents SDK.
In Python, NiadraAgentsMemory gives you run_config(), the filter that injects the context before every model call, hooks, which record the turns, and tools. In JavaScript, NiadraSession, niadraInstructions(), niadraTools() and niadraRunHooks(). The SDK's Session stays yours.
The Agents SDK's Session stores this agent's items: what it saw and produced in each run. It is one agent's working memory, keyed by a session of your application. What the same person told another vendor's voice agent, or what the billing agent did in the ERP, is in no session at all.
Niadra keeps the company's derived memory of the customer, not a copy of each item. The call_model_input_filter places the pack after the agent's instructions and the turn_block after the input, on every model call; the hooks record what the customer said and what the agent answered, with the usage reported. A handoff between the run's agents carries the same memory.
pip install 'niadra[openai-agents]' # openai-agents 0.22.3 or newer, below 1"""An OpenAI Agents SDK agent with the customer's memory."""
import asyncio
from agents import Agent, Runner
from niadra import AsyncNiadra, phone
from niadra.integrations.openai_agents import NiadraAgentsMemory
niadra = AsyncNiadra(channel="chat")
async def main() -> None:
async with niadra.conversation("thread-81", subject=phone("+5511912345678")) as conversation:
memory = NiadraAgentsMemory(conversation, agent_memory=True)
agent = Agent(
name="Support", instructions="You are Acme's agent.", model="gpt-4.1", tools=memory.tools
)
result = await Runner.run(
agent, "Where is my replacement lid?", hooks=memory.hooks, run_config=memory.run_config()
)
print(result.final_output)
await niadra.close()
asyncio.run(main())npm install @niadra/sdk @openai/agents # @openai/agents 0.18, as an optional peer dependencyimport { Agent, Runner } from "@openai/agents";
import { Niadra, handles } from "@niadra/sdk";
import { NiadraSession, niadraInstructions, niadraRunHooks, niadraTools } from "@niadra/sdk/openai-agents";
const niadra = new Niadra();
const runner = new Runner();
/** One customer message in; `userId` comes from your session, never from the model. */
export async function reply(userId: string, chatId: string, text: string): Promise<string> {
const convo = niadra.conversation({ subject: handles.appUserId(userId), channel: "web_chat", conversation_id: chatId });
const billing = new Agent({ name: "Billing", instructions: niadraInstructions("You handle invoices and credits.", convo), tools: niadraTools(convo) });
const support = new Agent({
name: "Support",
instructions: niadraInstructions("You are Acme's support agent. Hand billing questions to Billing.", convo),
tools: niadraTools(convo),
handoffs: [billing],
});
const stop = niadraRunHooks(runner, convo);
try {
const result = await runner.run(support, text, { session: new NiadraSession(convo) });
return String(result.finalOutput ?? "");
} finally {
stop();
}
}- Python
- TypeScript
The same code is in examples/openai_agents_run.py, examples/openai-agents.ts in the SDK repositories, where it runs in CI against the framework's real types and Niadra's emulator. To try it without Niadra's cloud, niadra-mock and NIADRA_BASE_URL=http://127.0.0.1:8765.
How the adapter wires in
The five primitives of every Niadra integration, in this framework's extension points.
- Context
- In Python, run_config() sets call_model_input_filter, which runs right before every model call: the pack goes after the agent's instructions and the turn_block after the input, as a system message; a filter you already had runs first. In JavaScript, niadraInstructions(base, conversation) makes the instructions dynamic: your text, then the agent's notes and the pack, then the suffix.
- Turns
- In Python, hooks (a RunHooks) records the customer's new messages when the model is first called for them, and the agent's answer with the usage reported; customer turns are keyed by position, so the history a Session replays on the next run is never recorded twice. In JavaScript, NiadraSession is a Session for run(agent, input, { session }): it keeps the items in the session you give it and records both sides.
- Tools
- tools: the three history tools as FunctionTools with the kit's names, descriptions and schemas, bound to the customer. In JavaScript, niadraTools(conversation).
- Verification
- What your app proved (a login, an OTP) goes to conversation.verify() before the run.
- Handoff
- An SDK handoff between agents records handoff("agent"); give every agent of the run the same memory. In JavaScript, niadraRunHooks(runner, conversation) records the handoffs.
What the agent receives
The context is compiled when the memory changes and served ready, with no AI model on the read. What another channel said during the conversation arrives as a delta, at the end of the prompt.
- Who the customer is, by what the conversation has proven: the verification level decides what goes in
- Facts, open items and promises, with the date and the channel they came from
- What other agents did inside the company, confirmed by the system of record
- Patterns computed by rule, with the evidence and the expiry
- The three history tools: search, timeline and open an item, bound to the customer in your code
- A receipt of every read, chained by SHA-256
What the adapter does not do
- The Python adapter does not implement the SDK's Session: a Session stores the agent's own items, and Niadra keeps derived memory, not a copy of each item. Use any Session next to it. In JavaScript, NiadraSession wraps the session you choose.
- Nothing here fails a run: Niadra slow or down leaves the instructions yours alone.
- In Python, the openai-agents extra pins versions incompatible with livekit, crewai and litellm; install one per environment.
- Tested against openai-agents 0.22.3 and @openai/agents 0.18.0 with the model replaced by a fake and Niadra on the emulator.
Frequently asked questions
Does this replace the SDK's Session?
No. The Session is this agent's working memory, and it stays yours, in memory or in SQLite. Niadra is the company's memory of the customer, derived from the events of every channel and agent. The two live in the same run.
With several agents and handoffs, who reads the memory?
Every agent of the run gets the same memory: the filter runs on every model call, from any agent, and the handoff between them is recorded as a transfer to the next agent, with the conversation open.
Is the context cached by the provider?
The pack goes after the instructions, which remain the prompt's prefix, and comes pinned per conversation, so the start of the prompt repeats call after call and the provider reuses it. What changed enters as a delta, at the end.
Do I have to change my model, my prompt or my vendor?
No. The adapter places the context after your instructions and the delta at the end of the prompt, in the extension points the framework already has. Your model, your prompt and your vendor stay the same, and switching any of them later does not erase the memory.
Where does the data live, and what does it cost?
The data stays in a single region, stated in the contract, encrypted with AES-256-GCM under a key exclusive to your company and protected in a FIPS 140-3 HSM. The price is per conversation or task in which an agent read the memory: US$ 2 to 3 per thousand, by volume, with reads, searches and system events included. Niadra is opening to companies by request, before the public launch.
Tell us what you are building.
A work email and two lines about your agents are enough. The people who write the code reply, with an early-access proposal for your case.
Rather tell us more about your company? Use the full form