mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
support jev
This commit is contained in:
@@ -8,9 +8,11 @@ const paths: Record<string, string | undefined> = {
|
||||
"POST /zen/v1/chat/completions": "/openai/v1/chat/completions",
|
||||
"POST /zen/v1/responses": "/openai/v1/responses",
|
||||
"POST /zen/v1/messages": "/anthropic/v1/messages",
|
||||
"POST /zen/systemone/v1/systemone": "/systemone/v1/systemone",
|
||||
"POST /zen/go/v1/chat/completions": "/go/openai/v1/chat/completions",
|
||||
"POST /zen/go/v1/responses": "/go/openai/v1/responses",
|
||||
"POST /zen/go/v1/messages": "/go/anthropic/v1/messages",
|
||||
"POST /zen/go/systemone/v1/systemone": "/go/systemone/v1/systemone",
|
||||
"GET /zen/v1/models": "/v1/models",
|
||||
"GET /zen/go/v1/models": "/go/v1/models",
|
||||
"GET /zen/go/v1/usage": "/go/v1/usage",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { handler } from "~/routes/zen/util/handler"
|
||||
|
||||
export function POST(input: APIEvent) {
|
||||
return handler(input, {
|
||||
format: "systemone",
|
||||
modelList: "lite",
|
||||
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
|
||||
parseModel: (url: string, body: any) => body.model,
|
||||
parseVariant: () => undefined,
|
||||
parseIsStream: () => false,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { handler } from "~/routes/zen/util/handler"
|
||||
|
||||
export function POST(input: APIEvent) {
|
||||
return handler(input, {
|
||||
format: "systemone",
|
||||
modelList: "full",
|
||||
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
|
||||
parseModel: (url: string, body: any) => body.model,
|
||||
parseVariant: () => undefined,
|
||||
parseIsStream: () => false,
|
||||
})
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import { anthropicHelper } from "./provider/anthropic"
|
||||
import { googleHelper } from "./provider/google"
|
||||
import { openaiHelper } from "./provider/openai"
|
||||
import { oaCompatHelper } from "./provider/openai-compatible"
|
||||
import { systemoneHelper } from "./provider/systemone"
|
||||
import { createRateLimiter as createIpRateLimiter } from "./ipRateLimiter"
|
||||
import { createRateLimiter as createKeyRateLimiter } from "./keyRateLimiter"
|
||||
import { createTrialLimiter } from "./trialLimiter"
|
||||
@@ -685,6 +686,7 @@ export async function handler(
|
||||
if (format === "anthropic") return anthropicHelper(opts)
|
||||
if (format === "google") return googleHelper(opts)
|
||||
if (format === "openai") return openaiHelper(opts)
|
||||
if (format === "systemone") return systemoneHelper(opts)
|
||||
return oaCompatHelper(opts)
|
||||
})(),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ProviderHelper } from "./provider"
|
||||
|
||||
type Usage = {
|
||||
input_tokens?: number
|
||||
output_tokens?: number
|
||||
}
|
||||
|
||||
export const systemoneHelper: ProviderHelper = () => ({
|
||||
format: "systemone",
|
||||
modifyUrl: (providerApi: string) => providerApi.replace(/\/$/, "") + "/systemone",
|
||||
modifyHeaders: (headers: Headers, apiKey: string, stickyId: string) => {
|
||||
headers.set("authorization", `Bearer ${apiKey}`)
|
||||
headers.set("x-session-affinity", stickyId)
|
||||
},
|
||||
modifyBody: (body: Record<string, any>) => body,
|
||||
createBinaryStreamDecoder: () => undefined,
|
||||
createUsageParser: () => ({
|
||||
parse: (_chunk: string) => {},
|
||||
retrieve: () => undefined,
|
||||
}),
|
||||
extractUsage: (response: any) => response.usage,
|
||||
normalizeUsage: (usage: Usage) => ({
|
||||
inputTokens: usage.input_tokens ?? 0,
|
||||
outputTokens: usage.output_tokens ?? 0,
|
||||
reasoningTokens: undefined,
|
||||
cacheReadTokens: undefined,
|
||||
cacheWrite5mTokens: undefined,
|
||||
cacheWrite1hTokens: undefined,
|
||||
}),
|
||||
})
|
||||
@@ -5,15 +5,27 @@ import { anthropicHelper } from "../src/routes/zen/util/provider/anthropic"
|
||||
import { googleHelper } from "../src/routes/zen/util/provider/google"
|
||||
import { oaCompatHelper } from "../src/routes/zen/util/provider/openai-compatible"
|
||||
import { openaiHelper } from "../src/routes/zen/util/provider/openai"
|
||||
import { systemoneHelper } from "../src/routes/zen/util/provider/systemone"
|
||||
|
||||
const providers = {
|
||||
anthropic: anthropicHelper({ reqModel: "claude-haiku-4-5", providerModel: "claude-haiku-4-5" }),
|
||||
google: googleHelper({ reqModel: "gemini-3-flash", providerModel: "gemini-3-flash" }),
|
||||
openai: openaiHelper({ reqModel: "gpt-5", providerModel: "gpt-5" }),
|
||||
"oa-compat": oaCompatHelper({ reqModel: "gpt-5-nano", providerModel: "gpt-5-nano" }),
|
||||
systemone: systemoneHelper({ reqModel: "jev-1.13", providerModel: "jev-latest" }),
|
||||
} satisfies Record<ZenData.Format, ReturnType<ProviderHelper>>
|
||||
|
||||
describe("provider usage extraction", () => {
|
||||
test("prepares SystemOne requests", () => {
|
||||
const headers = new Headers()
|
||||
providers.systemone.modifyHeaders(headers, "secret", "session")
|
||||
|
||||
expect(providers.systemone.modifyUrl("https://api.typesafe.ai/v1/"))
|
||||
.toBe("https://api.typesafe.ai/v1/systemone")
|
||||
expect(headers.get("authorization")).toBe("Bearer secret")
|
||||
expect(headers.get("x-session-affinity")).toBe("session")
|
||||
})
|
||||
|
||||
test("extracts Google non-stream usage metadata", () => {
|
||||
const usage = providers.google.extractUsage({
|
||||
usageMetadata: {
|
||||
@@ -66,6 +78,21 @@ describe("provider usage extraction", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("extracts SystemOne usage", () => {
|
||||
expect(
|
||||
providers.systemone.normalizeUsage(
|
||||
providers.systemone.extractUsage({ usage: { input_tokens: 312, output_tokens: 48 } }),
|
||||
),
|
||||
).toEqual({
|
||||
inputTokens: 312,
|
||||
outputTokens: 48,
|
||||
reasoningTokens: undefined,
|
||||
cacheReadTokens: undefined,
|
||||
cacheWrite5mTokens: undefined,
|
||||
cacheWrite1hTokens: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
test("parses OpenAI stream cache write usage", () => {
|
||||
const usageParser = providers.openai.createUsageParser()
|
||||
usageParser.parse(
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Actor } from "./actor"
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
|
||||
export namespace ZenData {
|
||||
const FormatSchema = z.enum(["anthropic", "google", "openai", "oa-compat"])
|
||||
const FormatSchema = z.enum(["anthropic", "google", "openai", "oa-compat", "systemone"])
|
||||
export type Format = z.infer<typeof FormatSchema>
|
||||
|
||||
const ModelCostSchema = z.object({
|
||||
|
||||
@@ -47,3 +47,18 @@ describe("ZenData cost200K threshold", () => {
|
||||
expect(entry(data).cost200K?.threshold).toBe(272_000)
|
||||
})
|
||||
})
|
||||
|
||||
test("accepts the SystemOne provider format", () => {
|
||||
expect(
|
||||
ZenData.validate({
|
||||
...base,
|
||||
providers: {
|
||||
systemone: {
|
||||
api: "https://api.typesafe.ai/v1",
|
||||
apiKey: "test",
|
||||
format: "systemone",
|
||||
},
|
||||
},
|
||||
}).providers.systemone.format,
|
||||
).toBe("systemone")
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user