Caches provider file uploads so the same bytes are not uploaded twice.
Uploading is slow and, on some providers, billed. When the same asset is
attached across several turns of a conversation - or across conversations -
the second and later sends should reuse the first upload's
ExAgent.FileRef.
Entries are keyed by {scope, sha256(bytes)}, where the scope digests the
provider module, base URL, and API key. Scoping by API key matters: two
provider structs for different accounts must never share a file_id, because
one account's file reference is invalid - or worse, readable - for the other.
The API key itself is never stored, only its digest.
Expiry
fetch/2 treats an entry whose ExAgent.FileRef has expired as a miss and
evicts it, so the caller uploads afresh.
Growth
There is no eviction beyond expiry. Entries are small (a FileRef struct and
two digests), but an application uploading a large number of distinct files
will grow the table without bound, since OpenAI file references never expire.
Call clear/0 if that matters for your workload.
The table is public and named so that concurrent agents read it directly rather than serializing behind this process; the GenServer exists only to own the table across crashes.
Summary
Functions
Returns a specification to start this module under a supervisor.
Removes every cached upload.
Looks up a cached ExAgent.FileRef for these bytes.
Stores an ExAgent.FileRef for these bytes, replacing any existing entry.
Builds the cache scope for a provider instance.
Returns the number of cached entries, including any not yet evicted expired ones.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec clear() :: :ok
Removes every cached upload.
@spec fetch(binary(), binary()) :: {:ok, ExAgent.FileRef.t()} | :miss
Looks up a cached ExAgent.FileRef for these bytes.
Returns :miss when nothing is cached or the cached reference has expired.
@spec put(binary(), binary(), ExAgent.FileRef.t()) :: :ok
Stores an ExAgent.FileRef for these bytes, replacing any existing entry.
Builds the cache scope for a provider instance.
Two provider structs share a scope only when their module, base URL, and API key all match. The API key is digested, never retained.
@spec size() :: non_neg_integer()
Returns the number of cached entries, including any not yet evicted expired ones.