Attachment storage-offload rail: a registry of storage BACKENDS plus the
pure OFFLOAD DECISION that routes one attachment's payload either INLINE
(bytes in the session_attachment.bytes BLOB) or EXTERNAL (bytes handed to
a backend, which returns a storage_uri -- scheme://... -- kept in the row
instead).
Zero SQL, zero schema change: the V4 session_attachment table already
carries a nullable storage_uri with an exactly-one(bytes, storage_uri)
CHECK. This namespace only decides WHICH of the two a given attachment takes,
PUTs/GETs the external bytes through the scheme-dispatched backend, and
hydrates a read-back envelope's :base64 from its :storage-uri on demand.
The decision is a PURE predicate hot? AND size (see default-offload?):
an image replays to a vision model every turn its iteration stays live, so it
is HOT -- kept inline even when large; a non-image artifact (PDF/CSV/wav/
download) is fetched at most once by a human, so it is COLD -- a good offload
candidate past a size floor. A backend may override the predicate wholesale
via :storage/offload?.
Precedence, engine-owned so the loop never learns a storage dialect:
:storage/offload? (the backend knows its own cost)default-offload? (engine default policy)Attachment storage-offload rail: a registry of storage BACKENDS plus the pure OFFLOAD DECISION that routes one attachment's payload either INLINE (bytes in the `session_attachment.bytes` BLOB) or EXTERNAL (bytes handed to a backend, which returns a `storage_uri` -- `scheme://...` -- kept in the row instead). Zero SQL, zero schema change: the V4 `session_attachment` table already carries a nullable `storage_uri` with an exactly-one(bytes, storage_uri) CHECK. This namespace only decides WHICH of the two a given attachment takes, PUTs/GETs the external bytes through the scheme-dispatched backend, and hydrates a read-back envelope's `:base64` from its `:storage-uri` on demand. The decision is a PURE predicate `hot? AND size` (see `default-offload?`): an image replays to a vision model every turn its iteration stays live, so it is HOT -- kept inline even when large; a non-image artifact (PDF/CSV/wav/ download) is fetched at most once by a human, so it is COLD -- a good offload candidate past a size floor. A backend may override the predicate wholesale via `:storage/offload?`. Precedence, engine-owned so the loop never learns a storage dialect: 1. active backend's `:storage/offload?` (the backend knows its own cost) 2. else `default-offload?` (engine default policy) 3. no active backend -> always inline (zero regression)
(active-backend)Highest-priority registered backend, or nil when none. Nil means every attachment stays inline -- the zero-backend behaviour, unchanged.
Highest-priority registered backend, or nil when none. Nil means every attachment stays inline -- the zero-backend behaviour, unchanged.
(attachment-backend backend)Validate + normalize a storage backend descriptor. Returns the descriptor
with :storage/priority coerced to a long (default 0).
Required keys:
:storage/id keyword identity, e.g. :file
:storage/scheme the URI scheme string it owns, e.g. "file" -- the GET
dispatch key (a storage_uri is "<scheme>://...")
:storage/put-fn ({:bytes ^bytes :media-type :kind :filename :id}
-> uri-string) writes the bytes, returns the handle
:storage/get-fn (uri-string -> ^bytes | nil) reads the bytes back
Optional:
:storage/offload? ({:size :media-type :kind :tool-call-id} -> boolean)
overrides default-offload? when present
:storage/priority long; higher wins when several are registered (default 0)
Validate + normalize a storage backend descriptor. Returns the descriptor
with `:storage/priority` coerced to a long (default 0).
Required keys:
:storage/id keyword identity, e.g. `:file`
:storage/scheme the URI scheme string it owns, e.g. "file" -- the GET
dispatch key (a `storage_uri` is `"<scheme>://..."`)
:storage/put-fn ({:bytes ^bytes :media-type :kind :filename :id}
-> uri-string) writes the bytes, returns the handle
:storage/get-fn (uri-string -> ^bytes | nil) reads the bytes back
Optional:
:storage/offload? ({:size :media-type :kind :tool-call-id} -> boolean)
overrides `default-offload?` when present
:storage/priority long; higher wins when several are registered (default 0)Size floor below which the engine default keeps an artifact inline (256 KiB). Small payloads never earn an external round-trip.
Size floor below which the engine default keeps an artifact inline (256 KiB). Small payloads never earn an external round-trip.
(default-offload? {:keys [size media-type]})Engine default offload predicate: offload a COLD artifact past the size floor. Images are HOT (replayed to a vision model every turn their iteration is live) so they NEVER offload by default, regardless of size -- otherwise each turn would re-fetch the bytes. Pure; safe to call on any attachment map.
Engine default offload predicate: offload a COLD artifact past the size floor. Images are HOT (replayed to a vision model every turn their iteration is live) so they NEVER offload by default, regardless of size -- otherwise each turn would re-fetch the bytes. Pure; safe to call on any attachment map.
(file-backend {:keys [dir id priority offload?] :or {id :file priority 0}})Reference storage backend that offloads bytes to a local DIRECTORY, one file
per artifact, addressed by a file://<absolute-path> URI. A genuinely useful
default -- offload big COLD artifacts to disk instead of bloating the DB --
AND the reference implementation the registry contract is exercised against.
NOT auto-registered: an operator or extension opts in with
(register-backend! (file-backend {:dir "/var/vis/attachments"})).
Opts: :dir (required) target directory (created if absent); :id backend
id (default :file); :priority (default 0); :offload? an optional
predicate override (nil -> the engine default-offload?). Each PUT mints its
own opaque filename, so the storage key is independent of the DB row id.
Reference storage backend that offloads bytes to a local DIRECTORY, one file
per artifact, addressed by a `file://<absolute-path>` URI. A genuinely useful
default -- offload big COLD artifacts to disk instead of bloating the DB --
AND the reference implementation the registry contract is exercised against.
NOT auto-registered: an operator or extension opts in with
`(register-backend! (file-backend {:dir "/var/vis/attachments"}))`.
Opts: `:dir` (required) target directory (created if absent); `:id` backend
id (default `:file`); `:priority` (default 0); `:offload?` an optional
predicate override (nil -> the engine `default-offload?`). Each PUT mints its
own opaque filename, so the storage key is independent of the DB row id.(hydrate att)Read-back envelope -> same envelope with :base64 populated. A row stored
INLINE already carries :base64 (returned untouched). A row stored EXTERNAL
carries only :storage-uri, so its bytes are fetched via resolve-bytes and
base64-encoded into :base64. Left alone when it already has :base64, has
no :storage-uri, or the fetch fails (the caller still sees :storage-uri).
Read-back envelope -> same envelope with `:base64` populated. A row stored INLINE already carries `:base64` (returned untouched). A row stored EXTERNAL carries only `:storage-uri`, so its bytes are fetched via `resolve-bytes` and base64-encoded into `:base64`. Left alone when it already has `:base64`, has no `:storage-uri`, or the fetch fails (the caller still sees `:storage-uri`).
(hydrate-all atts)Map hydrate over a read-back attachment seq (nil-safe).
Map `hydrate` over a read-back attachment seq (nil-safe).
(offload-attachment att)Route ONE store-bound attachment map. An inline candidate carries :base64.
When a backend is active AND offload? says so, decode + PUT the bytes and
return the map with :storage-uri set (and :size fixed to the real byte
count) and :base64 DROPPED; otherwise the map is returned unchanged. NEVER
throws -- a decode/PUT failure or a non-string URI falls back to inline.
Route ONE store-bound attachment map. An inline candidate carries `:base64`. When a backend is active AND `offload?` says so, decode + PUT the bytes and return the map with `:storage-uri` set (and `:size` fixed to the real byte count) and `:base64` DROPPED; otherwise the map is returned unchanged. NEVER throws -- a decode/PUT failure or a non-string URI falls back to inline.
(offload-attachments atts)Map offload-attachment over a store-bound attachment seq. The value handed
to db-store-iteration! / db-store-session-turn! :attachments; the
in-memory replay copy stays inline (offload only the persisted path).
Map `offload-attachment` over a store-bound attachment seq. The value handed to `db-store-iteration!` / `db-store-session-turn!` `:attachments`; the in-memory replay copy stays inline (offload only the persisted path).
(offload? backend att)Decide inline (false) vs external (true) for ONE attachment, given backend
(typically active-backend). Precedence: the backend's :storage/offload?
when present, else default-offload?. Nil backend -> false. Never throws --
a predicate that blows up falls back to inline.
Decide inline (false) vs external (true) for ONE attachment, given `backend` (typically `active-backend`). Precedence: the backend's `:storage/offload?` when present, else `default-offload?`. Nil backend -> false. Never throws -- a predicate that blows up falls back to inline.
(register-backend! backend)Register a storage backend. Idempotent by :storage/id. Returns the
normalized descriptor.
Register a storage backend. Idempotent by `:storage/id`. Returns the normalized descriptor.
(registered-backends)Registered storage backends ordered by descending priority (id breaks ties).
Registered storage backends ordered by descending priority (id breaks ties).
(resolve-bytes storage-uri)Fetch the raw bytes for an external storage-uri by dispatching on its
scheme to the owning backend's :storage/get-fn. nil when no backend owns
the scheme, or the GET fails / returns nil.
Fetch the raw bytes for an external `storage-uri` by dispatching on its scheme to the owning backend's `:storage/get-fn`. nil when no backend owns the scheme, or the GET fails / returns nil.
(scheme-of uri)URI scheme of a storage-uri ("<scheme>://..."), or nil.
URI scheme of a `storage-uri` (`"<scheme>://..."`), or nil.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |