Bỏ qua, tới nội dung chính

Idempotency

Timeout mạng khiến bạn không biết request đã chạy hay chưa. Gửi lại mà không có bảo vệ thì có thể trả tiền hai lần cho cùng một việc. Header Idempotency-Key làm cho retry không-stream an toàn: cùng key trả cùng response, tính tiền một lần.

Cách dùng

Gửi header Idempotency-Key trên POST /v1/chat/completions với stream: false. Key là chuỗi bất kỳ bạn tự sinh (≤255 ký tự), duy nhất cho mỗi request trong tổ chức; key được giữ 24 giờ.

curl
curl https://api.clfaigateway.dev/v1/chat/completions \
  -H "Authorization: Bearer sk-gw-..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-42-summary-1" \
  -d '{
    "model": "kimi-k2.6",
    "messages": [{"role": "user", "content": "Summarize order 42."}],
    "max_tokens": 256
  }'

Ngữ nghĩa chính xác

  • Lần hoàn thành đầu tiên thắng: request kết thúc 2xx thì response được lưu dưới key đó.
  • Cùng key, cùng body gửi lại: response đã lưu được phát lại nguyên văn — cùng body, cùng x-request-id gốc — kèm header x-gw-idempotent-replay: true. Model không chạy lại và không có gì bị tính thêm.
  • Cùng key khi lần đầu còn đang chạy: 409 idempotency_key_in_flight — chờ lần đầu xong rồi gửi lại.
  • Cùng key, body khác: 422 idempotency_key_reused — một key đại diện đúng một request logic; request khác thì dùng key mới.
  • Chỉ response 2xx được lưu. Lỗi không bao giờ được phát lại, nên một lần thất bại không găm lỗi vào key. Sửa xong request bị từ chối thì gửi body đã sửa với key mới.

Vì sao stream không hỗ trợ

Request stream kèm Idempotency-Key bị từ chối 400. Stream là chuỗi sự kiện phát theo thời gian thực — bản "phát lại" từ kho lưu không thể tái hiện trung thực (nhất là khi bản gốc đứt giữa chừng), còn ghi trọn mọi stream chỉ để phòng retry là phí lưu trữ đổ vào giá của tất cả mọi người. Chúng tôi chọn từ chối thẳng thay vì giả vờ.

Với stream, mỗi lần gửi là một request riêng, tính tiền theo phần thực sự đã phát — chặn trần bằng max_tokens và đọc chính sách ở Billing.

Auto-retry của SDK

SDK OpenAI chính chủ (Python và Node) lặng lẽ retry 429 và 5xx — mặc định 2 lần, backoff lũy tiến, tôn trọng retry-after. Mỗi retry là một request mới với x-request-id mới. Nghĩa là:

ResponseSDK làm gìBạn nên làm gì
429 rpm_exceeded / tpm_exceededRetry sau retry-after (số giây còn lại của cửa sổ).Cứ để — retry rơi vào cửa sổ kế.
429 daily_budget_exceededCũng retry — vô ích, ngân sách vẫn cạn tới 00:00 UTC.Rẽ nhánh theo error.code và dừng retry; nâng ngân sách thì hơn.
503 no_capacityRetry sau retry-after: 10.Cứ để.
402 insufficient_creditsKhông retry.Nạp tiền rồi tự gửi lại.
Timeout mạng trên request không streamRetry — đây chính là chỗ rủi ro tính tiền đôi.Gửi kèm Idempotency-Key để retry thành phát lại thay vì chạy lại.

Bảo vệ tầng sổ cái luôn bật

Độc lập với header này, mỗi request chỉ chốt sổ trừ tiền đúng một lần — retry của chính hạ tầng chúng tôi không bao giờ chốt sổ đôi một request. Idempotency-Key bảo vệ bạn ở tầng trên: khi client của bạn gửi cùng một request logic hai lần.