summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFrank <[email protected]>2025-12-11 23:41:04 -0500
committerFrank <[email protected]>2025-12-11 23:41:04 -0500
commit57120e69edadb5dd6a03538ee8dfb85a228d5173 (patch)
tree8778c6788bc00a45b78dd94425b47bb9c1f9a8b6
parent11efda3f5caae86848478cebe479cab5f5fde002 (diff)
downloadopencode-57120e69edadb5dd6a03538ee8dfb85a228d5173.tar.gz
opencode-57120e69edadb5dd6a03538ee8dfb85a228d5173.zip
Zen: sync
-rw-r--r--infra/console.ts1
-rw-r--r--packages/console/app/src/routes/workspace/[id]/model-section.tsx7
-rw-r--r--packages/console/app/src/routes/zen/util/handler.ts15
-rw-r--r--packages/console/app/src/routes/zen/util/trialLimiter.ts18
-rwxr-xr-xpackages/console/core/script/promote-models.ts5
-rwxr-xr-xpackages/console/core/script/pull-models.ts5
-rwxr-xr-xpackages/console/core/script/update-models.ts11
-rw-r--r--packages/console/core/src/model.ts25
-rw-r--r--packages/console/core/sst-env.d.ts12
-rw-r--r--packages/console/function/sst-env.d.ts12
-rw-r--r--packages/console/resource/sst-env.d.ts12
-rw-r--r--packages/enterprise/sst-env.d.ts12
-rw-r--r--packages/function/sst-env.d.ts12
-rw-r--r--sst-env.d.ts12
14 files changed, 109 insertions, 50 deletions
diff --git a/infra/console.ts b/infra/console.ts
index 0a98ab072..8f54823f8 100644
--- a/infra/console.ts
+++ b/infra/console.ts
@@ -102,6 +102,7 @@ const ZEN_MODELS = [
new sst.Secret("ZEN_MODELS2"),
new sst.Secret("ZEN_MODELS3"),
new sst.Secret("ZEN_MODELS4"),
+ new sst.Secret("ZEN_MODELS5"),
]
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
diff --git a/packages/console/app/src/routes/workspace/[id]/model-section.tsx b/packages/console/app/src/routes/workspace/[id]/model-section.tsx
index 30815336d..e760ccea2 100644
--- a/packages/console/app/src/routes/workspace/[id]/model-section.tsx
+++ b/packages/console/app/src/routes/workspace/[id]/model-section.tsx
@@ -43,9 +43,12 @@ const getModelsInfo = query(async (workspaceID: string) => {
const pA = getPriority(idA)
const pB = getPriority(idB)
if (pA !== pB) return pA - pB
- return modelA.name.localeCompare(modelB.name)
+
+ const modelAName = Array.isArray(modelA) ? modelA[0].name : modelA.name
+ const modelBName = Array.isArray(modelB) ? modelB[0].name : modelB.name
+ return modelAName.localeCompare(modelBName)
})
- .map(([id, model]) => ({ id, name: model.name })),
+ .map(([id, model]) => ({ id, name: Array.isArray(model) ? model[0].name : model.name })),
disabled: await Model.listDisabled(),
}
}, workspaceID)
diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts
index 7d7767b8d..5e9c877cc 100644
--- a/packages/console/app/src/routes/zen/util/handler.ts
+++ b/packages/console/app/src/routes/zen/util/handler.ts
@@ -57,15 +57,17 @@ export async function handler(
const sessionId = input.request.headers.get("x-opencode-session") ?? ""
const requestId = input.request.headers.get("x-opencode-request") ?? ""
const projectId = input.request.headers.get("x-opencode-project") ?? ""
+ const ocClient = input.request.headers.get("x-opencode-client") ?? ""
logger.metric({
is_tream: isStream,
session: sessionId,
request: requestId,
+ client: ocClient,
})
const zenData = ZenData.list()
const modelInfo = validateModel(zenData, model)
const dataDumper = createDataDumper(sessionId, requestId, projectId)
- const trialLimiter = createTrialLimiter(modelInfo.trial?.limit, ip)
+ const trialLimiter = createTrialLimiter(modelInfo.trial, ip, ocClient)
const isTrial = await trialLimiter?.isTrial()
const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
await rateLimiter?.check()
@@ -286,11 +288,14 @@ export async function handler(
}
function validateModel(zenData: ZenData, reqModel: string) {
- if (!(reqModel in zenData.models)) {
- throw new ModelError(`Model ${reqModel} not supported`)
- }
+ if (!(reqModel in zenData.models)) throw new ModelError(`Model ${reqModel} not supported`)
+
const modelId = reqModel as keyof typeof zenData.models
- const modelData = zenData.models[modelId]
+ const modelData = Array.isArray(zenData.models[modelId])
+ ? zenData.models[modelId].find((model) => opts.format === model.formatFilter)
+ : zenData.models[modelId]
+
+ if (!modelData) throw new ModelError(`Model ${reqModel} not supported for format ${opts.format}`)
logger.metric({ model: modelId })
diff --git a/packages/console/app/src/routes/zen/util/trialLimiter.ts b/packages/console/app/src/routes/zen/util/trialLimiter.ts
index 15561c9f6..531e5cf0c 100644
--- a/packages/console/app/src/routes/zen/util/trialLimiter.ts
+++ b/packages/console/app/src/routes/zen/util/trialLimiter.ts
@@ -1,12 +1,18 @@
import { Database, eq, sql } from "@opencode-ai/console-core/drizzle/index.js"
import { IpTable } from "@opencode-ai/console-core/schema/ip.sql.js"
import { UsageInfo } from "./provider/provider"
+import { ZenData } from "@opencode-ai/console-core/model.js"
-export function createTrialLimiter(limit: number | undefined, ip: string) {
- if (!limit) return
+export function createTrialLimiter(trial: ZenData.Trial | undefined, ip: string, client: string) {
+ if (!trial) return
if (!ip) return
- let trial: boolean
+ const limit =
+ trial.limits.find((limit) => limit.client === client)?.limit ??
+ trial.limits.find((limit) => limit.client === undefined)?.limit
+ if (!limit) return
+
+ let _isTrial: boolean
return {
isTrial: async () => {
@@ -20,11 +26,11 @@ export function createTrialLimiter(limit: number | undefined, ip: string) {
.then((rows) => rows[0]),
)
- trial = (data?.usage ?? 0) < limit
- return trial
+ _isTrial = (data?.usage ?? 0) < limit
+ return _isTrial
},
track: async (usageInfo: UsageInfo) => {
- if (!trial) return
+ if (!_isTrial) return
const usage =
usageInfo.inputTokens +
usageInfo.outputTokens +
diff --git a/packages/console/core/script/promote-models.ts b/packages/console/core/script/promote-models.ts
index 0ff859df8..bebef5cfb 100755
--- a/packages/console/core/script/promote-models.ts
+++ b/packages/console/core/script/promote-models.ts
@@ -16,16 +16,19 @@ const value1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[
const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
+const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1]
if (!value1) throw new Error("ZEN_MODELS1 not found")
if (!value2) throw new Error("ZEN_MODELS2 not found")
if (!value3) throw new Error("ZEN_MODELS3 not found")
if (!value4) throw new Error("ZEN_MODELS4 not found")
+if (!value5) throw new Error("ZEN_MODELS5 not found")
// validate value
-ZenData.validate(JSON.parse(value1 + value2 + value3 + value4))
+ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5))
// update the secret
await $`bun sst secret set ZEN_MODELS1 ${value1} --stage ${stage}`
await $`bun sst secret set ZEN_MODELS2 ${value2} --stage ${stage}`
await $`bun sst secret set ZEN_MODELS3 ${value3} --stage ${stage}`
await $`bun sst secret set ZEN_MODELS4 ${value4} --stage ${stage}`
+await $`bun sst secret set ZEN_MODELS5 ${value5} --stage ${stage}`
diff --git a/packages/console/core/script/pull-models.ts b/packages/console/core/script/pull-models.ts
index a89e3951c..afa865625 100755
--- a/packages/console/core/script/pull-models.ts
+++ b/packages/console/core/script/pull-models.ts
@@ -16,16 +16,19 @@ const value1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[
const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
+const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1]
if (!value1) throw new Error("ZEN_MODELS1 not found")
if (!value2) throw new Error("ZEN_MODELS2 not found")
if (!value3) throw new Error("ZEN_MODELS3 not found")
if (!value4) throw new Error("ZEN_MODELS4 not found")
+if (!value5) throw new Error("ZEN_MODELS5 not found")
// validate value
-ZenData.validate(JSON.parse(value1 + value2 + value3 + value4))
+ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5))
// update the secret
await $`bun sst secret set ZEN_MODELS1 ${value1}`
await $`bun sst secret set ZEN_MODELS2 ${value2}`
await $`bun sst secret set ZEN_MODELS3 ${value3}`
await $`bun sst secret set ZEN_MODELS4 ${value4}`
+await $`bun sst secret set ZEN_MODELS5 ${value5}`
diff --git a/packages/console/core/script/update-models.ts b/packages/console/core/script/update-models.ts
index a8523a5f2..5d40b4d5a 100755
--- a/packages/console/core/script/update-models.ts
+++ b/packages/console/core/script/update-models.ts
@@ -14,15 +14,17 @@ const oldValue1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=
const oldValue2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1]
const oldValue3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
const oldValue4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
+const oldValue5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1]
if (!oldValue1) throw new Error("ZEN_MODELS1 not found")
if (!oldValue2) throw new Error("ZEN_MODELS2 not found")
if (!oldValue3) throw new Error("ZEN_MODELS3 not found")
if (!oldValue4) throw new Error("ZEN_MODELS4 not found")
+if (!oldValue5) throw new Error("ZEN_MODELS5 not found")
// store the prettified json to a temp file
const filename = `models-${Date.now()}.json`
const tempFile = Bun.file(path.join(os.tmpdir(), filename))
-await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4), null, 2))
+await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4 + oldValue5), null, 2))
console.log("tempFile", tempFile.name)
// open temp file in vim and read the file on close
@@ -31,12 +33,15 @@ const newValue = JSON.stringify(JSON.parse(await tempFile.text()))
ZenData.validate(JSON.parse(newValue))
// update the secret
-const chunk = Math.ceil(newValue.length / 4)
+const chunk = Math.ceil(newValue.length / 5)
const newValue1 = newValue.slice(0, chunk)
const newValue2 = newValue.slice(chunk, chunk * 2)
const newValue3 = newValue.slice(chunk * 2, chunk * 3)
-const newValue4 = newValue.slice(chunk * 3)
+const newValue4 = newValue.slice(chunk * 3, chunk * 4)
+const newValue5 = newValue.slice(chunk * 4)
+
await $`bun sst secret set ZEN_MODELS1 ${newValue1}`
await $`bun sst secret set ZEN_MODELS2 ${newValue2}`
await $`bun sst secret set ZEN_MODELS3 ${newValue3}`
await $`bun sst secret set ZEN_MODELS4 ${newValue4}`
+await $`bun sst secret set ZEN_MODELS5 ${newValue5}`
diff --git a/packages/console/core/src/model.ts b/packages/console/core/src/model.ts
index 47ba3e9d8..55d6c895c 100644
--- a/packages/console/core/src/model.ts
+++ b/packages/console/core/src/model.ts
@@ -9,7 +9,17 @@ import { Resource } from "@opencode-ai/console-resource"
export namespace ZenData {
const FormatSchema = z.enum(["anthropic", "google", "openai", "oa-compat"])
+ const TrialSchema = z.object({
+ provider: z.string(),
+ limits: z.array(
+ z.object({
+ limit: z.number(),
+ client: z.enum(["cli", "desktop"]).optional(),
+ }),
+ ),
+ })
export type Format = z.infer<typeof FormatSchema>
+ export type Trial = z.infer<typeof TrialSchema>
const ModelCostSchema = z.object({
input: z.number(),
@@ -26,12 +36,7 @@ export namespace ZenData {
allowAnonymous: z.boolean().optional(),
byokProvider: z.enum(["openai", "anthropic", "google"]).optional(),
stickyProvider: z.boolean().optional(),
- trial: z
- .object({
- limit: z.number(),
- provider: z.string(),
- })
- .optional(),
+ trial: TrialSchema.optional(),
rateLimit: z.number().optional(),
fallbackProvider: z.string().optional(),
providers: z.array(
@@ -53,7 +58,7 @@ export namespace ZenData {
})
const ModelsSchema = z.object({
- models: z.record(z.string(), ModelSchema),
+ models: z.record(z.string(), z.union([ModelSchema, z.array(ModelSchema.extend({ formatFilter: FormatSchema }))])),
providers: z.record(z.string(), ProviderSchema),
})
@@ -63,7 +68,11 @@ export namespace ZenData {
export const list = fn(z.void(), () => {
const json = JSON.parse(
- Resource.ZEN_MODELS1.value + Resource.ZEN_MODELS2.value + Resource.ZEN_MODELS3.value + Resource.ZEN_MODELS4.value,
+ Resource.ZEN_MODELS1.value +
+ Resource.ZEN_MODELS2.value +
+ Resource.ZEN_MODELS3.value +
+ Resource.ZEN_MODELS4.value +
+ Resource.ZEN_MODELS5.value,
)
return ModelsSchema.parse(json)
})
diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts
index 0b09bfd0c..632ea3fbe 100644
--- a/packages/console/core/sst-env.d.ts
+++ b/packages/console/core/sst-env.d.ts
@@ -50,10 +50,6 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
- "Enterprise": {
- "type": "sst.cloudflare.SolidStart"
- "url": string
- }
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
@@ -94,6 +90,10 @@ declare module "sst" {
"type": "sst.sst.Linkable"
"value": string
}
+ "Teams": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
+ }
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
@@ -114,6 +114,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
+ "ZEN_MODELS5": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
}
}
// cloudflare
diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts
index 0b09bfd0c..632ea3fbe 100644
--- a/packages/console/function/sst-env.d.ts
+++ b/packages/console/function/sst-env.d.ts
@@ -50,10 +50,6 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
- "Enterprise": {
- "type": "sst.cloudflare.SolidStart"
- "url": string
- }
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
@@ -94,6 +90,10 @@ declare module "sst" {
"type": "sst.sst.Linkable"
"value": string
}
+ "Teams": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
+ }
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
@@ -114,6 +114,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
+ "ZEN_MODELS5": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
}
}
// cloudflare
diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts
index 0b09bfd0c..632ea3fbe 100644
--- a/packages/console/resource/sst-env.d.ts
+++ b/packages/console/resource/sst-env.d.ts
@@ -50,10 +50,6 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
- "Enterprise": {
- "type": "sst.cloudflare.SolidStart"
- "url": string
- }
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
@@ -94,6 +90,10 @@ declare module "sst" {
"type": "sst.sst.Linkable"
"value": string
}
+ "Teams": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
+ }
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
@@ -114,6 +114,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
+ "ZEN_MODELS5": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
}
}
// cloudflare
diff --git a/packages/enterprise/sst-env.d.ts b/packages/enterprise/sst-env.d.ts
index 0b09bfd0c..632ea3fbe 100644
--- a/packages/enterprise/sst-env.d.ts
+++ b/packages/enterprise/sst-env.d.ts
@@ -50,10 +50,6 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
- "Enterprise": {
- "type": "sst.cloudflare.SolidStart"
- "url": string
- }
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
@@ -94,6 +90,10 @@ declare module "sst" {
"type": "sst.sst.Linkable"
"value": string
}
+ "Teams": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
+ }
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
@@ -114,6 +114,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
+ "ZEN_MODELS5": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
}
}
// cloudflare
diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts
index 0b09bfd0c..632ea3fbe 100644
--- a/packages/function/sst-env.d.ts
+++ b/packages/function/sst-env.d.ts
@@ -50,10 +50,6 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
- "Enterprise": {
- "type": "sst.cloudflare.SolidStart"
- "url": string
- }
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
@@ -94,6 +90,10 @@ declare module "sst" {
"type": "sst.sst.Linkable"
"value": string
}
+ "Teams": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
+ }
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
@@ -114,6 +114,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
+ "ZEN_MODELS5": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
}
}
// cloudflare
diff --git a/sst-env.d.ts b/sst-env.d.ts
index 247b0ba81..2c182ec35 100644
--- a/sst-env.d.ts
+++ b/sst-env.d.ts
@@ -65,10 +65,6 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
- "Enterprise": {
- "type": "sst.cloudflare.SolidStart"
- "url": string
- }
"EnterpriseStorage": {
"name": string
"type": "sst.cloudflare.Bucket"
@@ -120,6 +116,10 @@ declare module "sst" {
"type": "sst.sst.Linkable"
"value": string
}
+ "Teams": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
+ }
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
@@ -140,6 +140,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
+ "ZEN_MODELS5": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
"ZenData": {
"name": string
"type": "sst.cloudflare.Bucket"