سازگاری با 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-5 | openai/gpt-5 |
gpt-5-mini | openai/gpt-5-mini |
o3 | openai/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/completions | legacy؛ برای مدلهایی که پشتیبانی میکنند |
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 میدهد.