← Tất cả bài viết

API tương thích OpenAI dạng drop-in — Di chuyển chỉ với hai dòng

20 thg 8 2026 · 7 phút đọc · api, migration

unbleep là một API tương thích OpenAI, và trên thực tế điều đó có nghĩa là việc di chuyển chỉ mất hai dòng và không thêm dependency nào. Bạn giữ nguyên SDK chính thức, logic retry, vòng lặp streaming, cách đếm token và các handler xử lý lỗi. Thứ thay đổi là nơi request được gửi đến và key nào xác thực chúng. Bài viết này đi qua bước chuyển đổi, rồi đến bốn điểm khác biệt đủ lớn để làm hỏng thứ gì đó nếu bạn không biết trước.

Di chuyển sang API tương thích OpenAI chỉ với hai dòng

python
import os

from openai import OpenAI

client = OpenAI(
    base_url="https://unbleep.ai/v1",
    api_key=os.environ["UNBLEEP_API_KEY"],
)

resp = client.chat.completions.create(
    model="unbleep",
    messages=[{"role": "user", "content": "Summarise this incident report."}],
)
print(resp.choices[0].message.content)

Nếu bạn không muốn đụng vào code chút nào, SDK đọc cả hai giá trị từ biến môi trường, nên chỉ cần đổi cấu hình là đủ:

bash
export OPENAI_BASE_URL="https://unbleep.ai/v1"
export OPENAI_API_KEY="ub_live_9f2c..."

Cặp giá trị đó cũng bao phủ phần lớn hệ sinh thái xây trên SDK — LangChain, LlamaIndex, Instructor, Vercel AI SDK, bất cứ thứ gì có tùy chọn base-URL. Key có tiền tố để rò rỉ dễ bị các công cụ quét secret phát hiện: ub_live_ và ub_test_ đều tính phí vào cùng một khoản credit trả trước với cùng đơn giá theo token. Test key là một thông tin xác thực riêng, có thể thu hồi, với trần thấp hơn — 15 request/phút thay vì 60 của tài khoản — chứ không phải một gói miễn phí. Giữ cả hai ở phía server.

GET /v1/models hoạt động, nên các công cụ liệt kê mô hình để điền vào dropdown không cần xử lý đặc biệt.

Chọn mô hình

Ba bậc. Các id có ngày tháng — unbleep-250811, unbleep-high-250811, unbleep-mini-250811 — được chấp nhận như alias, nhưng hiện tại chúng trỏ về cùng một bản build với id trần, và response trả về id trần. Hãy xem chúng là cách viết tương thích về sau, không phải một cam kết về khả năng tái lập; nếu một bài đánh giá cần lặp lại được, hãy lưu lại output chứ không phải model id.

| Mô hình | Ngữ cảnh | Giá vào / ra mỗi 1M | Ghi chú | | --- | --- | --- | --- | | unbleep | 256K | $3.00 / $3.00 | Mặc định. Bậc có suy luận. | | unbleep-high | 1M* | $5.00 / $5.00 | Cho các tác vụ lớn nhất. Bậc có suy luận. | | unbleep-mini | 32K | $1.00 / $1.00 | Rẻ và nhanh. Trả lời trực tiếp, không có reasoning trace. |

*Request body bị giới hạn ở 2,000,000 byte — khoảng 500k token — nên một lần gọi đơn lẻ thực ra không thể lấp đầy cửa sổ 1M; bất cứ thứ gì lớn hơn sẽ nhận về 413 payload_too_large.

Trần 32K của unbleep-mini là thứ hay bắt được những người di chuyển từ một mô hình ngữ cảnh 128K: một prompt trước đây vừa vặn giờ sẽ bị từ chối. Nếu bạn định tuyến theo chi phí, hãy định tuyến theo cả độ dài.

python
def pick_model(prompt_chars: int) -> str:
    """~4 chars/token is a deliberate under-estimate; leave room for the completion."""
    est_tokens = prompt_chars // 4
    if est_tokens < 24_000:
        return "unbleep-mini"
    return "unbleep" if est_tokens < 200_000 else "unbleep-high"

Streaming

Đặt stream=True và bạn nhận được Server-Sent Events tiêu chuẩn: mỗi event là một chat.completion.chunk mang theo một delta, và stream kết thúc bằng một dòng data: [DONE] đúng nghĩa đen. Vòng lặp sẵn có của bạn hoạt động không cần sửa.

bash
curl -N https://unbleep.ai/v1/chat/completions \
  -H "Authorization: Bearer $UNBLEEP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "unbleep",
    "messages": [{"role": "user", "content": "Explain the residual stream."}],
    "stream": true,
    "stream_options": {"include_usage": true}
  }'

Bạn nhận được số lượng token dù có yêu cầu hay không: unbleep luôn yêu cầu usage từ backend và chuyển tiếp nó, và chỉ khi bạn chỉ định rõ stream_options: {"include_usage": false} thì nó mới bị lược bỏ ở đầu ra. Dù thế nào thì stream cũng kết thúc bằng một chunk cuối có mảng choices rỗng — mang theo đối tượng usage đã điền đầy đủ trừ khi bạn chọn tắt — đó là lý do vòng lặp bên dưới kiểm tra choices trước khi chạm vào nó.

Trường reasoning_content

Đây là phần bổ sung thực sự duy nhất vào schema. unbleep và unbleep-high suy nghĩ trước khi trả lời, và chuỗi suy luận đó được trả về trong reasoning_content, một trường anh em với content trên message (không streaming) hoặc trên delta (streaming). Các backend upstream không thống nhất về việc gọi nó là reasoning hay reasoning_content; API chuẩn hóa về reasoning_content để bạn chỉ phải xử lý một dạng duy nhất.

Vì nó không thuộc schema của OpenAI, nó không có trong type stub của SDK. Các model response cho phép trường bổ sung, nên truy cập thuộc tính vẫn hoạt động lúc chạy — nhưng hãy đọc nó bằng getattr để một response từ mini, vốn không có trace, không ném ra ngoại lệ:

python
import sys

stream = client.chat.completions.create(
    model="unbleep",
    messages=[{"role": "user", "content": "Why did this detection rule misfire?"}],
    stream=True,
    stream_options={"include_usage": True},
)

usage = None
for chunk in stream:
    if not chunk.choices:          # final usage-only chunk
        usage = chunk.usage
        continue
    delta = chunk.choices[0].delta
    thought = getattr(delta, "reasoning_content", None)
    if thought:                    # trace to stderr, answer to stdout
        sys.stderr.write(thought)
    if delta.content:
        sys.stdout.write(delta.content)

if usage:
    details = usage.completion_tokens_details
    print(f"\nreasoning tokens: {getattr(details, 'reasoning_tokens', 0)}")

Ba hệ quả thực tế:

python
resp = client.chat.completions.create(
    model="unbleep",
    messages=[{"role": "user", "content": "phishing or benign?"}],
    max_tokens=4,
    extra_body={"thinking": False},   # spend the budget on the answer, not the trace
)

Đừng bao giờ đưa reasoning_content ngược trở lại một lượt sau như nội dung của assistant. Nó là output chẩn đoán, không phải lịch sử hội thoại, và phát lại nó sẽ làm giảm chất lượng response tiếp theo.

Lỗi và hai cái bẫy thực sự

Lỗi dùng envelope của OpenAI — {"error": {"type", "code", "message"}} — nên các khối except sẵn có của bạn vẫn hoạt động. Mã trạng thái ánh xạ như bạn mong đợi: 401 key sai, 422 bị chặn bởi policy: strict, 429 giới hạn tốc độ, 5xx lỗi upstream có thể retry.

Mã trạng thái mà có lẽ bạn chưa từng xử lý là 402, hết credit. Tài khoản là trả trước, nên không có vượt hạn mức và không có hóa đơn — request đơn giản là dừng lại cho đến khi bạn nạp thêm. OpenAI báo hiệu cạn quota bằng 429, nghĩa là với lối di chuyển ngây thơ, logic backoff của bạn sẽ retry một 402 mãi mãi. Hãy coi nó là lỗi chấm dứt và đặt cảnh báo cho nó.

Cái bẫy thứ hai: system_fingerprint không được trả về. Nó nhận diện backend đang phục vụ, nên bị lược bỏ cùng với các trường khác của nhà cung cấp. Nếu bạn dùng nó làm khóa cho cache hay kiểm tra khả năng tái lập, bạn sẽ cần dấu phiên bản của riêng mình: các model id có ngày tháng là alias cho bản build hiện tại, không phải snapshot đóng băng, nên chúng sẽ không cho bạn biết khi backend thay đổi.

Giới hạn tốc độ được trả về dưới dạng header trên mọi response — x-ratelimit-limit-requests, x-ratelimit-remaining-requests, x-ratelimit-reset-requests — nên một job xử lý hàng loạt có thể tự điều tiết tốc độ request thay vì phát hiện ra cái trần đó bằng cách đâm vào nó. Có một trần thứ hai mà các header không mô tả: tối đa 8 request đang xử lý đồng thời trên mỗi tài khoản, và request thứ chín sẽ nhận về 429 với code too_many_concurrent_requests. Một lần gọi streaming giữ chỗ của nó cho đến khi stream kết thúc, nên hãy giới hạn worker pool của bạn ở mức 8.

Bạn đang trỏ vào thứ gì

Cần nói rõ: các mô hình đằng sau endpoint này là abliterated, nghĩa là hành vi từ chối của chúng đã bị loại bỏ ở cấp trọng số. Đó chính là mục đích — đây là một API cho lập trình viên, nhắm đến nghiên cứu bảo mật, red-teaming và đánh giá, nơi một lần từ chối là một sai số đo lường. Điều đó cũng có nghĩa là các guardrail thông thường không có ở đó để chặn một prompt xấu, nên hãy giữ một con người chịu trách nhiệm cho output và đọc chính sách sử dụng. Sử dụng hợp pháp là trách nhiệm của bạn.

Lấy API key — việc di chuyển thực sự chỉ mất hai dòng.