# AICU STT API Skill

> 音声ファイルを文字起こし（Speech-to-Text）。OpenAI Whisper API 互換。`verbose_json` でセグメント単位のタイムスタンプ、`srt` / `vtt` で字幕をそのまま返す。

## Package Info

- **Name**: aicu-stt
- **Version**: 0.2.0 (Alpha)
- **Base URL**: https://api.aicu.ai/v1
- **Endpoint**: `POST /v1/audio/transcriptions`（別名 `POST /v1/tts/transcriptions`）
- **Auth**: APIキー必須 (Bearer Token)・スコープ `tts`
- **Model**: `whisper-large-v3-turbo`（さくらの AI Engine）

## Quick Start

```bash
# 1) テキストだけ
curl -X POST https://api.aicu.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer aicu_live_xxx" \
  -F file=@meeting.mp3 -F language=ja
# → {"text": "..."}

# 2) セグメント単位のタイムスタンプ（字幕・議事録向け）
curl -X POST https://api.aicu.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer aicu_live_xxx" \
  -F file=@meeting.mp3 -F language=ja \
  -F response_format=verbose_json -F "timestamp_granularities[]=segment"
# → {"task":"transcribe","language":"ja","duration":60.0,"text":"...","segments":[{"id":0,"start":0.0,"end":4.32,"text":"..."}]}

# 3) 字幕ファイルを直接
curl -X POST https://api.aicu.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer aicu_live_xxx" \
  -F file=@meeting.mp3 -F language=ja -F response_format=srt -o meeting.srt
```

## Parameters (multipart/form-data)

| Name | Type | Required | Description |
|------|------|----------|-------------|
| file | file | Yes | 音声ファイル。**1リクエスト最大 30MB / 30分**（超過は 413） |
| model | string | No | `whisper-large-v3-turbo`（既定・現状これのみ） |
| language | string | No | ISO-639-1（`ja` / `en` …）。指定すると精度と速度が上がる |
| prompt | string | No | 固有名詞・専門用語のヒント（例: "AICU, ComfyUI, しらいはかせ"）。誤変換対策に効く |
| response_format | string | No | `json`（既定）/ `verbose_json` / `text` / `srt` / `vtt` |
| timestamp_granularities[] | string[] | No | `segment` / `word`。**`response_format=verbose_json` が必要** |
| temperature | number | No | 0〜1（既定 0） |

`response_format` と `timestamp_granularities[]` はバックエンドへそのまま透過します（ゲートウェイでは落としません）。バックエンドが対応しない組み合わせはバックエンドの 4xx がそのまま返ります。

## Limits

| 項目 | 値 |
|---|---|
| ファイルサイズ | 30 MB / リクエスト |
| 音声長 | 30 分 / リクエスト |
| ゲートウェイのレート制限 | 60 リクエスト / 分 / キー（`X-RateLimit-*` ヘッダで残枠を通知） |
| バックエンドの枠 | 別途あり（超過時は **HTTP 429 + Retry-After**） |
| 入力形式 | mp3 / m4a / wav / ogg / flac / webm など一般的な音声コンテナ。**動画（mp4）は音声だけ抜いてから**送る |

**長い収録の送り方**: 30分ごとに分割する（65分 → 3リクエスト）。60秒刻みのような細切れは、リクエスト数だけ増えてレート制限に当たりやすくなります。

```bash
# 3GB の mp4 → 音声のみ mono 16kHz mp3（65分で約 8MB）→ 30分ごとに分割
ffmpeg -y -i recording.mp4 -vn -map 0:a:0 -ac 1 -ar 16000 -b:a 64k audio.mp3
ffmpeg -y -i audio.mp3 -f segment -segment_time 1800 -c copy part_%02d.mp3
```

## Response Headers

- `X-AICU-Model`: 使用モデル
- `X-AICU-Provider`: `sakura`
- `X-AICU-AP-Cost`: 消費 AP
- `X-AICU-Request-Id`: リクエスト ID（問い合わせ時に添えてください）
- `X-AICU-Latency-Ms`: 処理時間
- `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset`: ゲートウェイの残枠
- `Retry-After`: 429 のときの待ち秒数

## Errors

HTTP ステータスをそのまま返します（本文に 429 を包んで 200 で返すことはしません）。`curl -f` / `raise_for_status()` / SDK のリトライがそのまま効きます。

| HTTP | `error.type` | 意味 |
|---|---|---|
| 400 | invalid_request_error | パラメータ不正（未知の response_format 等） |
| 401 / 403 | — | キー無効 / スコープ `tts` なし |
| 402 | insufficient_credits | AP 残高不足 |
| 413 | invalid_request_error (`file_too_large`) | 30MB 超 |
| 429 | rate_limit_error | レート制限。**`Retry-After` 秒待って再送** |
| 502 | api_error | バックエンド障害（`upstream_status` に上流のステータス） |

```json
{"error":{"message":"rate limit exceeded","type":"rate_limit_error","code":"upstream_rate_limited","upstream_status":429,"hint":"..."}}
```

## Pricing

**音声 10 秒 = 3 AP**（10 秒単位で切り上げ・2026-09-09 から）。1 時間 ≈ 1,080 AP ≈ $0.11。改定は [News](https://api.aicu.ai/news) に載ります。
`verbose_json` のときは実測の `duration` で、それ以外の形式では文字数から概算（3文字≈1秒）で課金します。同じ課金単位は `X-AICU-AP-Cost` で毎回返ります。1 USD = 10,000 AP。

## Python (OpenAI SDK)

```python
from openai import OpenAI
client = OpenAI(base_url="https://api.aicu.ai/v1", api_key="aicu_live_xxx", max_retries=5)  # 429 は SDK が Retry-After を見て再送

with open("part_00.mp3", "rb") as f:
    tr = client.audio.transcriptions.create(
        model="whisper-large-v3-turbo", file=f, language="ja",
        response_format="verbose_json", timestamp_granularities=["segment"],
        prompt="AICU, ComfyUI, しらいはかせ",
    )
for seg in tr.segments:
    print(f"{seg.start:8.2f} --> {seg.end:8.2f}  {seg.text}")
```

## Python (requests・素の HTTP)

```python
import requests, time
def transcribe(path, key, **fields):
    for attempt in range(6):
        with open(path, "rb") as f:
            r = requests.post("https://api.aicu.ai/v1/audio/transcriptions",
                              headers={"Authorization": f"Bearer {key}"},
                              files={"file": f}, data={"language": "ja", **fields})
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "60")))
            continue
        r.raise_for_status()
        return r
    raise RuntimeError("rate limited too many times")
```

## Related

- TTS（読み上げ）: https://api.aicu.ai/skills/tts
- LLM: https://api.aicu.ai/skills/llm
- ドキュメント: https://api.aicu.ai/docs/audio-api
- ダッシュボード（キー発行・残高）: https://api.aicu.ai/dashboard

---
*AICU Inc.*
