uttapen

سازگاری با OpenAI و مهاجرت از OpenAI، OpenRouter و دیگران

مهاجرت کد موجود به uttapen فقط با تغییر base_url؛ شکل شناسهٔ مدل‌ها، مسیرهای پشتیبانی‌شده، فیلدهایی که عبور می‌کنند و جدول اشتباه‌های رایج.

به‌روزرسانی: ۱۶ شهریور ۱۴۰۵

یوتاپن همان قرارداد chat/completions را پیاده کرده که SDK رسمی OpenAI با آن حرف می‌زند. اگر کدت الان با OpenAI، OpenRouter، Azure OpenAI یا هر gateway سازگار دیگری کار می‌کند، مهاجرت یعنی دو تغییر: آدرس پایه و کلید. شناسهٔ مدل هم شکل provider/model می‌گیرد. باقی صفحه دربارهٔ همین جزئیات و چند تفاوت کوچک است.

تغییر base_url

# قبل
client = OpenAI(api_key="sk-...")

# بعد
client = OpenAI(
    base_url="https://api.uttapen.ir/v1",
    api_key="sk-up-...",
)

در Node نام فیلد baseURL است؛ در PHP withBaseUri()؛ در Go option.WithBaseURL(). اگر SDK از متغیر محیطی می‌خواند، OPENAI_BASE_URL=https://api.uttapen.ir/v1 و OPENAI_API_KEY=sk-up-... را ست کن و کد را دست نزن. مسیر /v1 جزء آدرس است؛ بدون آن 404 می‌گیری.

شناسهٔ مدل

شناسه‌ها به شکل provider/model هستند، همان قرارداد OpenRouter:

قبل (OpenAI)بعد (یوتاپن)
gpt-5openai/gpt-5
gpt-5-miniopenai/gpt-5-mini
o3openai/o3
anthropic/claude-sonnet-4.5
google/gemini-2.5-flash
deepseek/deepseek-chat

فهرست کامل با قیمت تومانی در صفحهٔ مدل‌ها یا GET /v1/models (بدون کلید هم کار می‌کند). اگر از OpenRouter می‌آیی، شناسه‌ها عیناً همان‌اند؛ پسوندهای :free، :batch و :online هم پشتیبانی می‌شوند. شناسهٔ بدون provider (gpt-5) پذیرفته نمی‌شود و 404 model_not_found برمی‌گرداند.

چه چیزهایی پشتیبانی می‌شود

مسیروضعیت
POST /v1/chat/completionsکامل؛ استریم و غیراستریم، tools، vision، فایل، response_format، reasoning
POST /v1/embeddingsکامل
POST /v1/completionslegacy؛ برای مدل‌هایی که پشتیبانی می‌کنند
GET /v1/models، GET /v1/models/{id}کامل، با بلوک uttapen_pricing
POST /v1/responsesفعلاً 404 با پیام راهنما؛ از chat/completions استفاده کن
/v1/files، /v1/fine_tuning، /v1/assistants، /v1/batches، /v1/audio، /v1/imagesندارد؛ 404 not_found

Responses API بعد از تثبیت شکل رویدادهایش برای متر کردن هزینه اضافه می‌شود. تا آن روز، هر چیزی که در Responses می‌خواهی (tools، تصویر، JSON، reasoning) در chat/completions هم هست؛ فقط شکل بدنه فرق دارد.

فیلدهایی که عبور می‌کنند

بدنهٔ درخواست تو تقریباً دست‌نخورده به provider می‌رسد. فیلدهای اختصاصی OpenRouter هم عبور می‌کنند:

  • provider — ترجیح یا اجبار provider خاص برای یک مدل (مثلاً {"order": ["Anthropic"], "allow_fallbacks": false} در بدنه). برای کاربر حرفه‌ای که به latency یا region حساس است.
  • models — فهرست fallback. اگر مدل اول جواب نداد، بعدی امتحان می‌شود. رزرو هزینه با گران‌ترین مدل فهرست محاسبه می‌شود، ولی شارژ نهایی با مدلی است که واقعاً پاسخ داده.
  • reasoning، plugins، web_search_options، transforms — عبور می‌کنند و در برآورد هزینه لحاظ می‌شوند.

دو فیلد را خودمان اضافه می‌کنیم: usage: {include: true} تا مصرف واقعی برگردد، و stream: true به سمت upstream حتی وقتی تو استریم نخواسته‌ای. در حالت دوم gateway چانک‌ها را جمع می‌کند و یک JSON استاندارد تحویلت می‌دهد، پس از بیرون فرقی نمی‌بینی.

تفاوت‌های رفتاری

  • فیلدهای عددی باید عدد صحیح JSON باشند. max_tokens، max_completion_tokens، max_output_tokens، n و best_of به‌صورت "100" (رشته)، 100.0 یا 1e2 رد می‌شوند و 400 invalid_request می‌گیری. دلیلش این است که برآورد هزینه نباید با نمایشی که provider می‌پذیرد ولی ما نمی‌خوانیم دور زده شود.
  • 402 قبل از ارسال. اگر موجودی قابل‌استفاده از برآورد هزینهٔ درخواست کمتر باشد، درخواست اصلاً به provider نمی‌رود. max_tokens کوچک‌تر یعنی رزرو کوچک‌تر (قیمت‌گذاری).
  • حداکثر بدنه ۲۰ مگابایت است؛ بیشتر از آن 413.
  • پیام‌های خطای provider عیناً با همان status برمی‌گردند، فقط رشته‌های شناسایی‌کنندهٔ upstream حذف شده‌اند.
  • بدون ذخیرهٔ محتوا. prompt و پاسخ در مسیر API جایی ثبت نمی‌شوند (حریم خصوصی).
  • تأخیر اضافه نسبت به فراخوانی مستقیم حدود ۱۰۰ میلی‌ثانیه است (مسیر ایران ← relay ← provider).

اشتباه‌های رایج

نشانهعلتراه‌حل
404 not_found روی هر درخواستbase_url بدون /v1 یا با /v1/chat/completions کاملفقط https://api.uttapen.ir/v1
404 model_not_found با مدل معتبرشناسه بدون provider یا با حروف بزرگopenai/gpt-5-mini، همه حروف کوچک
401 بعد از کار درست چند روزهکلید منقضی یا لغو شده؛ یا کلید OpenAI قدیمی در env ماندهUTTAPEN_API_KEY را چک کن؛ OPENAI_API_KEY اگر ست است اولویت ندارد ولی گیج‌کننده است
400 invalid_request روی max_tokensمقدار رشته یا اعشاریعدد صحیح بفرست
402 روی درخواست کوچکmax_tokens ست نشده و پیش‌فرض ۴۰۹۶ توکن خروجی برای مدل گران رزرو شدهmax_tokens واقعی بده
کلاینت استریم قطع می‌شودproxy میانی (nginx، Cloudflare) بافر می‌کندproxy_buffering off یا هدر X-Accel-Buffering: no روی proxy خودت
client.responses.create خطا می‌دهدResponses API هنوز فعال نیستchat.completions.create
خروجی SDK PHP در استریم می‌شکندهدر X-Uttapen-Include-Meta با createStreamedاین هدر را فقط با کلاینت HTTP خام بفرست (PHP)

مهاجرت از OpenRouter

اگر مستقیماً OpenRouter را صدا می‌زدی، فقط آدرس و کلید عوض می‌شود. HTTP-Referer و X-Title لازم نیست؛ خودمان می‌فرستیم. پاسخ همان فیلد اضافی provider را دارد. تفاوت مهم: تسویه به تومان از کیف پول یوتاپن است و درخواست از IP ایران مستقیم به OpenRouter نمی‌رود.

مهاجرت از Azure OpenAI

کلاینت AzureOpenAI را با OpenAI معمولی جایگزین کن؛ api_version و azure_endpoint معنایی ندارند و نام deployment جای خودش را به شناسهٔ provider/model می‌دهد.