ExAgent.UploadCache (ExAgent v0.4.1)

Copy Markdown View Source

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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear()

@spec clear() :: :ok

Removes every cached upload.

fetch(scope, bytes)

@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.

put(scope, bytes, ref)

@spec put(binary(), binary(), ExAgent.FileRef.t()) :: :ok

Stores an ExAgent.FileRef for these bytes, replacing any existing entry.

scope(provider_module, base_url, api_key)

@spec scope(module(), String.t(), String.t() | nil) :: binary()

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.

size()

@spec size() :: non_neg_integer()

Returns the number of cached entries, including any not yet evicted expired ones.