# ファイル

Source: https://aiand-docs-staging.pages.dev/ja/api/files/

Files API を使うと、アセットを一度アップロードして `file_id` で推論リクエストから参照できます。ターンごとにバイト列 (または base64) を再送する必要がありません。[OpenAI Files API](https://platform.openai.com/docs/api-reference/files) のシェイプと完全互換で、`anthropic-version` ヘッダ付きのリクエストには [Anthropic Files API](https://docs.claude.com/en/docs/build-with-claude/files) のシェイプを返します。これにより、公式の `openai` SDK と `anthropic` SDK のどちらも拡張なしで動作します。

```http
POST   /v1/files
GET    /v1/files
GET    /v1/files/{file_id}
GET    /v1/files/{file_id}/content
DELETE /v1/files/{file_id}
```

<Aside type="note">
  v1 では `purpose: "vision"` (画像)、`purpose: "video"` (動画クリップ)、`purpose: "audio"` (音声クリップ)、`purpose: "document"` (PDF) をサポートしています。
</Aside>

## 制限

| Purpose    | 単発の `/v1/files` | [`/v1/uploads`](/ja/api/uploads/) 経由の合計 | 許可される MIME タイプ                                                                                                          |
|------------|--------------------|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
| `vision`   | 100 MiB            | 100 MiB                                     | `image/png`, `image/jpeg`, `image/webp`, `image/gif`                                                                            |
| `video`    | 100 MiB            | 8 GiB                                       | `video/mp4`, `video/webm`, `video/quicktime`                                                                                    |
| `audio`    | 100 MiB            | 1 GiB                                       | `audio/wav`, `audio/x-wav`, `audio/mpeg`, `audio/mp3`, `audio/flac`, `audio/ogg`, `audio/webm`, `audio/mp4`, `audio/x-m4a`      |
| `document` | 100 MiB            | 1 GiB                                       | `application/pdf`                                                                                                               |

動画生成は GIF を受け付けません。[`/v1/videos`](/ja/api/videos/) のフレームと参照画像は PNG、JPEG、WebP のいずれかである必要があります（`vision` はチャット用に `image/gif` も受け付けます）。

<Aside type="tip">
  100 MiB を超えるファイルには [Uploads API](/ja/api/uploads/) を使用してください — ファイルを 64 MiB 以下のパートに分割します。公式 `openai` SDK の `client.uploads.upload(...)` ヘルパーが、このチャンク分割フローを 1 回の呼び出しで実行します。
</Aside>

<Aside type="caution">
  `document` ファイルはアップロード時にページごとの画像に変換されるため、アップロード呼び出しは同期的です。PDF は 100 ページが上限で、超過した場合は `400 invalid_request_error` を返します。推論時にはモデルが 1 ページにつき 1 枚の画像を受け取るため、対象モデルには `vision` capability が必要です。
</Aside>

アップロードのデフォルト有効期限は 30 日で、ファイルオブジェクトには常に `expires_at` が含まれます。期限切れ後の参照は `410 file_expired` で失敗します。ファイルは `DELETE` によるソフト削除の対象でもあります。

## ファイルをアップロードする

```http
POST /v1/files
```

multipart フォームアップロードです。

| フィールド | 型 | 必須 | 説明 |
|-------|------|----------|-------------|
| `file` | file | はい | アセットのバイト列 |
| `purpose` | string | いいえ | `vision`、`video`、`audio`、`document` のいずれか。制限値と参照可能なモデルを決定します。省略時はファイルの MIME タイプから推測されます — `purpose` を送信しない Anthropic SDK のアップロードでもそのまま動作します |

### 使用例

<Tabs>
  <TabItem label="Python">
    ```python
    from openai import OpenAI

    client = OpenAI(
        api_key="sk-your-api-key",
        base_url="https://api.aiand.com/v1",
    )

    with open("photo.png", "rb") as f:
        file = client.files.create(file=f, purpose="vision")

    print(file.id)  # → file-abc123...
    ```
  </TabItem>
  <TabItem label="JavaScript">
    ```javascript
    import OpenAI, { toFile } from "openai";
    import fs from "node:fs";

    const client = new OpenAI({
      apiKey: "sk-your-api-key",
      baseURL: "https://api.aiand.com/v1",
    });

    const file = await client.files.create({
      file: await toFile(fs.createReadStream("photo.png"), "photo.png", {
        type: "image/png",
      }),
      purpose: "vision",
    });

    console.log(file.id); // → file-abc123...
    ```
  </TabItem>
  <TabItem label="curl">
    ```bash
    curl https://api.aiand.com/v1/files \
      -H "Authorization: Bearer sk-your-api-key" \
      -F "file=@photo.png" \
      -F "purpose=vision"
    ```
  </TabItem>
</Tabs>

MIME タイプは multipart パートの `Content-Type` から判定されます。openai-node は `fs.ReadStream` をそのまま渡すと `application/octet-stream` として送信し、どの purpose でも拒否されるため、`toFile` で明示的に `type` を指定してください。

### レスポンス

```json
{
  "id": "file-abc123def456",
  "object": "file",
  "bytes": 482301,
  "purpose": "vision",
  "filename": "photo.png",
  "created_at": 1719450000,
  "expires_at": 1722042000
}
```

| フィールド | 型 | 説明 |
|-------|------|-------------|
| `id` | string | `file-` プレフィックス付きの安定 ID。チャット補完のコンテンツパートで使用します |
| `object` | string | 常に `"file"` |
| `bytes` | integer | ファイルサイズ (バイト) |
| `purpose` | string | アップロード時に指定した purpose |
| `filename` | string \| null | 提供された場合は元のファイル名 |
| `created_at` | integer | Unix タイムスタンプ |
| `expires_at` | integer \| null | Unix タイムスタンプ。デフォルトはアップロードから 30 日後 |
| `page_count` | integer | `document` アップロードでのみ存在 — PDF のページ数 |

## チャット補完でファイルを参照する

ユーザーメッセージ内で `type: "file"` のコンテンツパートを使用します。クライアントが送信するのは ID のみで、バイト列が再度送信されることはありません。

```json
{
  "model": "<vision-capable-model>",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "What's in this image?" },
      { "type": "file", "file": { "file_id": "file-abc123def456" } }
    ]
  }]
}
```

<Tabs>
  <TabItem label="Python">
    ```python
    completion = client.chat.completions.create(
        model="<vision-capable-model>",
        messages=[{
            "role": "user",
            "content": [
                {"type": "text", "text": "What's in this image?"},
                {"type": "file", "file": {"file_id": file.id}},
            ],
        }],
    )

    print(completion.choices[0].message.content)
    ```
  </TabItem>
  <TabItem label="JavaScript">
    ```javascript
    const completion = await client.chat.completions.create({
      model: "<vision-capable-model>",
      messages: [{
        role: "user",
        content: [
          { type: "text", text: "What's in this image?" },
          { type: "file", file: { file_id: file.id } },
        ],
      }],
    });

    console.log(completion.choices[0].message.content);
    ```
  </TabItem>
</Tabs>

<Aside type="caution">
  モデルはファイルの purpose に対応する capability を持つ必要があります。`purpose: "vision"` には `vision` 対応モデル、`purpose: "video"` には `video` 対応モデル、`purpose: "audio"` には `audio` 対応モデル、`purpose: "document"` には `vision` 対応モデル (モデルは 1 ページにつき 1 枚の画像を受け取ります) が必要です。条件を満たさない場合、`400 invalid_request_error` が返ります。各モデルの capability は [`GET /v1/models`](/ja/api/models/) を参照してください。
</Aside>

### 動画の例

```json
{
  "model": "<video-capable-model>",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "Describe what happens in this clip." },
      { "type": "file", "file": { "file_id": "file-vid789..." } }
    ]
  }]
}
```

ワイヤーフォーマットは画像と同じです — 送信するのは常に `type: "file"` で、ファイルの `purpose` がモデルへの受け渡し方を決定します。

### 音声の例

```json
{
  "model": "<audio-capable-model>",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "Transcribe and summarize this clip." },
      { "type": "file", "file": { "file_id": "file-aud456..." } }
    ]
  }]
}
```

シェイプは同じです。音声をアップロードせずインラインで送る場合は、OpenAI シェイプの `{ "type": "input_audio", "input_audio": { "data": "<base64>", "format": "wav" } }` パートを直接送信することもできます。

### ドキュメント (PDF) の例

```json
{
  "model": "<vision-capable-model>",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "Summarize this paper." },
      { "type": "file", "file": { "file_id": "file-doc456..." } }
    ]
  }]
}
```

ワイヤーフォーマットは同じ `type: "file"` 参照です。モデルは 1 ページにつき 1 枚の画像を受け取るため、`vision` capability が必要です。ページ数は `GET /v1/files/{file_id}` のレスポンスの `page_count` で確認できます。

PDF の処理はアップロード時に行われるため、アップロード済みのドキュメントであれば、推論レイテンシはそのサイズの影響を受けません。リクエストにインラインで含めた PDF は、それを含む最初のリクエストで同じように処理・保存され、この一覧に表示されます（[ゼロデータリテンション](/ja/organizations/zero-data-retention/)の組織を除く）。[Chat Completions](/ja/api/chat-completions/) を参照してください。

## ファイル一覧

```http
GET /v1/files
```

| クエリパラメータ | 型 | 説明 |
|-------------|------|-------------|
| `purpose` | string | purpose で絞り込み |
| `limit` | integer | ページサイズ (1〜100、デフォルト 20) |
| `after` | string | カーソル: 前ページの最後のアイテムの `id` を指定 |

ファイルは新しい順 (逆時系列) で返されます。

### 使用例

```bash
curl "https://api.aiand.com/v1/files?purpose=vision&limit=50" \
  -H "Authorization: Bearer sk-your-api-key"
```

### レスポンス

```json
{
  "object": "list",
  "data": [
    { "id": "file-abc...", "object": "file", "bytes": 482301, "purpose": "vision", "filename": "photo.png", "created_at": 1719450000, "expires_at": 1722042000 }
  ],
  "has_more": false,
  "first_id": "file-abc...",
  "last_id": "file-abc..."
}
```

`has_more: true` のときは、次のリクエストの `after` に `last_id` を指定します。SDK のページネーション (`client.files.list().auto_paging_iter()`) は自動的に動作します。

## ファイルメタデータを取得する

```http
GET /v1/files/{file_id}
```

アップロードレスポンスと同じシェイプを返します。

```bash
curl https://api.aiand.com/v1/files/file-abc123 \
  -H "Authorization: Bearer sk-your-api-key"
```

## ファイルコンテンツをダウンロードする

```http
GET /v1/files/{file_id}/content
```

生のバイト列を返します。`Content-Type` と `Content-Disposition` ヘッダはアップロード時のメタデータから設定されます。

<Tabs>
  <TabItem label="Python">
    ```python
    content = client.files.content("file-abc123")
    with open("downloaded.png", "wb") as f:
        f.write(content.read())
    ```
  </TabItem>
  <TabItem label="curl">
    ```bash
    curl https://api.aiand.com/v1/files/file-abc123/content \
      -H "Authorization: Bearer sk-your-api-key" \
      -o downloaded.png
    ```
  </TabItem>
</Tabs>

## ファイルを削除する

```http
DELETE /v1/files/{file_id}
```

ファイルを削除します。以降、その ID を参照する推論リクエストは失敗します。

```bash
curl -X DELETE https://api.aiand.com/v1/files/file-abc123 \
  -H "Authorization: Bearer sk-your-api-key"
```

### レスポンス

```json
{
  "id": "file-abc123",
  "object": "file",
  "deleted": true
}
```

ファイルが存在しない、または既に削除されている場合は `404` を返します (冪等な動作)。

## エラー

| ステータス | `error.code` | 発生条件 |
|--------|--------------|------|
| `400` | `invalid_request_error` | `file`/`purpose` の欠落、未知の `purpose`、許可外の mime、ファイルサイズの上限超過 |
| `400` | `model_capability_mismatch` | チャット補完が、モデルに無い capability を必要とする purpose のファイルを参照 |
| `401` | `invalid_api_key` | `Authorization` ヘッダが欠落または無効 |
| `403` | `zero_data_retention` | 組織が[ゼロデータリテンション](/ja/organizations/zero-data-retention/)のため、アップロードできない |
| `404` | `file_not_found` | 未知の ID、または別の組織に属する ID |
| `410` | `file_expired` | ファイルが `expires_at` を過ぎている |
| `500` | `server_error` | 内部エラー |

## 注意事項

- ファイル ID はアップロード元の **組織** にスコープされます。組織をまたいだ参照は `404` を返します。
- 単発の小さな画像であれば、標準の `{type: "image_url", image_url: {url: "data:..."}}` コンテンツパートで base64 をインラインで送ることも可能です。ただしマルチターン会話や大きなアセットでは「一度アップロードしてから参照する」方式の方が効率的です。
