1. はじめに
前回の記事では、自作プロキシを用意して Codex CLI から複数のモデル(OpenAI, Ollama, gpt-oss, 社内モデルなど)を統合的に扱う方法を解説しました。
本記事ではその拡張編として、ストリーミング対応(stream: true
) と リトライ/エラーハンドリング戦略 を詳しく紹介します。これにより、より実用的で安定したプロキシを構築できます。
2. ストリーミング対応の必要性
なぜ必要か
- Codex CLIはOpenAI互換APIを利用するため、
stream: true
を指定するとサーバーから部分的にレスポンスが返される - 大規模テキスト出力時に全体を待たずに結果を受け取れる
- 対話的な利用体験を向上させる
実装のポイント
- HTTPサーバーは「逐次レスポンス」を返せる仕組みが必要
- Node.js では ReadableStream を活用して転送
- OpenAI ↔ Ollama でレスポンス形式の差異があるため変換が必要になる場合あり
3. ストリーミング対応 実装例
routes.ts
import { Context } from 'hono'
export async function routeCompletionsStream(c: Context) {
const body = await c.req.json()
const model: string = body?.model || ''
const isOllama = model.includes('gpt-oss')
const upstream = isOllama ? process.env.OLLAMA_BASE_URL : process.env.OPENAI_BASE_URL
const res = await fetch(`${upstream}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(isOllama ? {} : { 'Authorization': `Bearer ${process.env.OPENAI_API_KEY}` })
},
body: JSON.stringify({ ...body, stream: true })
})
// ストリームをそのまま返す
return new Response(res.body, {
status: res.status,
headers: { 'Content-Type': 'text/event-stream' }
})
}
CLI側の使い方
codex --provider internal-proxy --stream "Generate a long article outline"
4. リトライ戦略の必要性
よくある失敗パターン
- ネットワークタイムアウト
- 上流API(OpenAI / Ollama)の一時的なエラー
- レート制限(429エラー)
実装の考え方
- 指数バックオフリトライ: 1秒 → 2秒 → 4秒 … のように待機時間を伸ばす
- 最大試行回数を設定(例: 3〜5回)
- エラー種別ごとの分岐: 認証エラー(401)はリトライ不要、429/500系はリトライ対象
5. リトライ実装例
async function fetchWithRetry(url: string, options: any, retries = 3, backoff = 1000): Promise<Response> {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url, options)
if (res.ok) return res
if (res.status === 401) throw new Error('Unauthorized - check API key')
if (res.status === 429 || res.status >= 500) {
console.warn(`Retrying after error ${res.status}...`)
} else {
throw new Error(`Unexpected status: ${res.status}`)
}
} catch (err) {
console.error(`Attempt ${i + 1} failed:`, err)
if (i === retries - 1) throw err
await new Promise(r => setTimeout(r, backoff * (2 ** i)))
}
}
throw new Error('All retries failed')
}
これを providers/openai.ts
や providers/ollama.ts
内の fetch
の代わりに利用すれば、リトライが適用されます。
6. 図解:拡張後のプロキシ構成
7. まとめ
本記事では、Codex CLI 用自作プロキシの拡張として以下を解説しました:
-
stream: true
によるストリーミング対応 - 指数バックオフを用いたリトライ戦略
これらを実装することで、より実用的かつ堅牢なプロキシが完成します。
次回は、監視とメトリクス収集(Prometheus/Grafana連携) をテーマに、運用フェーズでの安定性を高める方法を解説します。