Files
CherryHQ-cherry-studio/v2-refactor-temp/docs/file-manager/files-api-research.md
Phantom d2c568e349 feat(file): Add schema and foundation for new file module (#13451)
### What this PR does

Adds the **Phase 1a contract surface** for the file module — types, DB
schema, DataApi + File IPC contracts, FileManager skeleton, and
architecture docs.

**Phase 1b.1 (Read Path & Repository), 1b.2 (Write Path & Lifecycle),
1b.3 (Watcher & DanglingCache), and 1b.4 (OrphanSweep &
FileRefCheckerRegistry) are now all landed on top of 1a in this same
PR.** This is the complete Phase 1b runtime — reviewers see the full
read + write + watcher + orphan-sweep picture in one place.

Design, contracts, and decision rationale live in the architecture docs:

-
[`docs/references/file/architecture.md`](docs/references/file/architecture.md)
— module boundaries, type system, IPC/DataApi contracts, layered
architecture, service lifecycle, mutation propagation
-
[`docs/references/file/file-manager-architecture.md`](docs/references/file/file-manager-architecture.md)
— FileManager internals (storage, version detection, atomic writes,
reference cleanup, DirectoryWatcher, orphan sweep, DanglingCache state
machine, key design decisions)

#### Phase 1a deliverables

- Types (`FileEntry` / `FileInfo` / `FileHandle` /
`CanonicalExternalPath` brand)
- DB schema (`file_entry` + `file_ref`) with per-origin CHECK
constraints
- DataApi schemas + stub handlers
- File IPC contract (polymorphic `FileHandle` dispatch;
`batchGetMetadata` included)
- FileManager skeleton + `internal/*` + `ops/*` + `watcher/` +
`DanglingCache` + `versionCache`
- Mutation propagation design (three typed events + prefix-based
queryKey invalidation)

#### Phase 1b.1 deliverables (read-path runtime)

- Shared utilities: `getFileTypeByExt`, `sanitizeFilename`,
`validateFileName` extracted to `@shared/file/types`
- Path utilities: `canonicalizeExternalPath` (NFC + null-byte guard +
trailing-sep strip), `isPathInside`, `isUnderInternalStorage`,
`canWrite`
- FS read primitives: `stat`, `exists`, `read` (text/base64/binary
overloads), `hash` (initial MD5; swapped to xxhash-h64 in 1b.2)
- Metadata utilities: `getFileType(path)`, `isTextFile`, `mimeToExt`
- Repositories: `FileEntryService` + `FileRefService` read methods
(Drizzle-backed, Zod-branded outputs)
- Pure-function modules: `internal/content/{read,hash}`,
`internal/dispatch.ts` (FileHandle dispatcher)
- `toFileInfo(entry)` projection
- `FileManager` class as `BaseService` (`@Injectable('FileManager')`
`@ServicePhase(WhenReady)`); read methods only
- `DanglingCache` + `VersionCache` minimal viable singletons (full impls
in 1b.3 / 1b.2)
- DataApi `/files/*` read handlers fully implemented (entries / single /
ref-counts / refs-by-source)
- 60+ TDD tests (unit + boundary + setupTestDatabase integration)

#### Phase 1b.2 deliverables (write-path runtime)

- **FS atomic primitives** (open to non-file-module consumers per
architecture §5.3): `atomicWriteFile` (tmp + fsync + rename +
fsync(dir)), `atomicWriteIfUnchanged` (re-stat OCC + content-hash
fallback for second-precision mtime), `createAtomicWriteStream`
(Writable wrapper, abort/destroy unlinks tmp)
- **FS general primitives**: `write` (delegates to atomicWriteFile),
`copy` (atomic dest), `move` (rename + EXDEV → copy+unlink fallback),
`remove` (idempotent ENOENT), `mkdir` / `ensureDir` / `removeDir`,
`download` (fetch → atomic stream)
- **Hash swap**: `hash()` migrated MD5 → `xxhash-wasm` h64 streaming;
legacy `md5` dep retained for KnowledgeService loaders
- **VersionCache LRU**: capacity-bounded (default 2000) with
re-insert-on-touch recency
- **Repository mutations**: `FileEntryService.create/update/delete`
(auto UUIDv7 default id, raw DB CHECK errors propagate);
`FileRefService.create/createMany/cleanupBySource/cleanupBySourceBatch`
(`onConflictDoNothing` for batch upsert)
- **internal/entry/**: `create.createInternal` (4 source variants: bytes
/ base64 / path / url) + `create.ensureExternal` (canonicalize + stat +
idempotent upsert + duplicate-suspect peer warn);
`lifecycle.trash/restore/permanentDelete` + batch variants (DB+FS
decoupled, internal best-effort unlink); `rename` (internal DB-only,
external fs.move + canonical externalPath); `copy` (pipes through
createInternal with rollback)
- **internal/content/write.ts**: `write` / `writeIfUnchanged`
(cache-not-trusted re-stat OCC, `StaleVersionError` rewrap from
`PathStaleVersionError`) / `createWriteStream` / `*ByPath` variants
- **internal/system/**: `shell.open` / `shell.showInFolder` (electron
`shell` wrappers); `tempCopy.withTempCopy` (isolated tmp dir; cleanup on
throw)
- **FileManager facade**: every IFileManager mutation method now
delegates to its `internal/*` counterpart (`createInternalEntry` /
`ensureExternalEntry` / `batchCreate*` / `batchEnsure*` / `write` /
`writeIfUnchanged` / `createWriteStream` / `createReadStream` / `trash`
/ `restore` / `permanentDelete` + batch / `rename` / `copy` /
`withTempCopy` / `open` / `showInFolder`); no method throws
notImplemented anymore
- ~60 new TDD tests (each behavior unit = one RED→GREEN→REFACTOR
commit); end-to-end integration scenarios via `setupTestDatabase` cover
atomic-rollback zero-residue, OCC second-precision-mtime
no-false-positive, trash-external CHECK enforcement, full
create→write→read→trash→restore→permanentDelete round-trip, and external
permanentDelete-leaves-user-file-untouched

#### Phase 1b.3 deliverables (watcher + DanglingCache observability)

- **DanglingCache class** (replaces 1a const-literal skeleton):
`byEntryId: Map<entryId, CachedState>` + `pathToEntryIds:
Map<canonicalPath, Set<entryId>>` reverse index, lazy TTL expiration
(default 30 min per architecture §11.2), `forceRecheck` escape hatch,
`Emitter<DanglingStateChangedEvent>` firing only on genuine state
transitions (same-state observations are silent). Injectable `now` /
`statProbe` / `ttlMs` / `fileEntryService` seams for deterministic
tests.
- **createDirectoryWatcher** chokidar v4 wrapper: `add` / `unlink` /
`change` / `ready` / `error` events; built-in OS-junk basename ignores
(`.DS_Store` / `.localized` / `Thumbs.db` / `desktop.ini`); idempotent
`close()`. Factory auto-wires `add` →
`danglingCache.onFsEvent(path,'present')` and `unlink` → `'missing'`.
(Architecture §8.2's richer `onAddDir`/`onUnlinkDir`/`onRename` events
deferred — no consumer needs them in scope.)
- **Reverse-index maintenance from mutation flows**: `ensureExternal`
calls `addEntry` + `onFsEvent('present','ops')` on insert (no-op on
reuse); `permanentDelete(external)` calls `removeEntry`;
`rename(external)` swaps `removeEntry(oldPath) + addEntry(newPath) +
onFsEvent(newPath,'present','ops')`.
- **FileManager surface**: `getDanglingState({id})` (internal →
'present', external → cache check, unknown id → 'unknown');
`batchGetDanglingStates({ids})` (parallel fan-out, unknown ids mapped to
'unknown'); `subscribeDangling({id}, listener)` (in-process per-entry
filter; renderer fan-out via `file-manager-event` IPC channel deferred
to Phase 2).
- **FileManager.onInit**: awaits `danglingCache.initFromDb()` (populates
reverse index from non-trashed external entries; no startup stat probe
per architecture §10.6); registers `File_GetDanglingState` /
`File_BatchGetDanglingStates` IPC handlers via `this.ipcHandle`
(auto-disposed on stop).
- New `IpcChannel` constants: `File_GetDanglingState`,
`File_BatchGetDanglingStates`.
- ~30 new TDD tests across DanglingCache (18 unit) + watcher (6 real-FS)
+ FileManager integration (INT-7..INT-10).

#### Phase 1b.4 deliverables (orphan sweep + FileRefCheckerRegistry)

- **FileRefCheckerRegistry**: `Record<FileRefSourceType,
SourceTypeChecker<...>>` typed registry forces exhaustive coverage at
compile time — adding a new variant to `FileRefSourceType` without a
checker triggers a TS build error. Phase 1 ships `FileRefSourceType =
'temp_session' | 'knowledge_item'`: real DB-backed checker for
`knowledge_item` (Drizzle `inArray` against `knowledge_item`);
`temp_session` checker treats every sourceId as gone (sessions are
in-memory only). `chat_message` / `painting` / `note` are **deliberately
not in the union yet** — each will be added in lockstep (tuple entry in
`allSourceTypes` + `createRefSchema` variant + `SourceTypeChecker`) by
the PR that migrates the owning domain's DB tables to v2. Stray writes
during the migration window fail fast at `FileRefSchema.parse` rather
than being silently persisted under a no-op stub.
- **OrphanRefScanner** (RFC §6.4): `scanOneType(sourceType)` enumerates
distinct `file_ref.sourceId` per type, asks the checker which are alive,
deletes the rest via `cleanupBySourceBatch`. `scanAll()` aggregates
across every registered sourceType. Backed by new
`FileRefService.listDistinctSourceIds` to keep all SQL inside the repo.
- **Report-only orphan-entry pass** (architecture §7.1 default policy is
"preserve"): `scanOrphanEntries` groups active entries with zero
`file_ref` rows by origin. **No deletion** — surfaced via
`getOrphanReport()` for the cleanup-UI consumer. Backed by new
`FileEntryService.findUnreferenced` LEFT JOIN-based query.
- **Startup file sweep** (architecture §10): `runStartupFileSweep`
snapshots `file_entry.id` (active + trashed) into a `Set` via new
`FileEntryService.listAllIds`, walks `{userData}/files/`, plans unlink
for (a) UUID-named files whose id is not in the snapshot and (b)
`*.tmp-<UUID>` atomic-write residue. Applies the `mtime > 5min`
freshness gate (§10.3) — files newer than that are presumed in-flight
and preserved. Plan-then-execute with the `50% / 20-count-floor /
10MB-floor` safety threshold (§10.4); aborts emit
`abortReason='count-fraction'|'byte-fraction'`. Single structured
`orphan-file-sweep` log per run (info / warn / error per outcome,
§10.5).
- **DB-sweep umbrella + observability** (`runDbSweep`): runs scanAll +
scanOrphanEntries, emits one `orphan-sweep` structured record
summarising both passes; failure path returns `outcome='failed'` +
`errorMessage` so callers don't throw on background fire-and-forget.
- **FileManager integration**: `onInit` schedules a fire-and-forget
`runStartupSweeps` that runs the FS-level + DB-level sweeps in parallel;
failures of either are logged but never block ready. `getOrphanReport()`
exposes the most recent `DbSweepReport` (orphan-ref counts already
cleaned + orphan-entry counts preserved) + `lastRunAt` for the cleanup
UI surface.
- ~30 new TDD tests across registry (14 unit) + orphan sweep (16 unit +
integration) + FileManager integration (INT-11/INT-12) + repo
(`findUnreferenced`, `listAllIds`, `listDistinctSourceIds`).

**Out of scope (deferred to Phase 2)**:
- Architecture §7.2 dangling-external auto-cleanup (external + missing +
0-ref + >30d retention) — narrow extension shipping with the cleanup UI.
- Adding `chat_message` / `painting` / `note` as `FileRefSourceType`
variants (tuple entry + schema + checker added together) — gated on each
domain's v2 batch migration.
- Cleanup-UI surface that consumes `getOrphanReport()` — Phase 2
renderer work.

Renderer-side File IPC bridge for write/dangling methods stays deferred
to Phase 2 alongside the consumer-batch migrations. The Phase 1b runtime
is consumable from main-side business services through
`application.get('FileManager')`.

### Why we need it and why it was done in this way

Contract-first concentrates design review in one place; Phase 1b.x then
becomes pure "honor the contracts". Each 1b.x phase keeps strict TDD
(RED → GREEN → REFACTOR per behavior, ~one commit per cycle); each phase
ends with a verification gate (push → CI green) before the next phase
begins.

Core decisions (origin two-state, `FileEntry`/`FileInfo` split, DataApi
SQL-only, external `permanentDelete` DB-only, TTL `DanglingCache`, OCC
trust boundary, atomic write fsync default, etc.) and their rationale
are recorded in `file-manager-architecture.md §12 Key Design Decisions`
— not duplicated here.

### Breaking changes

None — purely additive (read+write paths are new, no existing callers
replaced yet).

### Special notes for your reviewer

- Review focus: contracts (1a) + read-path runtime (1b.1) + write-path
runtime (1b.2) + watcher/DanglingCache (1b.3) + orphan sweep / registry
(1b.4). Phase 1b is now complete on this branch.
- **Phase 1a contract stability policy** (architecture.md top) is
binding — any 1b.x PR that finds a contract mismatch PRs the doc
revision first.
- Deferred (Phase 2): renderer-side File IPC bridge for
write/dangling/orphan methods (alongside consumer migration); cleanup-UI
surface consuming `getOrphanReport()`; architecture §7.2
dangling-external auto-cleanup (>30d retention); adding `chat_message` /
`painting` / `note` as `FileRefSourceType` variants (each adds tuple
entry + schema + checker in lockstep, gated on the owning domain's v2
batch migration); DanglingCache periodic snapshot logger (architecture
§11.8); `listDirectory` ripgrep wrapper; `compressImage`
(KnowledgeService consumer); FileUploadService + `file_upload` table
(Vercel AI SDK Files API).

### Checklist

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: Write code that humans can understand and Keep it simple
- [x] Refactor: You have left the code cleaner than you found it (Boy
Scout Rule)
- [ ] Upgrade: N/A — purely additive
- [ ] Documentation: Internal architecture docs included
(`docs/references/file/`); no user-facing docs change
- [x] Self-review: I have reviewed my own code before requesting review
from others

### Release note

```release-note
NONE
```

---------

Signed-off-by: icarus <eurfelux@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
2026-05-13 16:06:10 +08:00

18 KiB
Raw Permalink Blame History

OpenAI / Google / Anthropic Files API 调研报告

调研时间2026-04-18 资料来源developers.openai.com、ai.google.dev、docs.claude.com经 Context7 MCP / WebFetch 检索)


核心对比一览

维度 OpenAI GoogleGemini Dev API Anthropic
状态 GA GA Betaanthropic-beta: files-api-2025-04-14
单文件上限 512 MB 2 GB 500 MB
账号/项目总容量 2.5 TB / 项目 20 GB / 项目 500 GB / 组织
生命周期 默认永久,可选 expires_after 48 小时自动删除,不可续期 永久,需手动 DELETE
存储计费 原始 Files 免费Vector Store $0.10/GB·天 全免费 全免费
引用方式 file_id / file_url / file_database64≤32 MB file_data.file_uri / inline base64 source.type="file" / base64 / url(统一 block
purpose 分类 assistants/batch/fine-tune/vision/user_data/evals 无(单一池) 无(单一池)
跨能力复用 最强Responses / Assistants / Batch / Fine-tune / Vision 共用 file_id 主要服务 generateContent Messages + Code Execution + Skills 双向闭环
特色能力 Vector Store + File Search、Batch 产出回取 视频帧率 / 时间裁剪(fps/start_offset 与 Citations + Prompt Caching 深度协同、产出可下载
音视频原生 图像为主,音视频走专用模型 完整支持MP4 / MP3 等) 未明确支持,仅 PDF / 图像 / 文本 / 代码
多云可用 N/A Vertex AI 走 GCS URI不用 Files API Bedrock / Vertex AI 不可用(仅 Anthropic 第一方)

一句话定位

  • 🤖 OpenAI——跨能力复用之王:一个 file_id 在 Responses / Batch / Fine-tune / Vision 之间打通,purpose 强制分类带来秩序感。
  • 🔷 Google——音视频之王2 GB 超大单文件 + 视频时间戳裁剪,但 48h 即焚决定了它只适合"上传即用"场景。
  • 🟣 Anthropic——工程优雅之王:统一 source 协议把 file 当作 base64 / url 的平替,与 Citations / Prompt Caching / Skills 深度联动,但仍在 beta 且不支持音视频。

选型建议

场景 推荐 理由
长期知识库 / 跨会话复用 Anthropic / OpenAI 均持久;避开 Google 的 48h 过期
视频 / 音频理解 Google 几乎是唯一选择
一份数据跑多条流水线(在线 + Batch + 微调) OpenAI purpose 体系最顺手
带高质量引用的文档问答 Anthropic document + citations.enabled + file_id 组合最舒服
预算敏感 Google / Anthropic 存储全免费OpenAI 的 Vector Store 按 GB·天收费

一、OpenAI Files API

1.1 基本端点

Base URL https://api.openai.com,需 Authorization: Bearer $OPENAI_API_KEY

操作 方法 路径
上传文件 POST /v1/filesmultipart/form-data,字段 file + purpose;可选 expires_after
列出文件 GET /v1/files(可按 purpose / limit / order / after 过滤)
获取元数据 GET /v1/files/{file_id}
下载内容 GET /v1/files/{file_id}/content
删除文件 DELETE /v1/files/{file_id}

File 对象典型字段:id / object / bytes / created_at / expires_at / filename / purpose / status / status_details

1.2 purpose 参数

  • assistants:供 Assistants APIcode_interpreterfile_search)引用。
  • batchBatch API .jsonl 输入;产出 purpose 为 batch_output
  • fine-tune:微调训练集(.jsonl,符合 chat/completion 格式)。
  • visionVision / Responses 图像输入png/jpg/gif/webp
  • user_dataResponses API 通用用户文档PDF 等),也可作为 Prompt 模板变量。
  • evalsEvals API 数据集(细节未完全确认)。
  • 只读/系统产出:batch_outputfine-tune-results(不可手动上传)。

1.3 文件限制

  • 单文件512 MB。
  • File Search 单文件 token:≤ 5,000,000 tokens。
  • 项目总容量2.5 TB组织层面无硬上限
  • 支持格式File Search 类文本文档pdf/md/docx/txt/html/代码、Visionpng/jpg/gif/webp、Batch/Fine-tunejsonl、Responses input_filePDF 等)。

1.4 生命周期

  • 默认 expires_at: null,文件永久保留,需手动 DELETE。
  • 上传时可传 expires_after(相对 created_at 的秒数 anchor到期自动删除。
  • Batch 产出文件不会自动过期,需手动清理。
  • Vector Store 是独立对象,有自己的 expiration 策略(供 File Search 使用)。

1.5 与其他能力的集成

  • Responses API主推input_file { file_id } / input_image { file_id };也支持 file_database64 ≤32 MBfile_url
  • Assistants API v2file_search 工具走 Vector Store 消费 purpose=assistants 的文件;code_interpreter 通过 message attachments 引用。v1 已弃用
  • Batch API:上传 purpose=batch.jsonl,在 /v1/batchesinput_file_id 引用,输出通过 /v1/files/{output_file_id}/content 下载。
  • Fine-tuningpurpose=fine-tune 上传训练 / 验证集。
  • Vision / 图像生成purpose=vision,可做视觉模型输入或 image edit 源图。

1.6 计费

  • Files API 原始对象存储不单独计费
  • File Search / Vector Store$0.10 / GB·天每组织前 1 GB 免费;工具调用 $2.50 / 1k calls。
  • ChatKit / Agent Kit 上传$0.10 / GB·天每账号每月首 1 GB 免费。
  • Fine-tune 训练数据训练期不单独计存储费,按 token 训练费计。

1.7 最简 SDK 示例

from openai import OpenAI
client = OpenAI()

f = client.files.create(file=open("report.pdf", "rb"), purpose="user_data")
resp = client.responses.create(
    model="gpt-5",
    input=[{"role": "user", "content": [
        {"type": "input_text", "text": "总结这份 PDF"},
        {"type": "input_file", "file_id": f.id},
    ]}],
)
print(resp.output_text)
import fs from "fs";
import OpenAI from "openai";
const openai = new OpenAI();

const f = await openai.files.create({
  file: fs.createReadStream("report.pdf"),
  purpose: "user_data",
});
const r = await openai.responses.create({
  model: "gpt-5",
  input: [{ role: "user", content: [
    { type: "input_text", text: "总结这份 PDF" },
    { type: "input_file", file_id: f.id },
  ]}],
});
console.log(r.output_text);

1.8 差异化亮点

  • 一次上传、多端复用:同一 file_id 可在 Responses / Assistants / Batch / Fine-tune / Vision 之间跨场景引用(受 purpose 约束),这是相较 Anthropic 与 Google 较少见的"长生命周期 + 跨能力"设计。
  • 三种文件传递方式并存file_idFiles API/ file_url(外链)/ file_database64 内联 ≤32 MB开发者在"持久托管"与"一次性内联"之间可灵活选择。

1.9 未确认项

  • evals purpose 的完整字段约束。
  • 各 purpose 的精细 MIME 白名单。
  • expires_after 的最大 / 最小秒数上限。

二、Google Gemini Files API

聚焦 Gemini Developer APIgenerativelanguage.googleapis.com)下的 Files service末尾对比 Vertex AI。

2.1 基本端点REST v1beta

基址:https://generativelanguage.googleapis.com

操作 方法 路径
媒体上传resumable POST /upload/v1beta/files
仅创建元数据 POST /v1beta/files
列出文件 GET /v1beta/filespageSize ≤ 100默认 10
获取文件 GET /v1beta/files/{name}
删除文件 DELETE /v1beta/files/{name}
注册 GCS 对象 POST /v1beta/files:register

文件资源字段:name / displayName / mimeType / sizeBytes / uri / statePROCESSING / ACTIVE / FAILED/ expirationTime / sha256Hash / videoMetadata

2.2 上传协议

  • Resumable upload推荐:三步协议,X-Goog-Upload-Protocol: resumable + X-Goog-Upload-Command: start/upload, finalize 头。SDK files.upload 底层即此。
  • Inlinebase64 inlineData:字节直接放进 generateContent.contents
  • 选择门槛
    • 图片:请求总大小 ≤ 20 MB 用 inline否则走 Files API。
    • PDF小文档 / 一次性用 inline较大或复用走 Files API无明确 MB 阈值)。
    • 视频:<1 分钟可 inline>100 MB 或 10 分钟以上强制 Files API。

2.3 文件限制

  • 单文件 2 GB;项目总存储 20 GB
  • PDF:单文件 ≤ 50 MB 且 ≤ 1000 页;每页 258 tokens
  • 图片 MIMEimage/pngimage/jpegimage/webpimage/heicimage/heif
  • 视频 MIMEMP4、MPEG、MOV、AVI、FLV、MPG、WebM、WMV、3GPP。
  • 音频:约 32 tokens/秒(完整 MIME 列表未完全确认)。
  • 普通 generateContent 请求总 payload ≤ 100 MB。

2.4 生命周期与 TTL

  • 48 小时自动删除:存储 48 小时后自动清理;期间可读元数据但下载受限。
  • expirationTime 字段标注过期时刻。
  • 不支持延长 / 续期。长期保存请走 GCSVertex AI或重新上传。

2.5 与 generateContent 的集成

contents.parts 中通过 file_data 引用:

{"file_data": {"mime_type": "video/mp4", "file_uri": "files/abc-123"}}

视频特殊点

  • 上传后 state 先为 PROCESSING,需轮询至 ACTIVE 才能推理。
  • videoMetadata 可传 fps / start_offset / end_offsetPrompt 内用 MM:SS 引用时间戳。
  • 默认 1 FPS 抽帧;默认分辨率约 300 tokens/秒,低分辨率 100 tokens/秒;单帧标准分辨率 258 tokens。
  • 1M context 模型可处理默认分辨率 1 小时视频,低分辨率 3 小时。

2.6 计费

  • Files API 本身免费:所有可用区域存储均不收费。
  • 输入 token 按模型实际消耗计价PDF 258 tokens/页;视频按秒;图片按分辨率 tile

2.7 最简 SDK 示例(新 SDK google-genai

from google import genai

client = genai.Client(api_key="YOUR_KEY")

my_file = client.files.upload(file="sample.pdf")

resp = client.models.generate_content(
    model="gemini-2.5-pro",
    contents=["请总结这份文档", my_file],
)
print(resp.text)

for f in client.files.list():
    print(f.name, f.state)
client.files.delete(name=my_file.name)
import { GoogleGenAI, createUserContent, createPartFromUri } from "@google/genai";

const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

const uploaded = await ai.files.upload({
  file: "sample.mp3",
  config: { mimeType: "audio/mpeg" },
});

const resp = await ai.models.generateContent({
  model: "gemini-2.5-flash",
  contents: createUserContent([
    "转写这段音频",
    createPartFromUri(uploaded.uri, uploaded.mimeType),
  ]),
});
console.log(resp.text);

旧的 google-generativeai 已进入维护模式;新项目请用 google-genaiPython/ @google/genaiNode

2.8 Vertex AI 对比

Vertex AI 没有独立 Files API,文件引用通过:

  • GCS gs:// URI(主流方式):公开可读或同项目。
  • inline base64 fileData
  • 公网 HTTP(S) URL
  • Vertex AI Studio 控制台直传(最大 7 MB

因此生产上 Vertex AI 侧的"Files"等价于 GCS 生命周期管理(用户自管 TTL、权限、计费不存在 48 小时自动过期Gemini Developer API 的 Files 则是托管式、免费但 48h 即焚的临时存储。


三、Anthropic Files API

3.1 当前状态与 Beta Header

Files API 仍处于 beta 阶段,未 GA。调用任一端点需携带

anthropic-beta: files-api-2025-04-14
anthropic-version: 2023-06-01

Messages 中引用 file_id 时同样需要此 beta header。不适用于 ZDR,且在 Amazon Bedrock / Google Vertex AI 上不可用

3.2 基本端点

方法 路径 说明
POST /v1/files 上传(multipart/form-data,字段 file
GET /v1/files 分页列出当前 workspace 的文件
GET /v1/files/{file_id} 获取元数据(id / filename / mime_type / size_bytes / created_at / type / downloadable,可选 scope
GET /v1/files/{file_id}/content 下载文件内容(仅限 Skills / Code Execution 产出;用户上传文件不可回取)
DELETE /v1/files/{file_id} 删除(不可恢复)

3.3 文件限制与 MIME

  • 单文件500 MB。
  • 组织总容量500 GB。
  • 速率限制beta 期):约 100 req/min。
  • 支持 MIME
    • application/pdfdocument block
    • text/plaindocument block
    • image/jpeg / image/png / image/gif / image/webpimage block
    • Code Execution 支持的数据集CSV / XLSX / DOCX 等)→ container_upload block
  • 音视频Files API 页未列出原生音视频类型,仅视觉与 Code Execution 场景。音视频支持未确认。
  • 不支持作为 document 直传的格式(.csv / .md / .docx / .xlsx建议先转 PDF 或纯文本。

3.4 生命周期

  • 持久存储:文件一直存在,直到显式 DELETE
  • 作用域为 API key 所属 workspace,同 workspace 其他 key 可共享。
  • 删除后极短时间内进行中的 Messages 调用仍可能可读,之后按 Anthropic 数据保留策略清除。
  • 无自动过期(与 OpenAI expires_after、Google 48h TTL 不同)。

3.5 与 Messages API 的集成

source.type = "file" 替代 base64 / url

{ "type": "document",
  "source": { "type": "file", "file_id": "file_011C..." },
  "title": "...", "context": "...",
  "citations": { "enabled": true } }
{ "type": "image",
  "source": { "type": "file", "file_id": "file_011C..." } }
  • base64:每次请求重编码、吃带宽。
  • url:需可公开访问。
  • file:只传一次、跨请求复用,天然适合 Prompt Cachingfile_id 哈希稳定,命中率高)。
  • Code Execution ToolSkills 双向:既消费 Files 作为输入,又产出 Files图表、CSV可通过 /content 下载。
  • Computer Use 不直接消费 file_id(截图走 tool_result 的 image block是否有直接消费路径未确认。

3.6 计费

  • 存储 / 上传 / 下载 / 列举 / 元数据 / 删除:全免费
  • 文件真正进入 Messages 请求时,内容按普通输入 token 计价。
  • Prompt Caching 组合:把大型 PDF / 图像放进带 cache_control 的 block配合稳定 file_id,可大幅降低重复请求的 token 成本。

3.7 最简 SDK 示例

from anthropic import Anthropic
client = Anthropic()
up = client.beta.files.upload(
    file=("doc.pdf", open("doc.pdf", "rb"), "application/pdf"),
)
resp = client.beta.messages.create(
    model="claude-opus-4-7", max_tokens=1024,
    betas=["files-api-2025-04-14"],
    messages=[{"role": "user", "content": [
        {"type": "text", "text": "总结这份文档"},
        {"type": "document", "source": {"type": "file", "file_id": up.id}},
    ]}],
)
import Anthropic, { toFile } from "@anthropic-ai/sdk";
import fs from "fs";
const anthropic = new Anthropic();
const up = await anthropic.beta.files.upload({
  file: await toFile(fs.createReadStream("doc.pdf"), undefined, { type: "application/pdf" }),
  betas: ["files-api-2025-04-14"],
});
const resp = await anthropic.beta.messages.create({
  model: "claude-opus-4-7", max_tokens: 1024, betas: ["files-api-2025-04-14"],
  messages: [{ role: "user", content: [
    { type: "text", text: "总结这份文档" },
    { type: "document", source: { type: "file", file_id: up.id } },
  ]}],
});

3.8 差异化亮点

  • 统一 content block 抽象:同一 source 协议把 base64 / url / file 视为可互换子类型,前端代码几乎无需改动即可从"嵌入式"升级为"引用式";比 OpenAI 的 file_idimage_url 混合模型更一致。
  • 与 Skills / Code Execution 的双向打通既能上传给模型读Code Execution 产出的图表 / 数据也以 file_id 回流并可 /content 下载——形成"上传 → 分析 → 产出 → 下载"闭环OpenAI 的 Code Interpreter 回取需走 assistants/thread 链路,路径更长。
  • Citations 与 Prompt Caching 协同document block 内建 citations.enabled,配合 file_id 产生稳定字符 / 页码级引用Prompt Caching 命中率相比重复 base64 显著更高。
  • 持久 + 无过期:对比 OpenAI Assistants 默认生命周期语义与 Google 的 48h 自动过期,对常驻知识库 / 跨会话引用更友好。
  • 上传 / 存储完全免费:只在推理时按 token 计费,商业模型透明。

3.9 未确认项

  • 音视频原生支持。
  • Computer Use 是否直接消费 file_id

参考来源

OpenAI

Google

Anthropic