Python SDK reference
The Oda Python SDK drives DataMind Oda from your own Python program: it starts an Oda runtime as a subprocess and speaks newline-delimited JSON-RPC with it over standard input and output. This page is the reference companion to The Oda Python SDK: it covers what a run returns, the runtime's lifecycle, the wire surface, the errors you can meet, and the systems that have a runtime build.
What a run result carries
A run returns a result with five parts:
| Part | What it holds |
|---|
session_id | The session the run worked in. |
final_response | The text of the last assistant message in the run, with its text blocks joined; an empty string when the run has no assistant message. |
finish_reason | How the last turn ended: completed, max-tokens or error, or none when the run has no turn end. |
events | The events of your own session, in the order they arrived. |
notifications | The notifications of your session and of every helper agent below it, in the order they arrived on the wire. |
- The notifications follow the helper-agent tree: the client records the parent-to-child edge from each helper start notification and pairs the matching finish notification with it, so the activity of the whole tree arrives. For what helper agents are, see Delegating work to subagents.
- Events stay with your session: the final response is built from the session's own text, and a helper agent's output cannot replace it.
Starting, reusing and closing the runtime
- Starting is lazy and idempotent: the runtime launches the first time a run needs it, and asking again while it is up changes nothing.
- One client keeps one runtime across all of its runs; later runs reuse it until you close the client, and a context manager closes it for you when the block ends.
- A run sends your prompt and returns when the session goes
idle; the result describes the interval from the prompt's receipt to that moment.
- Each run works in its own session unless you pass a session id; reuse a session id, with the same home, to continue the same conversation.
- Closing stops the runtime in stages: a shutdown request with a bounded wait, then the runtime's input closes, then a termination signal if the process is still up, and a forced end as a last resort. Closing before the runtime ever started does nothing, and closing again is safe.
The wire surface
- The client is created with a configuration object or with keyword options; passing both is refused.
- The launch command carries the runtime executable, the selected profile and every patch file you pass, in order; each patch path becomes absolute before the runtime starts.
- The start-up handshake carries the working folder (
cwd), the provider, the model, and, when you set them, the reasoning effort (reasoningEffort) and the token cap (maxTokens).
- After the handshake, the client sends session prompts and listens for notifications; closing sends a shutdown request.
- At the wire level a prompt is queued on receipt, and the prompt request returns the message id of the queued message: a receipt, not the reply.
- A line on the runtime's output that is not JSON is ignored, so a stray line does not break the client.
- A notification that matches no subscription stays queued in the client instead of being dropped.
When a launch cannot start
Each failure below stops the launch, and the message names what is wrong.
- Missing runtime package. When the runtime package that matches the SDK is not installed, the launch fails with a message that says the bundled runtime cannot be located and names the runtime package to install.
- No home folder. Every launch needs a non-empty home folder, given in the client's configuration or in the environment it starts with; a launch without one is refused, and the SDK never falls back to a default home. The command the runtime package installs runs the bundled runtime directly and follows the same rule: without an explicit home it stops with exit code 2 instead of falling back.
- An incomplete runtime payload. Before the process starts, the runtime package checks its own files and names the first missing part: the runtime executable, the search helper, the process-spawn helper on macOS, or the Office sidecar and its engine.
- An unsupported system. A system with no runtime build is refused, and the message lists the supported ones: Linux x64 and arm64, macOS x64 and arm64, and Windows x64.
- An unknown runtime mode. A runtime mode the package does not recognise is refused before the process starts.
When a call fails
- Not running. Using the client while no runtime is running fails with a transport error whose text says the runtime is not running.
- A failed write. A write to the runtime's input that fails raises a transport error that says the write failed; the runtime's diagnostics come with it.
- A closed stream. A runtime that exits or closes its output fails the calls that are waiting with a transport error; the message says the runtime's output closed, or that the runtime closed, and the runtime's diagnostics come with it.
- A timeout. A call that passes its time limit fails with a timeout error that names the method and says it waited for the runtime. The start-up handshake is bounded by default (30 seconds); an ordinary run is unbounded unless you set a limit, because a turn can legitimately run long. The error carries the runtime's exit code, when the process has exited, and the tail of its error output, the newest 400 lines. A start-up timeout also names the profile the launch selected.
- A protocol error. A turn end whose reason is not a string kind fails the run:
turn/end event requires a string data.reason.kind. Run the task again; if it repeats, keep the error text for a problem report.
- Nothing is retried for you: a failed call raises in your program.
Supported systems
- Linux x64 and arm64, macOS x64 and arm64 on macOS 14 or later, and Windows x64. Windows on Arm has no wheel, and a system with no published runtime is refused rather than served by another system's build.
- Using the SDK needs no system Node.js: the bundled runtime ships inside the runtime wheel, prebuilt.
- The full matrix for the app and the native pieces is on Platform support.
Questions, answered
What is in a run result?
Five parts: the session id, the final response, the finish reason, your session's events and the notifications for your session and its helper agents. The table above lists each one.
When does the client consider a run finished?
When the session goes idle, the moment the whole session has settled. The run covers the interval from your prompt's receipt to that point.
Does one client reuse its runtime?
Yes. The runtime starts lazily on the first run that needs it and stays up for reuse until you close the client; a context manager closes it for you when the block ends. Starting again while it is up changes nothing.
On which systems can the SDK run?
Linux x64 and arm64, macOS x64 and arm64 on macOS 14 or later, and Windows x64. Windows on Arm has no wheel, and a system with no published runtime is refused rather than served by another system's build.
The launch cannot locate the runtime. What should I install?
The runtime package for your system and processor. The failure names the package to install, and the SDK package brings the matching runtime wheel with it, so a normal install covers both.
What happens when the runtime install is incomplete?
Before the process starts, the runtime package checks its own files and names the first missing part: the runtime executable, the search helper, the process-spawn helper on macOS, or the Office sidecar and its engine. Reinstall the runtime package so the full payload lands.
Why does a failed call carry the runtime's diagnostics?
So you can see why the runtime stopped answering. A transport error carries the runtime's exit code when the process has exited, and the tail of its error output, the newest 400 lines. Nothing is retried for you, so the diagnostics are the place to look.
What does a turn-end protocol error mean?
The runtime sent a turn end whose reason is not a string kind, which the protocol does not allow, so the run cannot report a finish reason. The run fails with turn/end event requires a string data.reason.kind. Run the task again; if it repeats, keep the error text for a problem report.
Do unread notifications get dropped?
No. A notification that matches no subscription stays queued in the client instead of being dropped, so a later reader can pick it up.
What happens to output lines that are not JSON?
They are ignored. A stray line on the runtime's output does not break the client, so an unexpected line cannot fail your calls.
How does the client close the runtime?
In stages: a shutdown request with a bounded wait, then the runtime's input closes, then a termination signal if the process is still up, and a forced end as a last resort. Closing before the runtime ever started does nothing, and closing again is safe.
Can I combine a configuration object with keyword options?
No. The client takes one form or the other; passing both is refused.