Skip to content

feat: provider files API - #334

Merged
cpsievert merged 14 commits into
mainfrom
worktree-file-management
Jul 29, 2026
Merged

feat: provider files API#334
cpsievert merged 14 commits into
mainfrom
worktree-file-management

Conversation

@cpsievert

@cpsievert cpsievert commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Why this matters

Today, referencing a file in a conversation (via content_pdf_file() or
content_image_file()) re-sends the file's bytes on every turn. For a large
PDF or a file you ask about repeatedly, that's wasted bandwidth and tokens on
each exchange.

This PR lets you upload a file to the provider once and reference it by id
for the rest of the conversation. The bytes travel a single time; every later
turn just points at the reference.

import chatlas as ctl

chat = ctl.ChatOpenAI()
uploaded = chat.files.upload("quarterly-report.pdf")

chat.chat(uploaded, "Summarize the key findings.")
chat.chat(uploaded, "Now list the risks mentioned.")   # no re-upload

What you get

A new chat.files accessor for the full lifecycle of provider-hosted files:

  • upload() / list() / get() / download() / delete() (each with an
    _async counterpart)
  • upload() returns a ContentUploaded reference (id, mime type, provider)
    that you pass to .chat() like any other content object
  • FileMetadata normalizes metadata across providers (id, filename,
    mime_type, size_bytes, created_at, expires_at, provider, plus
    provider-native fields in extra)

ContentUploaded can also be constructed directly to reference a file uploaded
out-of-band — e.g. a Vertex gs:// URI.

Supported providers

Works with ChatOpenAI() (Responses API), ChatAnthropic(), and
ChatGoogle() (Gemini Developer API). Everything else — OpenAI Completions and
compatible third parties, Azure OpenAI, Bedrock, Posit's Anthropic gateway, and
Vertex-backed chats — raises NotImplementedError from chat.files.*.

Notes for reviewers

  • Downloading is intentionally limited. download() targets
    model-generated files (e.g. Gemini video output). OpenAI, Anthropic, and
    Google all refuse to serve back bytes for files a caller uploaded, so
    download() on your own upload will surface a provider error. Documented in
    the guide with a callout.
  • Anthropic Files API is beta. chatlas auto-adds the
    anthropic-beta: files-api-2025-04-14 header when a turn references an
    uploaded file — no configuration needed.
  • Google specifics: files expire after 48h, aren't available on Vertex, and
    upload() blocks until Gemini finishes processing large media (video,
    audio) before returning, since the API rejects references to files that
    aren't yet ACTIVE. All covered in the docs.

See docs/get-started/files.qmd for the full guide.

@cpsievert
cpsievert force-pushed the worktree-file-management branch from 26d869a to c5a0b35 Compare July 28, 2026 17:39
@cpsievert
cpsievert marked this pull request as ready for review July 28, 2026 17:52
cpsievert added 10 commits July 28, 2026 13:33
…tion

Adds provider-side file CRUD (upload/list/get/download/delete) for
ChatOpenAI backed by the Files API, plus ContentUploaded -> input_file /
input_image serialization in as_input_param so an uploaded file can be
referenced by id in later turns instead of re-sent inline.
…ad limitation

Renames the four module-level helpers introduced for OpenAI file CRUD
(open_binary/maybe_write in _files.py, openai_uploaded/openai_meta in
_provider_openai.py) to follow the project's no-underscore-in-private-module
convention, and documents that OpenAI rejects downloading files uploaded
with purpose="user_data" on FileManager.download/download_async.
Lets users pass a chat.files.upload()'d document into ChatOpenAICompletions
(and OpenAI-compatible third parties) without re-sending the file bytes each
turn. Referencing an uploaded image raises a clear error, since the Chat
Completions image part only accepts image_url, not a file id.
Adds file upload/list/get/download/delete on ChatAnthropic (via the beta
Files API), lets an uploaded file be referenced directly in chat() without
resending bytes, and auto-injects the required anthropic-beta header on any
Messages request that references an uploaded file.
Adds provider-side file CRUD for ChatGoogle (upload/list/get/download/delete)
and lets ContentUploaded be referenced directly in chat turns via
Part.from_uri, mirroring the OpenAI/Anthropic support already in place.
Raises a clear error on Vertex AI, where the Files API isn't available.
…Posit Anthropic file management

OpenAIAzureProvider, AnthropicBedrockProvider, and PositAnthropicProvider
inherited a working file-management implementation from their parent
provider even though none of them actually support it, so chat.files.*
either made a live call or raised an unclear AttributeError instead of
NotImplementedError.
Fixes four gaps found in the final branch review: adds async lifecycle
tests (OpenAI/Anthropic/Google) alongside the sync ones so async paths
get real VCR coverage; forwards the Anthropic files-api beta header
through token_count and batch_submit so counting tokens or batching a
turn with an uploaded file doesn't silently drop the header Anthropic
requires; documents all FileManager CRUD methods so help() isn't empty;
and clarifies that a Google file with no URI yet is likely still
PROCESSING (large media uploads are async and chatlas v1 doesn't poll),
both in the raised error and in the get-started docs. Also aligns the
OpenAI-Completions cross-provider ContentUploaded error message with
its sibling providers' remediation text.
@cpsievert
cpsievert force-pushed the worktree-file-management branch from c5a0b35 to 3c5b3a7 Compare July 28, 2026 18:36
@cpsievert cpsievert changed the title feat: upload files to providers once and reference them across turns feat: server-side files API Jul 28, 2026
…out logic

Large Gemini media (video/audio) uploads previously returned before
processing finished, leaving callers with a reference that fails when used
in .chat() and no built-in way to wait for readiness. upload() now polls
until the file is ACTIVE (or raises if it FAILED), so the reference handed
back is always usable. Also extracts the repeated file-management opt-out
(Azure OpenAI, Bedrock Anthropic, Posit Anthropic) into a `no_file_management`
decorator so every current and future file_* method is covered, and tightens
OpenAI's downloadability docs to reflect that batch-purpose files remain
downloadable.
@cpsievert cpsievert changed the title feat: server-side files API feat: provider files API Jul 28, 2026
@cpsievert
cpsievert requested a review from Copilot July 28, 2026 19:13

This comment was marked as resolved.

Address Copilot review feedback: the id docstring pointed at a bare
files/abc id but Google always returns a full URI, and extra used a
shared mutable dict default instead of default_factory.
…entry

Drop the standalone get-started/files.qmd article in favor of a brief
mention in chat.qmd's existing multi-modal input section, since file
upload is really just an alternative to re-sending content_image_file()/
content_pdf_file() bytes. Expose FileManager as a reference page so the
provider-support details it already documents (Anthropic beta header,
Google expiry/Vertex, OpenAI Completions image limitation) are
discoverable from there instead. Also move the new .files changelog entry
to the top of the New features section.
@cpsievert
cpsievert merged commit 08c18c4 into main Jul 29, 2026
8 checks passed
@cpsievert
cpsievert deleted the worktree-file-management branch July 29, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants