Sessions¶
Every request is independent by default, which keeps results reproducible and fallback safe. A conversation is the case where that default wastes work: the provider often already has everything the next turn needs. A session is how a caller says these requests belong together, without having to know what any particular provider does about it.
with client.session("copilot:auto") as chat:
client.generate("Summarize this report.", session=chat)
client.generate("Now list the risks.", session=chat)
What Each Provider Actually Saves¶
The providers that can carry state between turns save completely different things, which is why the handle is opaque rather than a conversation object:
| Provider | What an open session keeps | What that saves |
|---|---|---|
| GitHub Copilot | The conversation, server-side | Prior turns are not re-sent at all: fewer tokens billed, and no duplicated history |
| llama.cpp | The supervised server, pinned | No model load between turns, and the KV cache the next turn reuses survives |
| Ollama | The model, resident (keep_alive) |
No reload of several gigabytes of weights mid-conversation |
Everything else treats a session as inert.
A Session Never Changes an Answer¶
It is a performance and cost optimization, and holding that line is what makes it safe to pass one everywhere. Opening a session against a provider that cannot keep state is allowed and does nothing:
session = client.session("openai:gpt-5")
session.supported # False
session.reuse # 'unsupported', and every request behaves exactly as it would have
Reuse Is Reported, Not Assumed¶
reuse says what happened on the last turn, not what was hoped for:
| Value | Meaning |
|---|---|
fresh |
The provider started new state: the first turn, or one it had already expired. |
resumed |
The provider continued state it already held. |
unsupported |
Nothing was reused: this provider cannot, or that turn went somewhere else. |
State Is Bound to One Target¶
Provider state is not portable, so a session names the target it belongs to and applies
only there. If a route falls back to a different provider, or a different model on the
same one, that turn simply runs without it and reports unsupported:
result = client.generate(
"and the risks?",
route=ai.Route(targets=("ollama:qwen3:8b", "openai:gpt-5")),
session=chat, # chat belongs to ollama:qwen3:8b
)
chat.reuse # 'unsupported' if the fallback answered
Because a session already names a target, a caller can leave the target off entirely and
it stands in; it never overrides an explicit target or route.
Closing Is Local¶
close() stops the handle being used; it does not reach out to the provider.
Server-side state expires on the provider's own schedule (Ollama's keep_alive timer,
Copilot's service-side session lifetime). Closing the client itself does release what
an adapter holds open locally, such as a Copilot SDK session.
Key Takeaways
- A session is an opaque handle, not a conversation: the library never interprets what a provider stores in it.
- It never changes an answer, so passing one to a stateless provider is safe and inert.
reusereports what the provider actually did, including when a fallback meant the session did not apply.