Session.
Stateful multi-turn conversations with an auto-managed conversation ID and client-side transcript replay.
A Session wraps an Agent with auto-managed conversation_id and accumulates turn results. Agent runs send persist: false, so the server does not retain turn content — instead, the session accumulates each turn's exchange client-side and replays the transcript with every subsequent turn, giving the model full conversation context.
create_session(agent, **options)
import asyncio
from matilda_agent_sdk import create_session
async def main():
session = create_session(
{"name": "tutor", "instructions": "You are a patient programming tutor. Explain concepts simply."}
)
print("Conversation ID:", session.conversation_id)
# Turn 1
r1 = await session.run("What is a closure in Python?")
print("Turn 1:", r1.final_output[:100], "...")
# Turn 2 — the session replays the accumulated transcript, so the model
# sees the previous exchange
r2 = await session.run("Can you show me a simple example?")
print("Turn 2:", r2.final_output[:100], "...")
# The session accumulates all turn results
print("Total turns:", len(session.turns))
print("Last turn stream ID:", session.last_turn.stream_id if session.last_turn else None)
asyncio.run(main())Session class
session.run(prompt, **options)
Runs a turn and accumulates the result in session.turns. Options are the run() options (minus conversation_id); per-turn options merge with session defaults. Returns AgentRunResult.
session.stream(prompt, **options)
Streams a turn, yielding AgentEvent as they arrive. The result is accumulated in session.turns when the stream completes.
async for event in session.stream("Explain decorators in one paragraph."):
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
print("\n[Turns accumulated]:", len(session.turns))
print("[Final output cached]:", (session.last_turn.final_output[:60] + "...") if session.last_turn else "")session.conversation_id
The auto-generated (or provided) conversation ID. Reused across all turns.
session.turns
A list[AgentRunResult] — one per completed turn.
session.last_turn
The most recent AgentRunResult, or None if no turns have run.
Session options
| Field | type | description |
|---|---|---|
| runner | Runner | None | Custom runner instance. Defaults to the shared default runner. |
| client | MatildaClient | None | Explicit client (wrapped in a fresh Runner). Ignored when runner is set. |
| conversation_id | str | None | Explicit conversation ID. Auto-generated when omitted. |
| purpose | AgentPurpose | None | Fallback purpose when agent is a dict. |
| response_mode | str | None | Session-level response mode override. |
| metadata | dict | None | Session-level metadata merged into every turn. Defaults to {}. |
| tool_handlers | ToolHandlers | None | Session-level tool handlers. |
| max_tool_roundtrips | int | Session-level roundtrip cap. Defaults to 25. |
Metadata passthrough
Metadata can be set at multiple levels: Agent construction, Session construction, or per-call. Per-call metadata merges with (and overrides) session defaults.
from matilda_agent_sdk import create_session
def tutor_instructions(ctx):
env = ctx["metadata"].get("env", "unknown")
user = ctx["metadata"].get("user", "anonymous")
return f"Environment: {env}. User: {user}."
async def main():
session = create_session(
{"name": "helper", "instructions": tutor_instructions},
metadata={"env": "staging", "user": "demo-user"},
)
# Session-level metadata is used by default
await session.run("Who am I?")
# Per-call metadata overrides session defaults
await session.run("Who am I now?", metadata={"user": "admin"})
# → env=staging (from session), user=admin (overridden per-call)Custom runner
A Session can use a custom Runner for dependency injection in tests or isolated configuration:
import asyncio
from matilda_agent_sdk import Runner, Session
async def main():
my_runner = Runner()
session = Session(
{"name": "custom-runner-agent", "instructions": "Be brief."},
runner=my_runner,
)
result = await session.run("What is 2 + 2?")
asyncio.run(main())