The Oda web transport
When you work in the DataMind Oda web interface, the page and the DataMind Oda that serves it on this computer are in constant contact: everything the page asks for as data goes through one request layer, and a chat's live view is kept in step by frames. This page is the reference for both, from the shape of a single call to the frames of a chat. For the web app itself, see The Oda web app; for the messages a failed request can show, see Connection and upload messages.
The shape of a request
- One prefix carries every request. Everything the page asks for as data goes under a single prefix of the interface's address.
- A request is one JSON message. It states that it is a request, carries a call id, names the method it calls, and carries the payload.
- The call id ties the pair. The page mints the call id when it sends a call, and the answer must echo the same one; an answer with a different call id is discarded rather than used.
- The method must match its target. A request names the method it calls and is sent to the matching target; one that disagrees is refused as a bad request.
- Targets are checked first. A target is made of plain name segments; empty,
. and .. parts are refused before anything runs.
- Invalid requests stop before any handler. A body with the wrong media type, text that is not JSON, or a message that does not fit the agreed request shape is refused as a bad request, and each refusal has its own message. The messages are listed in Connection and upload messages.
How a request finds its handler
- Two checks come first. Every request passes the interface's address check and the browser sign-in before anything handles it; a request that fails either is refused before any part of the interface sees it. Both checks are covered in Local access and trusted hosts.
- The interface's parts get their say. Once admitted, a request passes one step where the running parts of the interface can hold it back or refuse it; while an update is being installed, new requests wait here until it finishes.
- Dispatch follows one order. A request that matches a registered route is handled by that route; if none matches, a part of the interface that has claimed requests under the prefix may take it; if nothing does, the request is answered
not found.
- Routes support ordinary fetching methods. Registered routes answer
GET, HEAD and POST, and a route belongs to the part that registered it: it exists while that part runs, and two claims on the same route are refused instead of being settled quietly.
How a body travels
- A route's way of taking its body is settled before any bytes are read. A route reads its body in full, or takes it as a stream.
- A body that is read in full waits in memory, under the limit. It is held in memory, bounded by the request size limit; the limit and its default are in Connection settings and defaults.
- A streamed body flows as it arrives. A route that takes raw bytes, such as the upload path, receives them while they arrive, and a streaming request is not counted against the aggregate limit. File uploads in the web app follows that path.
- An oversize request is refused and the connection closes. A request that declares more than the limit is refused with
413 before any of it is read, and one that turns out to be over the limit while arriving is refused the same way; neither reaches a handler.
- A slow reader slows the sender. Streamed bodies and answers are both written with backpressure, so a reader that falls behind slows the other side instead of letting bytes queue up.
- When reading stops, so does the rest. If a handler stops reading a streamed body, the connection is marked to close and the rest is discarded; if the page goes away mid-answer, the write stops there.
What an answer carries
- One message, in the agreed shapes. An answer states that it is a response, echoes the call id of its request, and carries the result of the call or the failure.
- JSON by default. A response with no bytes is plain JSON, and a failure stays JSON too.
- Multipart when bytes ride along. A response that carries bytes arrives as a multipart response: one part named
metadata carries the message, and the other parts carry the bytes. Binary attachments and downloads is the reference for that framing.
- A reply is checked like a request. An answer that does not fit the agreed response shape, or that carries a different call id, is discarded rather than used.
What the page fetches
- Pictures in a chat. When a chat shows a picture, the page fetches its bytes through the request layer, and they arrive as a multipart result.
- Office previews. Turning an Office document into something the page can show is a request too: the document is converted to a PDF, and the PDF's bytes arrive as a multipart result.
- A closing terminal. Closing a terminal is one call that answers with a small success result, and the terminal list is cleared.
The frames of a chat
A chat's live side is kept in step by frames, which arrive over the interface's own live channel.
- A turn has a small vocabulary. A working turn produces one
turn/start, an echo of the message you sent, the final assistant message, and one turn/end; a turn that finishes normally ends with reason completed.
- Tools add two frames. A turn that calls tools adds a
tool/call frame and a tool/result frame.
- A follow view brings a chat up to date. When the page needs a chat's current state, it asks for a follow view, and the answer is a
session/follow series of frames, beginning with a request/header frame whose reason is initial.
Limits of the request layer
- No upgrade routes of its own. The layer registers ordinary addresses only; the page's live channel is set up separately from it.
- No events over calls. A call carries one request and one answer; live updates do not travel inside it.
- No binary parameters. What a request carries is JSON; bytes ride back in answers, not in what you send.
- No streamed binary answers. Bytes come back whole, inside the multipart framing, rather than as a stream you read.
Questions, answered
How can I tell which answer belongs to which request?
Every request carries a call id that the page mints, and its answer must echo the same one. An answer with a different call id is discarded rather than used.
What does a call get back when it fails?
The answer still comes: it carries the failure in place of a result, with the message to show. The messages the interface can show are listed in Connection and upload messages.
Why is a request answered not found?
Nothing claimed it: it matched none of the interface's registered routes, and no part of the interface took it under its prefix. Check the target the request names, then send it again.
What happens to a request while an update is being installed?
It waits. While an update is being installed, new requests are held back rather than handled, and they continue once the installation finishes.
Is every request body read in full?
No. An ordinary body is read in full, under the size limit, before its handler runs. A route that takes raw bytes, such as the upload path, receives them as a stream while they arrive and is not counted against the aggregate limit. File uploads in the web app follows that path.
Does an oversize request ever reach its handler?
No. A request over the size limit is refused with 413 and the connection is closed; nothing reaches a handler. The limit and its default are in Connection settings and defaults.
When does an answer arrive as multipart instead of JSON?
When it carries bytes. A response with no bytes is plain JSON; one with bytes arrives as a multipart response whose parts carry the message and the bytes, described in Binary attachments and downloads.
How does the page follow a chat while a turn runs?
Through frames on the interface's own live channel: one turn/start for the turn, an echo of the message you sent, the final assistant message, and one turn/end when the turn finishes; a turn that used tools adds tool/call and tool/result. A full current view arrives as a session/follow series.
Who can answer a request under the interface's prefix?
The parts of the interface register the routes they own while they run: a route lives as long as its part does, and two claims on the same route are refused rather than settled quietly.
Can I cancel a request I sent?
A caller can cancel a call at any point, including while the answer is being read. The call then fails rather than delivering, and bytes that arrive after the cancellation are not used.