This commit is contained in:
Frank 2026-08-18 23:52:36 -04:00
parent fbcd4faa61
commit 4e29e04f24
20 changed files with 125 additions and 4 deletions

View file

@ -385,6 +385,9 @@ export const dict = {
"zen.api.error.modelDisabled": "النموذج معطل",
"zen.api.error.regionNotAllowed":
"لا يتوفر أحدث إصدار من هذا النموذج إلا مستضافًا في الصين، ويتطلب تفعيلًا صريحًا: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "هذا النموذج غير متاح في بلدك.",
"zen.api.error.trainingNotAllowed":
"يجمع هذا النموذج بيانات تُستخدم لتحسين جودته، ويتطلب موافقتك الصريحة: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"انتهى العرض المجاني لـ {{model}}. يمكنك مواصلة استخدام النموذج بالاشتراك في OpenCode Go - {{link}}",

View file

@ -394,6 +394,9 @@ export const dict = {
"zen.api.error.modelDisabled": "O modelo está desabilitado",
"zen.api.error.regionNotAllowed":
"A versão mais recente deste modelo está disponível apenas com hospedagem na China e requer adesão explícita: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Este modelo não está disponível no seu país.",
"zen.api.error.trainingNotAllowed":
"Este modelo coleta dados usados para melhorar sua qualidade e exige seu consentimento explícito: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"A promoção gratuita do {{model}} terminou. Você pode continuar usando o modelo assinando o OpenCode Go - {{link}}",

View file

@ -391,6 +391,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Modellen er deaktiveret",
"zen.api.error.regionNotAllowed":
"Den nyeste version af denne model er kun tilgængelig som hostet i Kina og kræver, at du aktivt tilvælger den: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Denne model er ikke tilgængelig i dit land.",
"zen.api.error.trainingNotAllowed":
"Denne model indsamler data, der bruges til at forbedre dens kvalitet, og kræver, at du aktivt tilvælger den: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"Den gratis kampagne for {{model}} er afsluttet. Du kan fortsætte med at bruge modellen ved at abonnere på OpenCode Go - {{link}}",

View file

@ -393,6 +393,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Modell ist deaktiviert",
"zen.api.error.regionNotAllowed":
"Die neueste Version dieses Modells ist nur verfügbar, wenn sie in China gehostet wird, und muss ausdrücklich aktiviert werden: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Dieses Modell ist in deinem Land nicht verfügbar.",
"zen.api.error.trainingNotAllowed":
"Dieses Modell erfasst Daten, die zur Verbesserung seiner Qualität verwendet werden, und erfordert deine ausdrückliche Zustimmung: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"Die kostenlose Aktion für {{model}} ist beendet. Du kannst das Modell weiterhin nutzen, indem du OpenCode Go abonnierst - {{link}}",

View file

@ -390,7 +390,8 @@ export const dict = {
"zen.api.error.modelDisabled": "Model is disabled",
"zen.api.error.regionNotAllowed":
"The latest version of this model is only available hosted in China and requires explicit opt in: {{consoleGoUrl}}",
"zen.api.error.nonZdrNotAllowed":
"zen.api.error.countryNotAllowed": "This model is not available in your country.",
"zen.api.error.trainingNotAllowed":
"This model collects data used to improve its quality and requires explicit opt in: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"Free promotion has ended for {{model}}. You can continue using the model by subscribing to OpenCode Go - {{link}}",

View file

@ -394,6 +394,9 @@ export const dict = {
"zen.api.error.modelDisabled": "El modelo está deshabilitado",
"zen.api.error.regionNotAllowed":
"La versión más reciente de este modelo solo está disponible alojada en China y requiere una aceptación explícita: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Este modelo no está disponible en tu país.",
"zen.api.error.trainingNotAllowed":
"Este modelo recopila datos que se utilizan para mejorar su calidad y requiere tu consentimiento explícito: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"La promoción gratuita de {{model}} ha finalizado. Puedes seguir usando el modelo suscribiéndote a OpenCode Go - {{link}}",

View file

@ -395,6 +395,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Le modèle est désactivé",
"zen.api.error.regionNotAllowed":
"La dernière version de ce modèle est uniquement disponible avec un hébergement en Chine et nécessite votre consentement explicite : {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Ce modèle nest pas disponible dans votre pays.",
"zen.api.error.trainingNotAllowed":
"Ce modèle collecte des données utilisées pour améliorer sa qualité et nécessite votre consentement explicite : {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"La promotion gratuite de {{model}} est terminée. Vous pouvez continuer à utiliser le modèle en vous abonnant à OpenCode Go - {{link}}",

View file

@ -390,6 +390,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Il modello è disabilitato",
"zen.api.error.regionNotAllowed":
"La versione più recente di questo modello è disponibile solo con hosting in Cina e richiede un consenso esplicito: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Questo modello non è disponibile nel tuo paese.",
"zen.api.error.trainingNotAllowed":
"Questo modello raccoglie dati utilizzati per migliorarne la qualità e richiede il tuo consenso esplicito: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"La promozione gratuita di {{model}} è terminata. Puoi continuare a usare il modello abbonandoti a OpenCode Go - {{link}}",

View file

@ -389,6 +389,9 @@ export const dict = {
"zen.api.error.modelDisabled": "モデルが無効です",
"zen.api.error.regionNotAllowed":
"このモデルの最新バージョンは中国でのみホスト提供されており、利用するには明示的なオプトインが必要です: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "このモデルはお住まいの国では利用できません。",
"zen.api.error.trainingNotAllowed":
"このモデルは品質向上のためのデータを収集します。利用するには明示的な同意が必要です: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"{{model}} の無料プロモーションは終了しました。OpenCode Go を購読するとモデルを引き続き使用できます - {{link}}",

View file

@ -383,6 +383,9 @@ export const dict = {
"zen.api.error.modelDisabled": "모델이 비활성화되었습니다",
"zen.api.error.regionNotAllowed":
"이 모델의 최신 버전은 중국에서 호스팅되는 경우에만 사용할 수 있으며, 명시적으로 사용에 동의해야 합니다: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "이 모델은 거주 국가에서 사용할 수 없습니다.",
"zen.api.error.trainingNotAllowed":
"이 모델은 품질 개선에 사용되는 데이터를 수집하며, 사용하려면 명시적으로 동의해야 합니다: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"{{model}}의 무료 프로모션이 종료되었습니다. OpenCode Go를 구독하면 모델을 계속 사용할 수 있습니다 - {{link}}",

View file

@ -392,6 +392,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Modellen er deaktivert",
"zen.api.error.regionNotAllowed":
"Den nyeste versjonen av denne modellen er bare tilgjengelig som en tjeneste driftet i Kina og krever at du uttrykkelig samtykker: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Denne modellen er ikke tilgjengelig i landet ditt.",
"zen.api.error.trainingNotAllowed":
"Denne modellen samler inn data som brukes til å forbedre modellens kvalitet, og krever at du uttrykkelig samtykker: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"Den gratis kampanjen for {{model}} er avsluttet. Du kan fortsette å bruke modellen ved å abonnere på OpenCode Go - {{link}}",

View file

@ -392,6 +392,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Model jest wyłączony",
"zen.api.error.regionNotAllowed":
"Najnowsza wersja tego modelu jest dostępna wyłącznie jako usługa hostowana w Chinach i wymaga jawnej zgody użytkownika: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Ten model nie jest dostępny w Twoim kraju.",
"zen.api.error.trainingNotAllowed":
"Ten model zbiera dane wykorzystywane do poprawy jego jakości i wymaga wyraźnej zgody użytkownika: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"Bezpłatna promocja {{model}} dobiegła końca. Możesz dalej korzystać z modelu, subskrybując OpenCode Go - {{link}}",

View file

@ -397,6 +397,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Модель отключена",
"zen.api.error.regionNotAllowed":
"Последняя версия этой модели размещена только в Китае. Чтобы использовать её, необходимо явно подтвердить согласие: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Эта модель недоступна в вашей стране.",
"zen.api.error.trainingNotAllowed":
"Эта модель собирает данные, которые используются для улучшения её качества, и требует явного согласия: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"Бесплатная акция для {{model}} завершена. Вы можете продолжить использование модели, подписавшись на OpenCode Go - {{link}}",

View file

@ -387,6 +387,9 @@ export const dict = {
"zen.api.error.modelDisabled": "โมเดลถูกปิดใช้งาน",
"zen.api.error.regionNotAllowed":
"โมเดลเวอร์ชันล่าสุดนี้ให้บริการเฉพาะผ่านระบบที่โฮสต์ในประเทศจีน และต้องให้ความยินยอมอย่างชัดแจ้งก่อนใช้งาน: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "โมเดลนี้ไม่พร้อมให้บริการในประเทศของคุณ",
"zen.api.error.trainingNotAllowed":
"โมเดลนี้เก็บรวบรวมข้อมูลที่ใช้เพื่อปรับปรุงคุณภาพของโมเดล และต้องได้รับความยินยอมอย่างชัดแจ้งก่อนใช้งาน: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"โปรโมชันฟรีสำหรับ {{model}} สิ้นสุดแล้ว คุณสามารถใช้โมเดลต่อได้โดยสมัครสมาชิก OpenCode Go - {{link}}",

View file

@ -395,6 +395,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Model devre dışı",
"zen.api.error.regionNotAllowed":
"Bu modelin en son sürümü yalnızca Çin'de barındırılıyor ve kullanabilmek için açıkça onay vermeniz gerekiyor: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Bu model ülkenizde kullanılamıyor.",
"zen.api.error.trainingNotAllowed":
"Bu model, kalitesini artırmak için kullanılan verileri toplar. Modeli kullanabilmek için açıkça onay vermeniz gerekir: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"{{model}} için ücretsiz promosyon sona erdi. OpenCode Go'ya abone olarak modeli kullanmaya devam edebilirsiniz - {{link}}",

View file

@ -391,6 +391,9 @@ export const dict = {
"zen.api.error.modelDisabled": "Модель вимкнено",
"zen.api.error.regionNotAllowed":
"Остання версія цієї моделі доступна лише на серверах у Китаї, і для її використання потрібно надати явну згоду: {{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "Ця модель недоступна у вашій країні.",
"zen.api.error.trainingNotAllowed":
"Ця модель збирає дані, які використовуються для покращення її якості; для її використання потрібна ваша явна згода: {{consoleGoUrl}}",
"zen.api.error.trialEnded":
"Безкоштовна акція для {{model}} закінчилася. Ви можете продовжити використання, підписавшись на OpenCode Go — {{link}}",

View file

@ -368,6 +368,8 @@ export const dict = {
"zen.api.error.userMonthlyLimitReached": "您已达到每月支出限额 ${{amount}}。请在此处管理您的限额:{{membersUrl}}",
"zen.api.error.modelDisabled": "模型已禁用",
"zen.api.error.regionNotAllowed": "该模型的最新版本仅在中国提供托管服务,需明确选择启用:{{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "该模型在你所在的国家或地区不可用。",
"zen.api.error.trainingNotAllowed": "该模型会收集用于提升其质量的数据,使用前需要你明确同意:{{consoleGoUrl}}",
"zen.api.error.trialEnded": "{{model}} 的限免活动已结束。您可以订阅 OpenCode Go 继续使用该模型 - {{link}}",
"black.meta.title": "OpenCode Black | 访问全球顶尖编程模型",

View file

@ -368,6 +368,8 @@ export const dict = {
"zen.api.error.userMonthlyLimitReached": "你已達到每月支出限額 ${{amount}}。請在此處管理你的限額:{{membersUrl}}",
"zen.api.error.modelDisabled": "模型已停用",
"zen.api.error.regionNotAllowed": "此模型的最新版本僅於中國託管,且需明確選擇啟用:{{consoleGoUrl}}",
"zen.api.error.countryNotAllowed": "此模型無法在你所在的國家或地區使用。",
"zen.api.error.trainingNotAllowed": "此模型會收集用於提升其品質的資料,使用前須經你明確同意:{{consoleGoUrl}}",
"zen.api.error.trialEnded": "{{model}} 的限免活动已結束。您可以訂閱 OpenCode Go 繼續使用該模型 - {{link}}",
"black.meta.title": "OpenCode Black | 存取全球最佳編碼模型",

View file

@ -1,5 +1,74 @@
const MUSE_SPARK_BLOCKED_COUNTRIES = new Set([
"AD",
"AF",
"AT",
"AU",
"BE",
"BG",
"BR",
"BY",
"CA",
"CH",
"CN",
"CU",
"CY",
"CZ",
"DE",
"DK",
"EE",
"EH",
"ER",
"ES",
"ET",
"FI",
"FR",
"GB",
"GR",
"HK",
"HR",
"HT",
"HU",
"IE",
"IQ",
"IR",
"IS",
"IT",
"KH",
"KP",
"KR",
"LI",
"LT",
"LU",
"LV",
"LY",
"MC",
"MM",
"MO",
"MT",
"NI",
"NL",
"NO",
"PK",
"PL",
"PT",
"RO",
"RU",
"SE",
"SI",
"SK",
"SM",
"SO",
"SY",
"VA",
"VE",
])
export function countryFromRequest(request: Request | undefined) {
if (!request) return undefined
const cloudflareRequest = request as Request & { cf?: { country?: string } }
return cloudflareRequest.cf?.country ?? request.headers.get("cf-ipcountry") ?? undefined
}
export function isModelCountryRestricted(model: string, country: string | undefined) {
return model === "muse-spark-1.2" && country !== undefined && MUSE_SPARK_BLOCKED_COUNTRIES.has(country.toUpperCase())
}

View file

@ -52,7 +52,7 @@ import { createModelTpsLimiter } from "./modelTpsLimiter"
import { createProviderBudgetTracker } from "./providerBudgetTracker"
import { accumulateUsage, HOT_WORKSPACES } from "./usageBatcher"
import { Workspace } from "@opencode-ai/console-core/workspace.js"
import { countryFromRequest } from "~/lib/request-country"
import { countryFromRequest, isModelCountryRestricted } from "~/lib/request-country"
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
type RetryOptions = {
@ -122,6 +122,8 @@ export async function handler(
})
const zenData = ZenData.list(opts.modelList)
const modelInfo = validateModel(zenData, model)
const country = countryFromRequest(input.request)
if (isModelCountryRestricted(modelInfo.id, country)) throw new RegionError(t("zen.api.error.countryNotAllowed"))
const trialLimiter = createTrialLimiter(modelInfo.trialProvider, ip)
const trialProviders = await trialLimiter?.check()
const rateLimiter = modelInfo.allowAnonymous
@ -131,7 +133,7 @@ export async function handler(
const authInfo = await authenticate(modelInfo, zenApiKey)
if (authInfo && opts.modelList === "lite" && modelInfo.id === "muse-spark-1.2" && !authInfo.allowTraining)
throw new DataPolicyError(
t("zen.api.error.nonZdrNotAllowed", {
t("zen.api.error.trainingNotAllowed", {
consoleGoUrl: `https://opencode.ai/workspace/${authInfo.workspaceID}/go`,
}),
)
@ -140,7 +142,7 @@ export async function handler(
: await (async () => {
if (!authInfo) return
return Actor.provide("system", { workspaceID: authInfo.workspaceID }, () =>
Workspace.setDefaultRegion({ country: countryFromRequest(input.request) }),
Workspace.setDefaultRegion({ country }),
)
})()
if (