unbleep מציעה API תואם OpenAI, ובפועל זה אומר שהמיגרציה היא שתי שורות ובלי תלות חדשה. אתם שומרים על ה-SDK הרשמי, על לוגיקת ה-retry שלכם, על לולאת הסטרימינג, על ספירת הטוקנים ועל מטפלי השגיאות. מה שמשתנה הוא לאן הבקשות הולכות ואיזה מפתח מאמת אותן. הפוסט הזה מכסה את ההחלפה, ואחריה את ארבעת הדברים ששונים מספיק כדי לשבור משהו אם לא יודעים עליהם.
מיגרציה ל-API תואם OpenAI בשתי שורות
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)אם אתם מעדיפים לא לגעת בקוד בכלל, ה-SDK קורא את שני הערכים מסביבת הריצה, כך ששינוי קונפיגורציה מספיק:
export OPENAI_BASE_URL="https://unbleep.ai/v1"
export OPENAI_API_KEY="ub_live_9f2c..."אותו זוג ערכים מכסה את רוב האקוסיסטם שנבנה מעל ה-SDK — LangChain, LlamaIndex, Instructor, ה-Vercel AI SDK, וכל דבר שחושף הגדרת base URL. למפתחות יש קידומת כדי שדליפות יהיו ברורות לסורקי סודות: ub_live_ ו-ub_test_ שניהם מחויבים מאותו קרדיט משולם מראש, באותו תעריף לטוקן. מפתח test הוא credential נפרד וניתן לביטול עם תקרה נמוכה יותר — 15 בקשות לדקה במקום 60 של החשבון — ולא דרגה חינמית. שמרו את שניהם בצד השרת.
GET /v1/models עובד, כך שכלים שמונים מודלים כדי למלא dropdown לא צריכים טיפול מיוחד.
בחירת מודל
שלוש דרגות. המזהים המתוארכים — unbleep-250811, unbleep-high-250811, unbleep-mini-250811 — מתקבלים כ-aliases, אבל נכון להיום הם מתורגמים לאותו build כמו המזהה הפשוט, והתשובה מדווחת בחזרה את המזהה הפשוט. התייחסו אליהם כאל איות תואם-קדימה, לא כאל ערובה לשחזוריות; אם הערכה צריכה להיות ניתנת לחזרה, תעדו את הפלטים, לא את מזהה המודל.
| מודל | הקשר | מחיר קלט / פלט ל-1M | הערות | | --- | --- | --- | --- | | unbleep | 256K | $3.00 / $3.00 | ברירת המחדל. דרגת reasoning. | | unbleep-high | 1M* | $5.00 / $5.00 | העבודות הגדולות ביותר. דרגת reasoning. | | unbleep-mini | 32K | $1.00 / $1.00 | זול ומהיר. עונה ישירות, בלי trace של reasoning. |
*גוף הבקשה מוגבל ל-2,000,000 בייטים — בערך 500k טוקנים — כך שקריאה בודדת לא באמת יכולה למלא את חלון ה-1M; כל דבר גדול יותר חוזר עם 413 payload_too_large.
תקרת ה-32K של unbleep-mini היא זו שתופסת אנשים שעוברים ממודל עם הקשר של 128K: פרומפט שנכנס קודם יידחה עכשיו. אם אתם מנתבים לפי עלות, נתבו גם לפי אורך.
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"סטרימינג
מגדירים stream=True ומקבלים Server-Sent Events סטנדרטיים: כל אירוע הוא chat.completion.chunk שנושא delta, והזרם מסתיים ב-data: [DONE] מילולי. הלולאה הקיימת שלכם עובדת ללא שינוי.
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}
}'אתם מקבלים את ספירות הטוקנים בין אם ביקשתם אותן ובין אם לא: unbleep תמיד מבקשת usage מה-backend ומעבירה אותו הלאה, ורק stream_options: {"include_usage": false} מפורש מסיר אותו בדרך החוצה. כך או כך, הזרם מסתיים ב-chunk אחרון אחד עם מערך choices ריק — שנושא את אובייקט ה-usage המלא, אלא אם ביטלתם — ולכן הלולאה שלמטה בודקת את choices לפני שהיא נוגעת בו.
השדה reasoning_content
זו התוספת האמיתית היחידה לסכמה. unbleep ו-unbleep-high חושבים לפני שהם עונים, ושרשרת המחשבה הזו מוחזרת ב-reasoning_content, שדה-אח של content על ההודעה (ללא סטרימינג) או על ה-delta (בסטרימינג). ה-backends במעלה הזרם חלוקים בשאלה אם לקרוא לזה reasoning או reasoning_content; ה-API מנרמל ל-reasoning_content, כך שתמיד תטפלו בצורה אחת בלבד.
מכיוון שהוא לא חלק מהסכמה של OpenAI, הוא לא נמצא ב-type stubs של ה-SDK. מודלי התשובה מאפשרים שדות נוספים, כך שגישה למאפיין עובדת בזמן ריצה — אבל קראו אותו עם getattr, כדי שתשובה של mini, שאין לה trace, לא תזרוק חריגה:
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)}")שלוש השלכות מעשיות:
- טוקני reasoning הם טוקני פלט. הם מופיעים ב-
completion_tokensומחויבים בתעריף הפלט.usage.completion_tokens_details.reasoning_tokensאומר לכם איזה חלק מהחשבון היה חשיבה. - הם גם צורכים
max_tokens. מגבלה הדוקה על דרגת reasoning עלולה להתבזבז כולה על ה-trace, ולהשאיר לכם תשובה קטועה אוcontentריק. תקצבו לשניהם, או כבו את החשיבה. - כבו את זה כשאתם לא צריכים. שלחו
"thinking": falseדרךextra_bodyלקריאות קצרות או בנפח גבוה. גםreasoning_effortעובר הלאה.
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
)לעולם אל תזינו reasoning_content בחזרה לתור מאוחר יותר כתוכן של assistant. זהו פלט אבחוני, לא היסטוריית שיחה, והשמעתו מחדש פוגעת בתשובה הבאה.
שגיאות ושתי המלכודות האמיתיות
שגיאות משתמשות במעטפת של OpenAI — {"error": {"type", "code", "message"}} — כך שבלוקי ה-except הקיימים שלכם ממשיכים לעבוד. קודי הסטטוס ממופים כפי שהייתם מצפים: 401 מפתח שגוי, 422 נחסם על ידי policy: strict, 429 מגבלת קצב, 5xx תקלה במעלה הזרם שאפשר לנסות שוב.
הסטטוס שכנראה מעולם לא טיפלתם בו הוא 402, נגמר הקרדיט. החשבונות משולמים מראש, כך שאין חריגה ואין חשבונית — הבקשות פשוט נעצרות עד שתטענו מחדש. OpenAI מאותתת על מיצוי מכסה בתור 429, מה שאומר שבמסלול המיגרציה הנאיבי לוגיקת ה-backoff שלכם תנסה שוב 402 לנצח. התייחסו אליו כסופי והפעילו עליו התראה.
המלכודת השנייה: system_fingerprint לא מוחזר. הוא מזהה את ה-backend שמגיש את הבקשה, ולכן הוא מוסר יחד עם שאר שדות הספק. אם אתם משתמשים בו כמפתח ל-cache או לבדיקת שחזוריות, תצטרכו סמן גרסה משלכם: מזהי המודל המתוארכים הם aliases ל-build הנוכחי, לא snapshots קפואים, ולכן הם לא יגידו לכם מתי ה-backend משתנה.
מגבלות הקצב חוזרות כ-headers על כל תשובה — x-ratelimit-limit-requests, x-ratelimit-remaining-requests, x-ratelimit-reset-requests — כך שעבודת אצווה יכולה לווסת את קצב הבקשות שלה במקום לגלות את התקרה בכך שהיא נתקלת בה. יש תקרה שנייה שה-headers לא מתארים: לכל היותר 8 בקשות במקביל לחשבון, והתשיעית חוזרת עם 429 וקוד too_many_concurrent_requests. קריאת סטרימינג מחזיקה את המקום שלה עד שהזרם מסתיים, אז הגבילו את מאגר ה-workers שלכם ל-8.
לאן אתם מכוונים
כדאי להיות מפורשים: המודלים מאחורי ה-endpoint הזה הם abliterated, כלומר התנהגות הסירוב שלהם הוסרה ברמת המשקולות. זו הנקודה — זהו API למפתחים שמיועד למחקר אבטחה, ל-red-teaming ולהערכה, שם סירוב הוא שגיאת מדידה. זה גם אומר שה-guardrails הרגילים לא נמצאים שם כדי לתפוס פרומפט גרוע, אז השאירו אדם אחראי על הפלטים וקראו את מדיניות השימוש המקובל. שימוש חוקי — באחריותכם.
קבלו מפתח API — המיגרציה היא באמת שתי שורות.