opencode با یوتاپن — opencode.json، انتخاب مدل و سقف خروجی
تنظیم opencode با یوتاپن در opencode.json: provider سازگار با OpenAI، شناسهٔ مدل، limit.output برای رزرو کوچک، small_model ارزان برای عنوان و رفع خطاها.
بهروزرسانی: ۳۱ شهریور ۱۴۰۵
opencode یک ایجنت کدنویسی ترمینالی است که هر سرویس سازگار با Chat Completions را میپذیرد. یوتاپن را در آن بهصورت یک provider سفارشی تعریف میکنی: آدرس https://api.uttapen.ir/v1، کلید sk-up-… و فهرست مدلهایی که میخواهی. این صفحه روی opencode نسخهٔ 1.18.31 بررسی شده و درخواستهایی که میفرستد را با یک سرور آزمایشی ثبت کردهایم.
opencode اگر سقف خروجی مدل را ندانَد، روی هر درخواست max_tokens: 32000 میفرستد و عنوان گفتگو را هم با همان مدل اصلی میسازد؛ یعنی هر پیام دو درخواست همزمان با رزرو ۳۲ هزار توکنی. با مدلی مثل Claude Sonnet همین کافی است که با موجودی معقول هم 402 بگیری. دو تنظیم limit.output و small_model که پایین آمدهاند این مشکل را حل میکنند.
پیشنیاز
- یک کلید از داشبورد ← کلیدها. برای opencode یک کلید جدا با سقف ماهانه بساز تا مصرفش جدا دیده شود.
- موجودی در کیف پول.
- کلید را در متغیر محیطی بگذار، نه در فایل:
export UTTAPEN_API_KEY="sk-up-..."
فایل opencode.json
این فایل را در ریشهٔ پروژه (opencode.json) یا برای همهٔ پروژهها در ~/.config/opencode/opencode.json بگذار. opencode هر دو را با هم ادغام میکند و فرمت JSONC (با کامنت) را هم میپذیرد.
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"uttapen": {
"npm": "@ai-sdk/openai-compatible",
"name": "uttapen",
"options": {
"baseURL": "https://api.uttapen.ir/v1",
"apiKey": "{env:UTTAPEN_API_KEY}"
},
"models": {
"anthropic/claude-sonnet-5": {
"name": "Claude Sonnet 5",
"limit": { "context": 200000, "output": 8192 }
},
"qwen/qwen3-coder": {
"name": "Qwen3 Coder",
"limit": { "context": 262144, "output": 8192 }
},
"deepseek/deepseek-v3.2": {
"name": "DeepSeek V3.2",
"limit": { "context": 163840, "output": 8192 }
},
"openai/gpt-5-nano": {
"name": "GPT-5 nano",
"limit": { "context": 400000, "output": 1024 }
}
}
}
},
"model": "uttapen/anthropic/claude-sonnet-5",
"small_model": "uttapen/openai/gpt-5-nano"
}
نکتههای هر فیلد:
npmباید@ai-sdk/openai-compatibleباشد. این پکیج فقط/v1/chat/completionsرا صدا میزند. پکیج@ai-sdk/openaiرا نگذار؛ آن سراغ/v1/responsesمیرود که یوتاپن ندارد و404میگیری.uttapenشناسهٔ provider در opencode است و هر نامی میتواند باشد؛ فقط باید درmodelوsmall_modelهمان را بنویسی.options.apiKeyبا{env:UTTAPEN_API_KEY}کلید را از متغیر محیطی میخواند. اگر ترجیح میدهی کلید را opencode نگه دارد، این خط را حذف کن، در opencode دستور/connectرا بزن، Other را انتخاب کن، شناسهٔuttapenرا بده و کلید را بچسبان.- کلیدهای
modelsدقیقاً شناسهٔ مدل در یوتاپناند؛ opencode همین رشته را در فیلدmodelدرخواست میفرستد. limit.outputهمانmax_tokensاست که opencode میفرستد (با سقف ۳۲٬۰۰۰). اگر نگذاری، ۳۲٬۰۰۰ فرستاده میشود.limit.contextرا حتماً بگذار. بدون آن opencode هیچوقت گفتگو را خودکار خلاصه (compact) نمیکند و prompt هر درخواست بزرگتر و گرانتر میشود. عدد کمتر از context واقعی مدل یعنی خلاصهسازی زودتر و prompt ارزانتر؛ Claude Sonnet 5 در واقع یک میلیون توکن context دارد، ولی ۲۰۰ هزار برای کار روزمره کافی و ارزانتر است.small_modelمدلی است که opencode برای کارهای سبک مثل ساختن عنوان گفتگو استفاده میکند. opencode برای provider سفارشی نمیتواند خودش مدل ارزان پیدا کند و بدون این خط، عنوان را با همان مدل اصلی و همان رزرو بزرگ میسازد.
opencode فهرست مدل را از /v1/models نمیخواند؛ فقط مدلهایی که در models نوشتهای در انتخابگر ظاهر میشوند.
انتخاب مدل
شناسهٔ هر مدل را از فهرست مدلها یا صفحهٔ همان مدل کپی کن؛ context و سقف خروجی هر مدل هم همانجاست (یا در GET https://api.uttapen.ir/v1/models در فیلدهای context_length و top_provider.max_completion_tokens). در opencode نام کامل مدل به شکل providerID/modelID است، پس مدل anthropic/claude-sonnet-5 در provider uttapen میشود uttapen/anthropic/claude-sonnet-5. opencode فقط تا اولین / را شناسهٔ provider میگیرد و بقیه را دست نمیزند.
چند انتخاب رایج برای کد:
| نقش | شناسه |
|---|---|
| مدل اصلی قوی | anthropic/claude-sonnet-5، openai/gpt-5 |
| مدل اصلی اقتصادی | qwen/qwen3-coder، deepseek/deepseek-v3.2، openai/gpt-5-mini |
small_model (عنوان) | openai/gpt-5-nano، google/gemini-2.5-flash-lite |
فهرست مدلهای تعریفشده را با این دستور ببین و داخل opencode با /models بینشان جابهجا شو:
opencode models uttapen
هزینه را مهار کن
یوتاپن پیش از فرستادن هر درخواست، هزینهٔ بیشترین خروجی ممکن را از موجودی رزرو میکند و بعد از پاسخ فقط مصرف واقعی را کم میکند (قیمتگذاری). پس max_tokens بزرگ پول بیشتری مصرف نمیکند، ولی موجودی بیشتری را موقتاً قفل میکند؛ و opencode چند درخواست همزمان میفرستد که رزروهایشان جمع میشوند.
limit.outputرا برای مدلهای اصلی حدود8192بگذار. رزرو خروجی نسبت به پیشفرض ۳۲٬۰۰۰ تقریباً یکچهارم میشود و برای ویرایش کد کافی است. برای مدلهای استدلالی که قبل از جواب فکر میکنند،16000امنتر است.small_modelرا روی یک مدل ارزان باlimit.outputحدود1024بگذار.- اگر نمیخواهی برای تکتک مدلها عدد بنویسی، متغیر
OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAXسقف سراسری است؛ opencode کمترین مقدار بین آن وlimit.outputرا میفرستد. همانطور که از نامش پیداست، آزمایشی است. - در کلید opencode سقف ماهانه و در صورت نیاز فهرست مدلهای مجاز بگذار (کلیدها).
با تنظیمات بالا، opencode نسخهٔ 1.18.31 برای یک پیام، درخواست عنوان را با openai/gpt-5-nano و max_tokens: 1024 و درخواست اصلی را با anthropic/claude-sonnet-5 و max_tokens: 8192 فرستاد. بدون آنها هر دو درخواست به Claude Sonnet 5 با max_tokens: 32000 رفتند.
بررسی اتصال
اول خود کلید را جدا از opencode تست کن:
curl https://api.uttapen.ir/v1/chat/completions \
-H "Authorization: Bearer $UTTAPEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"openai/gpt-5-nano","max_tokens":20,"messages":[{"role":"user","content":"ping"}]}'
بعد opencode را بدون رابط تمامصفحه اجرا کن:
opencode run -m uttapen/openai/gpt-5-nano "say hi"
اگر جواب آمد، در داشبورد ← مصرف باید دو درخواست کوچک ببینی (عنوان و پیام).
عیبیابی
| خطا | علت در opencode | راهحل |
|---|---|---|
401 invalid_api_key | متغیر UTTAPEN_API_KEY در شِلی که opencode را باز کرده ست نیست، یا کلید لغو شده | echo $UTTAPEN_API_KEY را در همان ترمینال بزن؛ یا کلید را با /connect دوباره ذخیره کن |
404 model_not_found | کلید models با شناسهٔ واقعی فرق دارد، مثلاً claude-sonnet-5 بدون anthropic/ یا claude-sonnet-4-5 با خط تیره بهجای نقطه | شناسه را از /models کپی کن (پیام 404 در uttapen.suggestions نزدیکترین شناسههای محتمل را هم پیشنهاد میدهد) |
404 not_found | npm روی @ai-sdk/openai است و درخواست به /v1/responses رفته | @ai-sdk/openai-compatible بگذار |
402 insufficient_balance | موجودی حتی برای ۱٬۰۲۴ توکن خروجی این درخواست کافی نیست؛ رزروهای همزمان جمع میشوند | کیف پول را شارژ کن؛ limit.output و small_model را مثل بالا تنظیم کن |
429 | سقف درخواست در دقیقهٔ کلید یا سقف ماهانهاش پر شده، یا مدل موقتاً محدود است | code خطا را ببین (محدودیت نرخ)؛ rate_limit_rpm کلید را بالا ببر |
| پاسخ نیمهکاره | finish_reason: "length"؛ یا limit.output کم است یا یوتاپن سقف را پایین آورده | پایین را ببین |
| opencode بیجواب میماند؛ نه خطا نه پاسخ | گیر در init، قبل از رسیدن به یوتاپن | پایینتر را ببین |
اگر opencode اصلاً بالا نمیآید (گیر در init). لاگ ~/.local/share/opencode/log/opencode.log را نگاه کن؛ اگر آخرین خط اجرا message=init باشد و ادامه پیدا نکند، یعنی هنوز به سرور یوتاپن هم نرسیده. علتش خود یوتاپن نیست: opencode موقع بالا آمدن چند تماس شبکهٔ پسزمینهٔ خودش را میزند — فهرست مدلها را از models.opencode.ai (مگر OPENCODE_DISABLE_MODELS_FETCH=1 باشد) و چک نسخهٔ جدید را (مگر OPENCODE_DISABLE_AUTOUPDATE=1 باشد)؛ اینها و opencode.ai و حتی registry.npmjs.org همه پشت Cloudflareاند، و روی یک اتصال ایرانی که دستدهی TLS را بهجای رد سریع معلق نگه میدارد، opencode همانجا میماند. اگر گیر کرد، این دو متغیر را قبل از اجرا بگذار:
OPENCODE_DISABLE_MODELS_FETCH=1 OPENCODE_DISABLE_AUTOUPDATE=1 opencode run -m uttapen/openai/gpt-5-nano "say hi"
این به فهرست مدلهای provider uttapen در opencode.json کاری ندارد — آن از فایل خودت خوانده میشود، نه از models.opencode.ai. این گیرکردن متناوب است نه دائمی؛ همین سیستم چند بار بدون هیچ متغیری هم بدون گیر کردن جواب داد، پس اول یک بار ساده امتحان کن.
وقتی موجودی برای کل max_tokens کافی نیست. اگر موجودی قابلاستفادهات هزینهٔ کل max_tokens درخواستی را پوشش ندهد، یوتاپن درخواست را رد نمیکند؛ max_tokens را تا جایی که موجودی میکشد پایین میآورد (حداقل ۱٬۰۲۴ توکن خروجی) و مقدار دادهشده را در هدر پاسخ X-Uttapen-Max-Tokens برمیگرداند. فقط اگر همان ۱٬۰۲۴ توکن هم ممکن نباشد 402 میگیری. نتیجهٔ این کاهش ممکن است پاسخی باشد که با finish_reason: "length" وسط کار قطع شده. راهحل شارژ کیف پول است، یا کمکردن limit.output تا درخواستها همیشه در موجودی جا شوند.
جدول کامل کدها در خطاها است و بقیهٔ ابزارها در یکپارچهسازی.
با شماره موبایل ثبتنام کن، کیف پول را از ۱۰۰٬۰۰۰ تومان شارژ کن و کلید بساز. مدلهای رایگان بدون شارژ هم کار میکنند — تا ۵۰ درخواست در روز. ساخت کلید API ←