mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-28 11:37:37 +00:00
Compare commits
5
Commits
overflow-reason
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7076a878a4 | ||
|
|
45b91eed82 | ||
|
|
39e1ce55bc | ||
|
|
d9f54392ba | ||
|
|
d73396ab3d |
@@ -64,6 +64,14 @@ const OpenAIChatTool = Schema.Struct({
|
||||
})
|
||||
type OpenAIChatTool = Schema.Schema.Type<typeof OpenAIChatTool>
|
||||
|
||||
// Gemini's OpenAI-compatible surface carries thought signatures in tool call
|
||||
// `extra_content` and rejects replayed parallel calls without them:
|
||||
// https://ai.google.dev/gemini-api/docs/thinking#signatures
|
||||
const ExtraContent = Schema.Struct({
|
||||
google: Schema.Struct({ thought_signature: Schema.String }),
|
||||
})
|
||||
const decodeExtraContent = (value: unknown) => Option.getOrUndefined(Schema.decodeUnknownOption(ExtraContent)(value))
|
||||
|
||||
const OpenAIChatAssistantToolCall = Schema.Struct({
|
||||
id: Schema.String,
|
||||
type: Schema.tag("function"),
|
||||
@@ -71,6 +79,7 @@ const OpenAIChatAssistantToolCall = Schema.Struct({
|
||||
name: Schema.String,
|
||||
arguments: Schema.String,
|
||||
}),
|
||||
extra_content: Schema.optional(ExtraContent),
|
||||
})
|
||||
type OpenAIChatAssistantToolCall = Schema.Schema.Type<typeof OpenAIChatAssistantToolCall>
|
||||
|
||||
@@ -112,12 +121,6 @@ const decodeReasoningDetail = Schema.decodeUnknownOption(ReasoningDetail)
|
||||
const knownReasoningDetails = (details: ReadonlyArray<unknown>) =>
|
||||
details.flatMap((detail) => Option.toArray(decodeReasoningDetail(detail)))
|
||||
|
||||
// Intentionally omit Gemini's provider-specific `extra_content.google.thought_signature`
|
||||
// extension until direct Google OpenAI-compatible routing is supported here:
|
||||
// https://github.com/vercel/ai/issues/11590
|
||||
// https://github.com/vercel/ai/pull/11745
|
||||
// https://ai.google.dev/gemini-api/docs/thought-signatures#openai
|
||||
|
||||
const OpenAIChatUserContent = Schema.Union([
|
||||
Schema.Struct({
|
||||
type: Schema.Literal("text"),
|
||||
@@ -242,6 +245,7 @@ const OpenAIChatToolCallDelta = Schema.Struct({
|
||||
index: optionalNull(Schema.Number),
|
||||
id: optionalNull(Schema.String),
|
||||
function: optionalNull(OpenAIChatToolCallDeltaFunction),
|
||||
extra_content: optionalNull(Schema.Unknown),
|
||||
})
|
||||
type OpenAIChatToolCallDelta = Schema.Schema.Type<typeof OpenAIChatToolCallDelta>
|
||||
|
||||
@@ -294,6 +298,7 @@ interface PendingToolDelta {
|
||||
readonly id?: string
|
||||
readonly name?: string
|
||||
readonly input: string
|
||||
readonly extraContent?: Schema.Schema.Type<typeof ExtraContent>
|
||||
}
|
||||
|
||||
export interface ParserState {
|
||||
@@ -347,13 +352,17 @@ const lowerToolChoice = (toolChoice: NonNullable<LLMRequest["toolChoice"]>) =>
|
||||
tool: (name) => ({ type: "function" as const, function: { name } }),
|
||||
})
|
||||
|
||||
const lowerToolCall = (part: ToolCallPart, options: LoweringOptions): OpenAIChatAssistantToolCall => ({
|
||||
const lowerToolCall = (
|
||||
part: ToolCallPart,
|
||||
options: LoweringOptions & { readonly providerMetadataKey: string },
|
||||
): OpenAIChatAssistantToolCall => ({
|
||||
id: options.toolCallID?.(part.id) ?? part.id,
|
||||
type: "function",
|
||||
function: {
|
||||
name: part.name,
|
||||
arguments: ProviderShared.encodeJson(part.input === undefined ? {} : part.input),
|
||||
},
|
||||
extra_content: decodeExtraContent(part.providerMetadata?.[options.providerMetadataKey]?.extraContent),
|
||||
})
|
||||
|
||||
const lowerMedia = Effect.fn("OpenAIChat.lowerMedia")(function* (part: MediaPart) {
|
||||
@@ -721,7 +730,9 @@ const detectSupportsStore = (provider: string, baseURL: string | undefined): boo
|
||||
p === "vercel-ai-gateway" || url.includes("ai-gateway.vercel.sh") || url.includes("vercel.sh")
|
||||
const isAntLing = p === "ant-ling" || url.includes("api.ant-ling.com")
|
||||
const isOpencode = p === "opencode" || url.includes("opencode.ai")
|
||||
const isGemini = url.includes("generativelanguage.googleapis.com")
|
||||
const isNonStandard =
|
||||
isGemini ||
|
||||
isNvidia ||
|
||||
isCerebras ||
|
||||
isXai ||
|
||||
@@ -1114,12 +1125,13 @@ const step = (state: ParserState, event: OpenAIChatEvent) =>
|
||||
const id = current?.id ?? pending?.id ?? (tool.id || undefined)
|
||||
const name = current?.name ?? pending?.name ?? (tool.function?.name || undefined)
|
||||
const text = `${pending?.input ?? ""}${tool.function?.arguments ?? ""}`
|
||||
const extraContent = pending?.extraContent ?? decodeExtraContent(tool.extra_content)
|
||||
latestToolIndex = index
|
||||
nextToolIndex = Math.max(nextToolIndex, index + 1)
|
||||
if (!current && (!id || !name)) {
|
||||
pendingTools = {
|
||||
...pendingTools,
|
||||
[index]: { id: id || undefined, name: name || undefined, input: text },
|
||||
[index]: { id: id || undefined, name: name || undefined, input: text, extraContent },
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -1131,7 +1143,12 @@ const step = (state: ParserState, event: OpenAIChatEvent) =>
|
||||
ADAPTER,
|
||||
tools,
|
||||
index,
|
||||
{ id: id || undefined, name: name || undefined, text },
|
||||
{
|
||||
id: id || undefined,
|
||||
name: name || undefined,
|
||||
text,
|
||||
providerMetadata: extraContent && { [state.providerMetadataKey]: { extraContent } },
|
||||
},
|
||||
"OpenAI Chat tool call delta is missing id or name",
|
||||
)
|
||||
if (ToolStream.isError(result))
|
||||
|
||||
@@ -147,7 +147,12 @@ export const appendOrStart = <K extends StreamKey>(
|
||||
route: string,
|
||||
tools: State<K>,
|
||||
key: K,
|
||||
delta: { readonly id?: string; readonly name?: string; readonly text: string },
|
||||
delta: {
|
||||
readonly id?: string
|
||||
readonly name?: string
|
||||
readonly text: string
|
||||
readonly providerMetadata?: ProviderMetadata
|
||||
},
|
||||
missingToolMessage: string,
|
||||
): AppendOutcome<K> | AIError => {
|
||||
const current = tools[key]
|
||||
@@ -161,7 +166,7 @@ export const appendOrStart = <K extends StreamKey>(
|
||||
namespace: current?.namespace,
|
||||
input: `${current?.input ?? ""}${delta.text}`,
|
||||
providerExecuted: current?.providerExecuted,
|
||||
providerMetadata: current?.providerMetadata,
|
||||
providerMetadata: current?.providerMetadata ?? delta.providerMetadata,
|
||||
}
|
||||
if (current && delta.text.length === 0 && current.id === id && current.name === name)
|
||||
return { tools, tool: current, events: [] }
|
||||
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"model": "gemini-3.8-flash",
|
||||
"tags": [
|
||||
"prefix:openai-compatible-chat",
|
||||
"provider:google",
|
||||
"protocol:openai-chat",
|
||||
"tool",
|
||||
"tool-loop",
|
||||
"continuation"
|
||||
],
|
||||
"name": "gemini-parallel-tool-signatures",
|
||||
"recordedAt": "2026-09-28T03:12:05.083Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"gemini-3.8-flash\",\"messages\":[{\"role\":\"system\",\"content\":\"Call get_weather for every requested city in parallel, then answer in one short sentence.\"},{\"role\":\"user\",\"content\":\"What is the weather in Paris and in Tokyo?\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}}],\"stream\":true,\"stream_options\":{\"include_usage\":true}}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"role\":\"assistant\",\"tool_calls\":[{\"extra_content\":{\"google\":{\"thought_signature\":\"ErYCCrMCAWkUfRNGh+/Zbc8YUSzk1yfWAfROcjA4HyF1x69jz6167w8zd4n6kZQQ5FDeBZ5HZMEbEkQ4ENOpzsQL8roCR6wONkhXpiduWrTD6XwbP8KGNkf6D1tX/JlBh7G5Cl+0rdjiSOl/mdY1lcjbkfyCRFs5T8odNWMG7WD3rCrXJDFQ/5QfOl+tqVTceKGz2yyXBhOhvsQDU33ulR9tHQJo/Fmx3HNyDdwvmyKUXm+kgqHsYZnkdv6y6xZwy9zBXGUO4QwxelMw3Rrc24Mp2rNbEDZS3YEeP72Jn/hIP5a8XWOx6+zop/4/CjYxxSfN/tJRY5d48NAKRNFzUN1Az8SvAs/N5X2I3/5ZMaDnQWW/BVdS9fm2KFYJ2baqtBiRRnJxMq4ELArkKjGZzHpd97XG8YjV6g==\"}},\"function\":{\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"name\":\"get_weather\"},\"id\":\"call_723181\",\"type\":\"function\"}]},\"index\":0}],\"created\":1790565123,\"id\":\"A9u5aoufBp3rz7IPke_3oAo\",\"model\":\"gemini-3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":16,\"prompt_tokens\":74,\"total_tokens\":132}}\n\ndata: {\"choices\":[{\"delta\":{\"role\":\"assistant\",\"tool_calls\":[{\"function\":{\"arguments\":\"{\\\"city\\\":\\\"Tokyo\\\"}\",\"name\":\"get_weather\"},\"id\":\"call_723184\",\"type\":\"function\"}]},\"index\":0}],\"created\":1790565123,\"id\":\"A9u5aoufBp3rz7IPke_3oAo\",\"model\":\"gemini-3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":32,\"prompt_tokens\":74,\"total_tokens\":148}}\n\ndata: {\"choices\":[{\"delta\":{\"role\":\"assistant\"},\"finish_reason\":\"stop\",\"index\":0}],\"created\":1790565124,\"id\":\"A9u5aoufBp3rz7IPke_3oAo\",\"model\":\"gemini-3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":32,\"prompt_tokens\":74,\"total_tokens\":148}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"gemini-3.8-flash\",\"messages\":[{\"role\":\"system\",\"content\":\"Call get_weather for every requested city in parallel, then answer in one short sentence.\"},{\"role\":\"user\",\"content\":\"What is the weather in Paris and in Tokyo?\"},{\"role\":\"assistant\",\"content\":null,\"tool_calls\":[{\"id\":\"call_723181\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"},\"extra_content\":{\"google\":{\"thought_signature\":\"ErYCCrMCAWkUfRNGh+/Zbc8YUSzk1yfWAfROcjA4HyF1x69jz6167w8zd4n6kZQQ5FDeBZ5HZMEbEkQ4ENOpzsQL8roCR6wONkhXpiduWrTD6XwbP8KGNkf6D1tX/JlBh7G5Cl+0rdjiSOl/mdY1lcjbkfyCRFs5T8odNWMG7WD3rCrXJDFQ/5QfOl+tqVTceKGz2yyXBhOhvsQDU33ulR9tHQJo/Fmx3HNyDdwvmyKUXm+kgqHsYZnkdv6y6xZwy9zBXGUO4QwxelMw3Rrc24Mp2rNbEDZS3YEeP72Jn/hIP5a8XWOx6+zop/4/CjYxxSfN/tJRY5d48NAKRNFzUN1Az8SvAs/N5X2I3/5ZMaDnQWW/BVdS9fm2KFYJ2baqtBiRRnJxMq4ELArkKjGZzHpd97XG8YjV6g==\"}}},{\"id\":\"call_723184\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Tokyo\\\"}\"}}]},{\"role\":\"tool\",\"tool_call_id\":\"call_723181\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"},{\"role\":\"tool\",\"tool_call_id\":\"call_723184\",\"content\":\"{\\\"temperature\\\":0,\\\"condition\\\":\\\"unknown\\\"}\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}}],\"stream\":true,\"stream_options\":{\"include_usage\":true}}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"It is currently sunny and 22°C in Paris,\",\"role\":\"assistant\"},\"index\":0}],\"created\":1790565125,\"id\":\"BNu5auWeB-2fz7IPxY6l4QY\",\"model\":\"gemini-3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":13,\"prompt_tokens\":150,\"total_tokens\":226}}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\" while Tokyo is 0°C.\",\"role\":\"assistant\"},\"index\":0}],\"created\":1790565125,\"id\":\"BNu5auWeB-2fz7IPxY6l4QY\",\"model\":\"gemini-3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":21,\"prompt_tokens\":150,\"total_tokens\":234}}\n\ndata: {\"choices\":[{\"delta\":{\"extra_content\":{\"google\":{\"thought_signature\":\"EpcDCpQDAWkUfRM325TAUtfFiOeQIEWn/TsCU9oi2js4VPHFeeLfAu+2k8PJm0fN/OaF0y4ovau7S9QIAsuOPI2w2aIyQ2kMGj1XvUyRTvz30DOZgtq1km6W6YGzZyyTCNSeBcpwtJtziHZVVWq9xEI/HHB8Ta1Ot215xnFyDL7iUGEwgGu45/mInpk+SOCYBy9biDddpDxcDi14BGoleArY9XEFAzYLxXssl7HMWjpfee5095im7gD125Nripq1Jf3nGY/2TxqjgQAdJpQybwct63p74O1szGHQxrkBt7AwphDgbOWtLpUP/QJBRdl8qhrozqRe611NQ6V5lMSwpO7OhQ/IDRtWMwOyrrKblZfmMnnPl2/9xDfZRsYnfmWq+7PeAptJl1cDRlMBKhj5iRn31xvN43EiuvWwWPsSndiWvxrMvVBd89TR4u0+z0pYCYZcsFkYKFlPA7pZsdnh6CON24AA5WUkO4dQNoqYdik6aO5hE9jLHG5FHTdr0W69qgPNozbxnO0ptRcOtkGpB9xYUVoTxcSXXZE=\"}},\"role\":\"assistant\"},\"finish_reason\":\"stop\",\"index\":0}],\"created\":1790565125,\"id\":\"BNu5auWeB-2fz7IPxY6l4QY\",\"model\":\"gemini-3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":21,\"prompt_tokens\":192,\"total_tokens\":276}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { LLM, LLMEvent, LLMRequest, Message, ToolRuntime, toDefinitions } from "../../src/index.js"
|
||||
import * as OpenAICompatible from "../../src/providers/openai-compatible.js"
|
||||
import { LLMClient } from "../../src/route.js"
|
||||
import { compileRequest } from "../../src/route/client.js"
|
||||
import { recordedTests } from "../recorded-test.js"
|
||||
import { weatherRuntimeTool, weatherToolName } from "../recorded-scenarios.js"
|
||||
|
||||
const model = OpenAICompatible.configure({
|
||||
provider: "google",
|
||||
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai",
|
||||
apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY ?? "fixture",
|
||||
}).model("gemini-3.8-flash")
|
||||
|
||||
const recorded = recordedTests({
|
||||
prefix: "openai-compatible-chat",
|
||||
provider: "google",
|
||||
protocol: "openai-chat",
|
||||
requires: ["GOOGLE_GENERATIVE_AI_API_KEY"],
|
||||
tags: ["tool", "tool-loop", "continuation"],
|
||||
metadata: { model: model.id },
|
||||
})
|
||||
|
||||
describe("Gemini OpenAI-compatible Chat recorded", () => {
|
||||
recorded.effect.with(
|
||||
"replays thought signatures through a parallel tool loop",
|
||||
{ cassette: "openai-compatible-chat/gemini-parallel-tool-signatures" },
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const tools = { [weatherToolName]: weatherRuntimeTool }
|
||||
const request = LLM.request({
|
||||
model,
|
||||
system: "Call get_weather for every requested city in parallel, then answer in one short sentence.",
|
||||
prompt: "What is the weather in Paris and in Tokyo?",
|
||||
tools: toDefinitions(tools),
|
||||
cache: "none",
|
||||
})
|
||||
const first = yield* LLMClient.generate(request)
|
||||
const calls = first.events.filter(LLMEvent.is.toolCall)
|
||||
expect(calls.map((call) => call.input)).toEqual([{ city: "Paris" }, { city: "Tokyo" }])
|
||||
const extraContent = calls[0]?.providerMetadata?.google?.extraContent
|
||||
expect(extraContent).toEqual({ google: { thought_signature: expect.any(String) } })
|
||||
|
||||
const results = yield* Effect.forEach(calls, (call) => ToolRuntime.dispatch(tools, call))
|
||||
const continuation = LLMRequest.update(request, {
|
||||
messages: [
|
||||
...request.messages,
|
||||
first.message,
|
||||
...calls.map((call, index) =>
|
||||
Message.tool({ id: call.id, name: call.name, result: results[index]!.result }),
|
||||
),
|
||||
],
|
||||
})
|
||||
const prepared = yield* compileRequest(continuation)
|
||||
const assistant = prepared.body.messages.find((message) => message.role === "assistant")
|
||||
expect(assistant?.role === "assistant" ? assistant.tool_calls?.[0]?.extra_content : undefined).toEqual(
|
||||
extraContent,
|
||||
)
|
||||
|
||||
const second = yield* LLMClient.generate(continuation)
|
||||
expect(second.events.filter(LLMEvent.is.toolCall)).toHaveLength(0)
|
||||
expect(second.text).toMatch(/Paris/)
|
||||
expect(second.text).toMatch(/Tokyo/)
|
||||
}),
|
||||
60_000,
|
||||
)
|
||||
})
|
||||
@@ -472,6 +472,45 @@ describe("OpenAI Chat route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("replays Gemini thought signatures as tool call extra content", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model,
|
||||
messages: [
|
||||
Message.user("Weather in Paris and Tokyo?"),
|
||||
Message.assistant([
|
||||
ToolCallPart.make({
|
||||
id: "call_1",
|
||||
name: "lookup",
|
||||
input: { city: "Paris" },
|
||||
providerMetadata: { openai: { extraContent: { google: { thought_signature: "sig_1" } } } },
|
||||
}),
|
||||
ToolCallPart.make({ id: "call_2", name: "lookup", input: { city: "Tokyo" } }),
|
||||
]),
|
||||
Message.tool({ id: "call_1", name: "lookup", result: "Sunny" }),
|
||||
Message.tool({ id: "call_2", name: "lookup", result: "Rainy" }),
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
const assistant = prepared.body.messages[1]
|
||||
expect(assistant?.role === "assistant" ? assistant.tool_calls : undefined).toEqual([
|
||||
{
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: { name: "lookup", arguments: encodeJson({ city: "Paris" }) },
|
||||
extra_content: { google: { thought_signature: "sig_1" } },
|
||||
},
|
||||
{
|
||||
id: "call_2",
|
||||
type: "function",
|
||||
function: { name: "lookup", arguments: encodeJson({ city: "Tokyo" }) },
|
||||
},
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("limits OpenAI and Azure Chat tool call IDs to 40 characters", () =>
|
||||
Effect.gen(function* () {
|
||||
const id = `call_${"a".repeat(48)}`
|
||||
@@ -1805,6 +1844,78 @@ describe("OpenAI Chat route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("preserves Gemini thought signatures on streamed parallel tool calls", () =>
|
||||
Effect.gen(function* () {
|
||||
// Gemini's OpenAI-compatible endpoint omits `index`, streams each call whole,
|
||||
// and signs only the first call of a parallel batch.
|
||||
const body = sseEvents(
|
||||
deltaChunk({
|
||||
role: "assistant",
|
||||
tool_calls: [
|
||||
{
|
||||
extra_content: { google: { thought_signature: "sig_1" } },
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: { name: "lookup", arguments: '{"city":"Paris"}' },
|
||||
},
|
||||
],
|
||||
}),
|
||||
deltaChunk({
|
||||
role: "assistant",
|
||||
tool_calls: [{ id: "call_2", type: "function", function: { name: "lookup", arguments: '{"city":"Tokyo"}' } }],
|
||||
}),
|
||||
deltaChunk({}, "stop"),
|
||||
)
|
||||
const response = yield* LLMClient.generate(
|
||||
LLMRequest.update(request, {
|
||||
tools: [ToolDefinition.make({ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } })],
|
||||
}),
|
||||
).pipe(Effect.provide(fixedResponse(body)))
|
||||
|
||||
expect(response.events.filter(LLMEvent.is.toolCall)).toEqual([
|
||||
{
|
||||
type: "tool-call",
|
||||
id: "call_1",
|
||||
name: "lookup",
|
||||
input: { city: "Paris" },
|
||||
providerExecuted: undefined,
|
||||
providerMetadata: { openai: { extraContent: { google: { thought_signature: "sig_1" } } } },
|
||||
},
|
||||
{
|
||||
type: "tool-call",
|
||||
id: "call_2",
|
||||
name: "lookup",
|
||||
input: { city: "Tokyo" },
|
||||
providerExecuted: undefined,
|
||||
providerMetadata: undefined,
|
||||
},
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("keeps extra content that arrives before the tool identity", () =>
|
||||
Effect.gen(function* () {
|
||||
const body = sseEvents(
|
||||
deltaChunk({
|
||||
tool_calls: [
|
||||
{ index: 0, extra_content: { google: { thought_signature: "sig_1" } }, function: { arguments: "{" } },
|
||||
],
|
||||
}),
|
||||
deltaChunk({ tool_calls: [{ index: 0, id: "call_1", function: { name: "lookup", arguments: "}" } }] }),
|
||||
deltaChunk({}, "tool_calls"),
|
||||
)
|
||||
const response = yield* LLMClient.generate(
|
||||
LLMRequest.update(request, {
|
||||
tools: [ToolDefinition.make({ name: "lookup", description: "Lookup data", inputSchema: { type: "object" } })],
|
||||
}),
|
||||
).pipe(Effect.provide(fixedResponse(body)))
|
||||
|
||||
expect(response.events.filter(LLMEvent.is.toolCall).map((event) => event.providerMetadata)).toEqual([
|
||||
{ openai: { extraContent: { google: { thought_signature: "sig_1" } } } },
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("does not finalize streamed tool calls when content is filtered", () =>
|
||||
Effect.gen(function* () {
|
||||
const body = sseEvents(
|
||||
|
||||
@@ -145,8 +145,9 @@ function parts(input: string) {
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
// cachePath makes each `:`-separated host part a directory.
|
||||
function safeHost(input: string) {
|
||||
return Boolean(input) && !input.startsWith("-") && !/[\s/\\]/.test(input)
|
||||
return Boolean(input) && !input.startsWith("-") && input.split(":").every(safeSegment)
|
||||
}
|
||||
|
||||
function safeSegment(input: string) {
|
||||
|
||||
@@ -41,6 +41,12 @@ describe("Repository", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("caches a host with a port under a directory per host part", () => {
|
||||
expect(Repository.cachePath("/cache", Repository.parseRemote("ssh://git@example.com:2222/owner/repo"))).toBe(
|
||||
path.join("/cache", "example.com", "2222", "owner", "repo"),
|
||||
)
|
||||
})
|
||||
|
||||
test("keeps local file repositories distinct from remote repositories", () => {
|
||||
const localPath = path.resolve("repo.git")
|
||||
const reference = Repository.parse(pathToFileURL(localPath).href)
|
||||
@@ -62,6 +68,18 @@ describe("Repository", () => {
|
||||
expect(() => Repository.validateBranch("bad branch")).toThrow(Repository.InvalidBranchError)
|
||||
})
|
||||
|
||||
test.each([
|
||||
"..:repo",
|
||||
"git@..:repo",
|
||||
"../owner/repo",
|
||||
"ssh://../repo",
|
||||
"ssh://..:22/repo",
|
||||
"https://%2e%2e/owner/repo",
|
||||
".:repo",
|
||||
])("rejects %s because its host contains a relative path segment", (input) => {
|
||||
expect(() => Repository.parseRemote(input)).toThrow(Repository.InvalidReferenceError)
|
||||
})
|
||||
|
||||
test("compares cache identity independent of input spelling", () => {
|
||||
const shorthand = Repository.parseRemote("owner/repo")
|
||||
|
||||
|
||||
@@ -2808,7 +2808,7 @@ function Shell(props: ToolProps) {
|
||||
command={stringValue(props.input.command)}
|
||||
workdir={stringValue(props.input.workdir)}
|
||||
status={props.part.state.status}
|
||||
background={Boolean(stringValue(props.metadata.shellID)) && props.part.state.status !== "running"}
|
||||
background={props.part.state.status === "completed" && props.metadata.status === "running"}
|
||||
output={stringValue(props.metadata.shellID) ? undefined : props.output}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import Card from "./Card.astro"
|
||||
import CardGroup from "./CardGroup.astro"
|
||||
import CodeBlock from "./CodeBlock.astro"
|
||||
import CodeTabs from "./CodeTabs.astro"
|
||||
import PlanTabs from "./PlanTabs.astro"
|
||||
import DocsLayout from "../layouts/DocsLayout.astro"
|
||||
|
||||
interface Props {
|
||||
@@ -22,5 +23,5 @@ const rendered = await render(entry)
|
||||
headings={rendered.headings}
|
||||
showTableOfContents={entry.data.tableOfContents !== false}
|
||||
>
|
||||
<rendered.Content components={{ Callout, Card, CardGroup, CodeBlock, CodeTabs }} />
|
||||
<rendered.Content components={{ Callout, Card, CardGroup, CodeBlock, CodeTabs, PlanTabs }} />
|
||||
</DocsLayout>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
interface Props {
|
||||
id: string
|
||||
label: string
|
||||
syncKey: string
|
||||
}
|
||||
|
||||
const plans = [
|
||||
{ id: "go", label: "Go" },
|
||||
{ id: "go-plus", label: "Go Plus" },
|
||||
] as const
|
||||
---
|
||||
|
||||
<div class="docs-plan-tabs" data-plan-tabs data-sync-key={Astro.props.syncKey}>
|
||||
<div class="docs-plan-tabs-list" role="tablist" aria-label={Astro.props.label}>
|
||||
{
|
||||
plans.map((plan, index) => (
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
id={`${Astro.props.id}-tab-${plan.id}`}
|
||||
aria-controls={`${Astro.props.id}-panel-${plan.id}`}
|
||||
aria-selected={index === 0 ? "true" : "false"}
|
||||
tabindex={index === 0 ? 0 : -1}
|
||||
data-plan-tab={plan.id}
|
||||
>
|
||||
{plan.label}
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{
|
||||
plans.map((plan, index) => (
|
||||
<div
|
||||
role="tabpanel"
|
||||
id={`${Astro.props.id}-panel-${plan.id}`}
|
||||
aria-labelledby={`${Astro.props.id}-tab-${plan.id}`}
|
||||
hidden={index !== 0}
|
||||
data-plan-panel={plan.id}
|
||||
>
|
||||
<slot name={plan.id} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const selectPlan = (tabs: HTMLElement, plan: string) => {
|
||||
tabs.querySelectorAll<HTMLButtonElement>("[data-plan-tab]").forEach((button) => {
|
||||
const selected = button.dataset.planTab === plan
|
||||
button.setAttribute("aria-selected", String(selected))
|
||||
button.tabIndex = selected ? 0 : -1
|
||||
})
|
||||
tabs.querySelectorAll<HTMLElement>("[data-plan-panel]").forEach((panel) => {
|
||||
panel.hidden = panel.dataset.planPanel !== plan
|
||||
})
|
||||
}
|
||||
|
||||
const selectSyncedPlan = (source: HTMLElement, plan: string) => {
|
||||
const syncKey = source.dataset.syncKey
|
||||
if (!syncKey) return
|
||||
document.querySelectorAll<HTMLElement>("[data-plan-tabs]").forEach((tabs) => {
|
||||
if (tabs.dataset.syncKey === syncKey) selectPlan(tabs, plan)
|
||||
})
|
||||
localStorage.setItem(`docs-plan-tabs:${syncKey}`, plan)
|
||||
}
|
||||
|
||||
document.querySelectorAll<HTMLElement>("[data-plan-tabs]").forEach((tabs) => {
|
||||
const syncKey = tabs.dataset.syncKey
|
||||
const plan = syncKey ? localStorage.getItem(`docs-plan-tabs:${syncKey}`) : undefined
|
||||
if (plan) selectPlan(tabs, plan)
|
||||
})
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!(event.target instanceof Element)) return
|
||||
const button = event.target.closest<HTMLButtonElement>("[data-plan-tab]")
|
||||
const tabs = button?.closest<HTMLElement>("[data-plan-tabs]")
|
||||
if (!button || !tabs || !button.dataset.planTab) return
|
||||
selectSyncedPlan(tabs, button.dataset.planTab)
|
||||
})
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (!(event.target instanceof HTMLButtonElement) || !event.target.matches("[data-plan-tab]")) return
|
||||
const tabs = event.target.closest<HTMLElement>("[data-plan-tabs]")
|
||||
if (!tabs) return
|
||||
const buttons = [...tabs.querySelectorAll<HTMLButtonElement>("[data-plan-tab]")]
|
||||
const selected = buttons.indexOf(event.target)
|
||||
const next =
|
||||
event.key === "Home"
|
||||
? buttons[0]
|
||||
: event.key === "End"
|
||||
? buttons.at(-1)
|
||||
: event.key === "ArrowRight"
|
||||
? buttons[(selected + 1) % buttons.length]
|
||||
: event.key === "ArrowLeft"
|
||||
? buttons[(selected - 1 + buttons.length) % buttons.length]
|
||||
: undefined
|
||||
if (!next?.dataset.planTab) return
|
||||
event.preventDefault()
|
||||
selectSyncedPlan(tabs, next.dataset.planTab)
|
||||
next.focus()
|
||||
})
|
||||
</script>
|
||||
@@ -1,17 +1,22 @@
|
||||
---
|
||||
title: "Go"
|
||||
description: "Low cost subscription for open coding models."
|
||||
description: "Reliable access to open coding models with two usage tiers."
|
||||
---
|
||||
|
||||
OpenCode Go is a low cost **$10/month subscription** that gives you reliable access to popular open coding models.
|
||||
OpenCode Go gives you reliable access to popular open coding models, with two monthly plans:
|
||||
|
||||
| Plan | Price | Included usage |
|
||||
| ---- | ----- | -------------- |
|
||||
| **Go** | **$10/month** | Lower-cost access to the models below |
|
||||
| **Go Plus** | **$40/month** | Higher usage limits across the models below |
|
||||
|
||||
Go works like any other provider in OpenCode. You subscribe to OpenCode Go and get your API key. It's **completely optional** and you don't need it to use OpenCode.
|
||||
|
||||
It is designed primarily for international users and provides stable global access.
|
||||
The service is designed primarily for international users and provides stable global access.
|
||||
|
||||
## How it works
|
||||
|
||||
1. Sign in to the [OpenCode console](https://opencode.ai/console), subscribe to Go, add your billing details, and copy your API key.
|
||||
1. Sign in to the [OpenCode Console](https://opencode.ai/console), subscribe to Go or Go Plus, add your billing details, and copy your API key.
|
||||
2. Run `/connect` in the TUI, select **OpenCode Go**, and paste your API key.
|
||||
|
||||
```text
|
||||
@@ -24,7 +29,7 @@ It is designed primarily for international users and provides stable global acce
|
||||
/models
|
||||
```
|
||||
|
||||
<Callout>Only one member per workspace can subscribe to OpenCode Go.</Callout>
|
||||
<Callout>Only one member per workspace can subscribe to OpenCode Go or Go Plus.</Callout>
|
||||
|
||||
The current list of models includes:
|
||||
|
||||
@@ -33,13 +38,13 @@ The current list of models includes:
|
||||
- **GLM-5.3-Flash**
|
||||
- **GLM-5.3**
|
||||
- **GLM-5.2**
|
||||
- **GLM-5.1**
|
||||
- **GPT 6 Luna**
|
||||
- **GPT 5.6 Luna**
|
||||
- **Kimi K3**
|
||||
- **Kimi K2.7 Code**
|
||||
- **Kimi K2.6**
|
||||
- **LongCat-2.0**
|
||||
- **LongCat 2.5 Preview Free** (limited time)
|
||||
- **MiMo-V2.6-Flash**
|
||||
- **MiMo-V2.6-Pro**
|
||||
- **MiMo-V2.5**
|
||||
@@ -50,9 +55,7 @@ The current list of models includes:
|
||||
- **Muse Spark 1.2 Contributor** ([limited regions](https://ai.developer.meta.com/legal/geographic-use-policy))
|
||||
- **Qwen3.8 Max**
|
||||
- **Qwen3.8 Flash**
|
||||
- **Qwen3.7 Max**
|
||||
- **Qwen3.7 Plus**
|
||||
- **Qwen3.6 Plus**
|
||||
- **DeepSeek V4.1 Flash**
|
||||
- **DeepSeek V4 Pro**
|
||||
- **DeepSeek V4 Flash**
|
||||
@@ -60,7 +63,6 @@ The current list of models includes:
|
||||
- **Hy4 preview**
|
||||
- **Hy3**
|
||||
- **Space Bunny Free** (limited time)
|
||||
- **LongCat 2.5 Preview Free** (limited time)
|
||||
|
||||
The list of models may change as we test and add new ones.
|
||||
|
||||
@@ -109,127 +111,212 @@ investigated. The linked reports track fixes and workarounds.
|
||||
## Usage limits
|
||||
|
||||
Usage limits are defined as monthly dollar amounts. The table below shows the
|
||||
monthly limit and token costs for each model.
|
||||
monthly limit for each plan and the token costs for each model. Token pricing is
|
||||
the same for Go and Go Plus.
|
||||
|
||||
Each model has the following usage limits: 5-hour — 20% of the monthly limit;
|
||||
weekly — 50%; and monthly — 100%.
|
||||
|
||||
For example, if a model has a $60 monthly limit, you can spend up to:
|
||||
For example, if a model has a $60 monthly limit on Go and a $120 monthly limit
|
||||
on Go Plus, you can spend up to:
|
||||
|
||||
- **5-hour limit** — $12 of usage
|
||||
- **Weekly limit** — $30 of usage
|
||||
- **Monthly limit** — $60 of usage
|
||||
- **5-hour limit** — $12 on Go or $24 on Go Plus
|
||||
- **Weekly limit** — $30 on Go or $60 on Go Plus
|
||||
- **Monthly limit** — $60 on Go or $120 on Go Plus
|
||||
|
||||
Across models, Go has $12 five-hour, $30 weekly, and $60 monthly allowances;
|
||||
Go Plus has $48 five-hour, $120 weekly, and $240 monthly allowances. Each
|
||||
model's monthly limit below determines how its usage counts toward those
|
||||
allowances.
|
||||
|
||||
Token prices are per 1M tokens.
|
||||
|
||||
<div class="docs-table-scroll" role="region" aria-label="Go model pricing" tabIndex={0}>
|
||||
<PlanTabs id="go-pricing" label="Go plan" syncKey="go-plan">
|
||||
<div slot="go">
|
||||
|
||||
| Model | Input | Output | Cached Read | Cached Write | Monthly limit |
|
||||
| --------------------------------------- | ------ | ------ | ----------- | ------------ | ---------------------------------------------------- |
|
||||
| GLM-5.3-Flash | $0.15 | $0.50 | $0.03 | - | **$60** |
|
||||
| GLM-5.3 | $1.40 | $4.40 | $0.26 | - | **$15** |
|
||||
| GLM-5.2 | $1.40 | $4.40 | $0.26 | - | **$60** |
|
||||
| GLM-5.1 | $1.40 | $4.40 | $0.26 | - | **$60** |
|
||||
| Kimi K3 | $3.00 | $15.00 | $0.30 | - | **$15** |
|
||||
| Kimi K2.7 Code | $0.95 | $4.00 | $0.19 | - | **$60** |
|
||||
| Kimi K2.6 | $0.95 | $4.00 | $0.16 | - | **$60** |
|
||||
| LongCat-2.0 | $0.30 | $1.20 | $0.006 | - | **$60** |
|
||||
| MiMo-V2.6-Flash | $0.14 | $0.28 | $0.0028 | - | **$60** |
|
||||
| MiMo-V2.6-Pro | $0.435 | $0.87 | $0.003625 | - | **$15** |
|
||||
| MiMo-V2.5 | $0.14 | $0.28 | $0.0028 | - | **$60** |
|
||||
| MiMo-V2.5-Pro | $0.435 | $0.87 | $0.003625 | - | **$15** |
|
||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | **$60** |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | **$60** |
|
||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | **$60** |
|
||||
| Muse Spark 1.3 Contributor | $0.10 | $0.20 | $0.002 | - | **$60** |
|
||||
| Muse Spark 1.2 Contributor | $0.10 | $0.20 | $0.002 | - | **$60** |
|
||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | **$15** |
|
||||
| Qwen3.8 Flash | $0.15 | $0.47 | $0.016 | $0.20 | **$30** |
|
||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | **$30** |
|
||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | **$60** |
|
||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | **$60** |
|
||||
| Qwen3.6 Plus (≤ 256K tokens) | $0.50 | $3.00 | $0.05 | $0.625 | **$60** |
|
||||
| Qwen3.6 Plus (> 256K tokens) | $2.00 | $6.00 | $0.20 | $2.50 | **$60** |
|
||||
| DeepSeek V4.1 Flash (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$60** |
|
||||
| DeepSeek V4.1 Flash (Peak) | $0.30 | $1.20 | $0.006 | - | **$60** |
|
||||
| DeepSeek V4 Pro (Off-Peak) | $0.66 | $1.98 | $0.022 | - | **$15** |
|
||||
| DeepSeek V4 Pro (Peak) | $1.32 | $3.96 | $0.044 | - | **$15** |
|
||||
| DeepSeek V4 Flash (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$30** |
|
||||
| DeepSeek V4 Flash (Peak) | $0.30 | $1.20 | $0.006 | - | **$30** |
|
||||
| DeepSeek V4 Flash Vision Exp (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$15** |
|
||||
| DeepSeek V4 Flash Vision Exp (Peak) | $0.30 | $1.20 | $0.006 | - | **$15** |
|
||||
| Hy4 preview | $0.834 | $2.501 | $0.042 | - | **$30** |
|
||||
| Hy3 | $0.14 | $0.58 | $0.035 | - | **$60** |
|
||||
| Space Bunny Free | Free | Free | Free | - | **Unlimited**<br /><small>limited time</small> |
|
||||
| LongCat 2.5 Preview Free | Free | Free | Free | - | **Unlimited**<br /><small>limited time</small> |
|
||||
| Grok 4.7 (≤ 200K tokens) | $2.00 | $6.00 | $0.50 | - | **$15** |
|
||||
| Grok 4.7 (> 200K tokens) | $4.00 | $12.00 | $1.00 | - | **$15** |
|
||||
| Grok 4.6 (≤ 200K tokens) | $2.00 | $6.00 | $0.50 | - | **$15** |
|
||||
| Grok 4.6 (> 200K tokens) | $4.00 | $12.00 | $1.00 | - | **$15** |
|
||||
| GPT 6 Luna (≤ 272K tokens) | $0.10 | $0.50 | $0.01 | $0.125 | **$15** |
|
||||
| GPT 6 Luna (> 272K tokens) | $0.20 | $0.75 | $0.02 | $0.25 | **$15** |
|
||||
| GPT 5.6 Luna (≤ 272K tokens) | $0.20 | $1.20 | $0.02 | $0.25 | **$15** |
|
||||
| GPT 5.6 Luna (> 272K tokens) | $0.40 | $1.80 | $0.04 | $0.50 | **$15** |
|
||||
| Model | Input | Output | Cached Read | Cached Write | Monthly limit |
|
||||
| --------------------------------------- | ------ | ------ | ----------- | ------------ | ---------------------------------------------- |
|
||||
| GLM-5.3-Flash | $0.15 | $0.50 | $0.03 | - | **$60** |
|
||||
| GLM-5.3 | $1.40 | $4.40 | $0.26 | - | **$15** |
|
||||
| GLM-5.2 | $1.40 | $4.40 | $0.26 | - | **$60** |
|
||||
| Kimi K3 | $3.00 | $15.00 | $0.30 | - | **$15** |
|
||||
| Kimi K2.7 Code | $0.95 | $4.00 | $0.19 | - | **$60** |
|
||||
| Kimi K2.6 | $0.95 | $4.00 | $0.16 | - | **$60** |
|
||||
| LongCat-2.0 | $0.30 | $1.20 | $0.006 | - | **$60** |
|
||||
| LongCat 2.5 Preview Free | Free | Free | Free | - | **Unlimited**<br /><small>limited time</small> |
|
||||
| MiMo-V2.6-Flash | $0.14 | $0.28 | $0.0028 | - | **$60** |
|
||||
| MiMo-V2.6-Pro | $0.435 | $0.87 | $0.003625 | - | **$15** |
|
||||
| MiMo-V2.5 | $0.14 | $0.28 | $0.0028 | - | **$60** |
|
||||
| MiMo-V2.5-Pro | $0.435 | $0.87 | $0.003625 | - | **$15** |
|
||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | **$60** |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | **$60** |
|
||||
| Muse Spark 1.3 Contributor | $0.10 | $0.20 | $0.002 | - | **$60** |
|
||||
| Muse Spark 1.2 Contributor | $0.10 | $0.20 | $0.002 | - | **$60** |
|
||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | **$15** |
|
||||
| Qwen3.8 Flash | $0.15 | $0.47 | $0.016 | $0.20 | **$30** |
|
||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | **$60** |
|
||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | **$60** |
|
||||
| DeepSeek V4.1 Flash (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$60** |
|
||||
| DeepSeek V4.1 Flash (Peak) | $0.30 | $1.20 | $0.006 | - | **$60** |
|
||||
| DeepSeek V4 Pro (Off-Peak) | $0.66 | $1.98 | $0.022 | - | **$15** |
|
||||
| DeepSeek V4 Pro (Peak) | $1.32 | $3.96 | $0.044 | - | **$15** |
|
||||
| DeepSeek V4 Flash (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$30** |
|
||||
| DeepSeek V4 Flash (Peak) | $0.30 | $1.20 | $0.006 | - | **$30** |
|
||||
| DeepSeek V4 Flash Vision Exp (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$15** |
|
||||
| DeepSeek V4 Flash Vision Exp (Peak) | $0.30 | $1.20 | $0.006 | - | **$15** |
|
||||
| Hy4 preview | $0.834 | $2.501 | $0.042 | - | **$30** |
|
||||
| Hy3 | $0.14 | $0.58 | $0.035 | - | **$60** |
|
||||
| Space Bunny Free | Free | Free | Free | - | **Unlimited**<br /><small>limited time</small> |
|
||||
| Grok 4.7 (≤ 200K tokens) | $2.00 | $6.00 | $0.50 | - | **$15** |
|
||||
| Grok 4.7 (> 200K tokens) | $4.00 | $12.00 | $1.00 | - | **$15** |
|
||||
| Grok 4.6 (≤ 200K tokens) | $2.00 | $6.00 | $0.50 | - | **$15** |
|
||||
| Grok 4.6 (> 200K tokens) | $4.00 | $12.00 | $1.00 | - | **$15** |
|
||||
| GPT 6 Luna (≤ 272K tokens) | $0.10 | $0.50 | $0.01 | $0.125 | **$15** |
|
||||
| GPT 6 Luna (> 272K tokens) | $0.20 | $0.75 | $0.02 | $0.25 | **$15** |
|
||||
| GPT 5.6 Luna (≤ 272K tokens) | $0.20 | $1.20 | $0.02 | $0.25 | **$15** |
|
||||
| GPT 5.6 Luna (> 272K tokens) | $0.40 | $1.80 | $0.04 | $0.50 | **$15** |
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div slot="go-plus">
|
||||
|
||||
| Model | Input | Output | Cached Read | Cached Write | Monthly limit |
|
||||
| --------------------------------------- | ------ | ------ | ----------- | ------------ | ---------------------------------------------- |
|
||||
| GLM-5.3-Flash | $0.15 | $0.50 | $0.03 | - | **$180** |
|
||||
| GLM-5.3 | $1.40 | $4.40 | $0.26 | - | **$120** |
|
||||
| GLM-5.2 | $1.40 | $4.40 | $0.26 | - | **$180** |
|
||||
| Kimi K3 | $3.00 | $15.00 | $0.30 | - | **$60** |
|
||||
| Kimi K2.7 Code | $0.95 | $4.00 | $0.19 | - | **$180** |
|
||||
| Kimi K2.6 | $0.95 | $4.00 | $0.16 | - | **$240** |
|
||||
| LongCat-2.0 | $0.30 | $1.20 | $0.006 | - | **$240** |
|
||||
| LongCat 2.5 Preview Free | Free | Free | Free | - | **Unlimited**<br /><small>limited time</small> |
|
||||
| MiMo-V2.6-Flash | $0.14 | $0.28 | $0.0028 | - | **$120** |
|
||||
| MiMo-V2.6-Pro | $0.435 | $0.87 | $0.003625 | - | **$60** |
|
||||
| MiMo-V2.5 | $0.14 | $0.28 | $0.0028 | - | **$120** |
|
||||
| MiMo-V2.5-Pro | $0.435 | $0.87 | $0.003625 | - | **$60** |
|
||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | **$180** |
|
||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | **$240** |
|
||||
| Muse Spark 1.3 Contributor | $0.10 | $0.20 | $0.002 | - | **$120** |
|
||||
| Muse Spark 1.2 Contributor | $0.10 | $0.20 | $0.002 | - | **$120** |
|
||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | **$60** |
|
||||
| Qwen3.8 Flash | $0.15 | $0.47 | $0.016 | $0.20 | **$90** |
|
||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | **$180** |
|
||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | **$180** |
|
||||
| DeepSeek V4.1 Flash (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$120** |
|
||||
| DeepSeek V4.1 Flash (Peak) | $0.30 | $1.20 | $0.006 | - | **$120** |
|
||||
| DeepSeek V4 Pro (Off-Peak) | $0.66 | $1.98 | $0.022 | - | **$60** |
|
||||
| DeepSeek V4 Pro (Peak) | $1.32 | $3.96 | $0.044 | - | **$60** |
|
||||
| DeepSeek V4 Flash (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$120** |
|
||||
| DeepSeek V4 Flash (Peak) | $0.30 | $1.20 | $0.006 | - | **$120** |
|
||||
| DeepSeek V4 Flash Vision Exp (Off-Peak) | $0.15 | $0.60 | $0.003 | - | **$60** |
|
||||
| DeepSeek V4 Flash Vision Exp (Peak) | $0.30 | $1.20 | $0.006 | - | **$60** |
|
||||
| Hy4 preview | $0.834 | $2.501 | $0.042 | - | **$120** |
|
||||
| Hy3 | $0.14 | $0.58 | $0.035 | - | **$240** |
|
||||
| Space Bunny Free | Free | Free | Free | - | **Unlimited**<br /><small>limited time</small> |
|
||||
| Grok 4.7 (≤ 200K tokens) | $2.00 | $6.00 | $0.50 | - | **$60** |
|
||||
| Grok 4.7 (> 200K tokens) | $4.00 | $12.00 | $1.00 | - | **$60** |
|
||||
| Grok 4.6 (≤ 200K tokens) | $2.00 | $6.00 | $0.50 | - | **$60** |
|
||||
| Grok 4.6 (> 200K tokens) | $4.00 | $12.00 | $1.00 | - | **$60** |
|
||||
| GPT 6 Luna (≤ 272K tokens) | $0.10 | $0.50 | $0.01 | $0.125 | **$60** |
|
||||
| GPT 6 Luna (> 272K tokens) | $0.20 | $0.75 | $0.02 | $0.25 | **$60** |
|
||||
| GPT 5.6 Luna (≤ 272K tokens) | $0.20 | $1.20 | $0.02 | $0.25 | **$60** |
|
||||
| GPT 5.6 Luna (> 272K tokens) | $0.40 | $1.80 | $0.04 | $0.50 | **$60** |
|
||||
|
||||
</div>
|
||||
</PlanTabs>
|
||||
|
||||
**Space Bunny Free:** Free for a limited time.
|
||||
|
||||
**LongCat 2.5 Preview Free:** Free for a limited time.
|
||||
|
||||
**DeepSeek V4.1 Flash / V4 Pro / V4 Flash Vision Exp:** Peak hours are 01:00-04:00 and 06:00-10:00 UTC, Monday through Friday; all other hours, including weekends, are Off-Peak. [Learn more](https://api-docs.deepseek.com/quick_start/pricing/).
|
||||
**DeepSeek V4.1 Flash / V4 Pro / V4 Flash / V4 Flash Vision Exp:** Peak hours are 01:00-04:00 and 06:00-10:00 UTC, Monday through Friday; all other hours, including weekends, are Off-Peak. [Learn more](https://api-docs.deepseek.com/quick_start/pricing/).
|
||||
|
||||
**DeepSeek V4 Flash Vision Exp:** Images are converted into tokens based on their dimensions and billed as input tokens alongside text tokens. [Learn more](https://api-docs.deepseek.com/quick_start/pricing/).
|
||||
|
||||
### Estimated requests
|
||||
|
||||
The table below provides an estimated request count based on typical Go usage patterns:
|
||||
The tables below estimate request counts based on typical Go usage patterns. Go
|
||||
Plus estimates scale with each model's higher usage limit.
|
||||
|
||||
<div class="docs-table-scroll" role="region" aria-label="Go estimated requests" tabIndex={0}>
|
||||
<PlanTabs id="go-requests" label="Go plan" syncKey="go-plan">
|
||||
<div slot="go">
|
||||
|
||||
| Model | requests per 5 hour | requests per week | requests per month |
|
||||
| -------------------------------------------------------- | ------------------------- | -------------------------- | --------------------------- |
|
||||
| GLM-5.3-Flash | 6,320 | 15,790 | 31,580 |
|
||||
| GLM-5.3 | 220 | 540 | 1,080 |
|
||||
| GLM-5.2 | 880 | 2,150 | 4,300 |
|
||||
| GLM-5.1 | 880 | 2,150 | 4,300 |
|
||||
| Kimi K3 | 110 | 250 | 490 |
|
||||
| Kimi K2.7 Code | 1,350 | 3,380 | 6,750 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| LongCat-2.0 | 11,400 | 28,600 | 57,200 |
|
||||
| MiMo-V2.6-Flash | 30,100 | 75,200 | 150,400 |
|
||||
| MiMo-V2.6-Pro | 3,250 | 8,150 | 16,300 |
|
||||
| MiMo-V2.5 | 30,100 | 75,200 | 150,400 |
|
||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| Muse Spark 1.3 Contributor | 45,300 | 113,300 | 226,600 |
|
||||
| Muse Spark 1.2 Contributor | 45,300 | 113,300 | 226,600 |
|
||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
||||
| Qwen3.8 Flash | 5,400 | 13,500 | 27,000 |
|
||||
| Qwen3.7 Max | 170 | 420 | 840 |
|
||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||
| DeepSeek V4.1 Flash | 26,000 | 65,000 | 130,000 |
|
||||
| DeepSeek V4 Pro | 1,050 | 2,600 | 5,200 |
|
||||
| DeepSeek V4 Flash | 13,000 | 32,500 | 65,000 |
|
||||
| DeepSeek V4 Flash Vision Exp | 6,500 | 16,250 | 32,500 |
|
||||
| Hy4 preview | 1,350 | 3,380 | 6,770 |
|
||||
| Hy3 | 4,300 | 10,750 | 21,500 |
|
||||
| Space Bunny Free | Unlimited | Unlimited | Unlimited |
|
||||
| LongCat 2.5 Preview Free | Unlimited | Unlimited | Unlimited |
|
||||
| Grok 4.7 | 169 | 423 | 845 |
|
||||
| Grok 4.6 | 169 | 423 | 845 |
|
||||
| GPT 6 Luna | 4,230 | 10,560 | 21,130 |
|
||||
| GPT 5.6 Luna | 2,050 | 5,100 | 10,250 |
|
||||
| Model | Requests per 5 hours | Requests per week | Requests per month |
|
||||
| ---------------------------- | -------------------- | ----------------- | ------------------ |
|
||||
| GLM-5.3-Flash | 6,320 | 15,790 | 31,580 |
|
||||
| GLM-5.3 | 220 | 540 | 1,080 |
|
||||
| GLM-5.2 | 880 | 2,150 | 4,300 |
|
||||
| Kimi K3 | 110 | 250 | 490 |
|
||||
| Kimi K2.7 Code | 1,350 | 3,380 | 6,750 |
|
||||
| Kimi K2.6 | 1,150 | 2,880 | 5,750 |
|
||||
| LongCat-2.0 | 11,400 | 28,600 | 57,200 |
|
||||
| LongCat 2.5 Preview Free | Unlimited | Unlimited | Unlimited |
|
||||
| MiMo-V2.6-Flash | 30,100 | 75,200 | 150,400 |
|
||||
| MiMo-V2.6-Pro | 3,250 | 8,150 | 16,300 |
|
||||
| MiMo-V2.5 | 30,100 | 75,200 | 150,400 |
|
||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||
| Muse Spark 1.3 Contributor | 45,300 | 113,300 | 226,600 |
|
||||
| Muse Spark 1.2 Contributor | 45,300 | 113,300 | 226,600 |
|
||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
||||
| Qwen3.8 Flash | 5,400 | 13,500 | 27,000 |
|
||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||
| DeepSeek V4.1 Flash | 26,000 | 65,000 | 130,000 |
|
||||
| DeepSeek V4 Pro | 1,050 | 2,600 | 5,200 |
|
||||
| DeepSeek V4 Flash | 13,000 | 32,500 | 65,000 |
|
||||
| DeepSeek V4 Flash Vision Exp | 6,500 | 16,250 | 32,500 |
|
||||
| Hy4 preview | 1,350 | 3,380 | 6,770 |
|
||||
| Hy3 | 4,300 | 10,750 | 21,500 |
|
||||
| Space Bunny Free | Unlimited | Unlimited | Unlimited |
|
||||
| Grok 4.7 | 169 | 423 | 845 |
|
||||
| Grok 4.6 | 169 | 423 | 845 |
|
||||
| GPT 6 Luna | 4,230 | 10,560 | 21,130 |
|
||||
| GPT 5.6 Luna | 2,050 | 5,100 | 10,250 |
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div slot="go-plus">
|
||||
|
||||
| Model | Requests per 5 hours | Requests per week | Requests per month |
|
||||
| ---------------------------- | -------------------- | ----------------- | ------------------ |
|
||||
| GLM-5.3-Flash | 18,960 | 47,370 | 94,740 |
|
||||
| GLM-5.3 | 1,760 | 4,320 | 8,640 |
|
||||
| GLM-5.2 | 2,640 | 6,450 | 12,900 |
|
||||
| Kimi K3 | 440 | 1,000 | 1,960 |
|
||||
| Kimi K2.7 Code | 4,050 | 10,140 | 20,250 |
|
||||
| Kimi K2.6 | 4,600 | 11,520 | 23,000 |
|
||||
| LongCat-2.0 | 45,600 | 114,400 | 228,800 |
|
||||
| LongCat 2.5 Preview Free | Unlimited | Unlimited | Unlimited |
|
||||
| MiMo-V2.6-Flash | 60,200 | 150,400 | 300,800 |
|
||||
| MiMo-V2.6-Pro | 13,000 | 32,600 | 65,200 |
|
||||
| MiMo-V2.5 | 60,200 | 150,400 | 300,800 |
|
||||
| MiMo-V2.5-Pro | 13,000 | 32,600 | 65,200 |
|
||||
| MiniMax M3 | 9,600 | 24,000 | 48,000 |
|
||||
| MiniMax M2.7 | 13,600 | 34,000 | 68,000 |
|
||||
| Muse Spark 1.3 Contributor | 90,600 | 226,600 | 453,200 |
|
||||
| Muse Spark 1.2 Contributor | 90,600 | 226,600 | 453,200 |
|
||||
| Qwen3.8 Max | 640 | 1,600 | 3,240 |
|
||||
| Qwen3.8 Flash | 16,200 | 40,500 | 81,000 |
|
||||
| Qwen3.7 Plus | 12,900 | 32,400 | 64,800 |
|
||||
| DeepSeek V4.1 Flash | 52,000 | 130,000 | 260,000 |
|
||||
| DeepSeek V4 Pro | 4,200 | 10,400 | 20,800 |
|
||||
| DeepSeek V4 Flash | 52,000 | 130,000 | 260,000 |
|
||||
| DeepSeek V4 Flash Vision Exp | 26,000 | 65,000 | 130,000 |
|
||||
| Hy4 preview | 5,400 | 13,520 | 27,080 |
|
||||
| Hy3 | 17,200 | 43,000 | 86,000 |
|
||||
| Space Bunny Free | Unlimited | Unlimited | Unlimited |
|
||||
| Grok 4.7 | 676 | 1,692 | 3,380 |
|
||||
| Grok 4.6 | 676 | 1,692 | 3,380 |
|
||||
| GPT 6 Luna | 16,920 | 42,240 | 84,520 |
|
||||
| GPT 5.6 Luna | 8,200 | 20,400 | 41,000 |
|
||||
|
||||
</div>
|
||||
</PlanTabs>
|
||||
|
||||
The estimates use the following token counts per request; actual usage varies.
|
||||
|
||||
- Grok 4.7/4.6 — 390 input, 32,500 cached, 120 output tokens per request
|
||||
- GLM-5.3-Flash — 1,000 input, 55,000 cached, 200 output tokens per request
|
||||
- GLM-5.3/5.2/5.1 — 700 input, 52,000 cached, 150 output tokens per request
|
||||
- GLM-5.3/5.2 — 700 input, 52,000 cached, 150 output tokens per request
|
||||
- GPT 6 Luna — 1,000 input, 50,000 cached, 220 output tokens per request
|
||||
- GPT 5.6 Luna — 1,000 input, 50,000 cached, 220 output tokens per request
|
||||
- Kimi K3 — 1,050 input, 76,500 cached, 300 output tokens per request
|
||||
@@ -249,9 +336,7 @@ The estimates use the following token counts per request; actual usage varies.
|
||||
- MiMo-V2.5-Pro — 790 input, 86,000 cached, 305 output tokens per request
|
||||
- Qwen3.8 Max — 420 input, 66,000 cached, 200 output tokens per request
|
||||
- Qwen3.8 Flash — 600 input, 58,000 cached, 200 output tokens per request
|
||||
- Qwen3.7 Max — 420 input, 66,000 cached, 200 output tokens per request
|
||||
- Qwen3.7 Plus — 500 input, 57,000 cached, 190 output tokens per request
|
||||
- Qwen3.6 Plus — 500 input, 57,000 cached, 190 output tokens per request
|
||||
- Hy4 preview — 830 input, 71,500 cached, 295 output tokens per request
|
||||
- Hy3 — 830 input, 71,500 cached, 295 output tokens per request
|
||||
|
||||
@@ -261,6 +346,7 @@ You can track your current usage in the [console](https://opencode.ai/console).
|
||||
|
||||
Usage limits may change as we learn from early usage and feedback.
|
||||
|
||||
---
|
||||
|
||||
### Usage beyond limits
|
||||
|
||||
@@ -271,7 +357,7 @@ after you've reached your usage limits instead of blocking requests.
|
||||
|
||||
### Why some models have lower usage
|
||||
|
||||
With Go, you pay $10/month, and the included monthly usage varies by model.
|
||||
With Go, the included monthly usage varies by model.
|
||||
|
||||
For most models, we make this work through bulk discounts and reserved GPU capacity. We then pass those savings on to you as higher monthly usage.
|
||||
|
||||
@@ -294,7 +380,6 @@ You can also access Go models through the following API endpoints.
|
||||
| GLM-5.3-Flash | glm-5.3-flash | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| GLM-5.3 | glm-5.3 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| GLM-5.2 | glm-5.2 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| GLM-5.1 | glm-5.1 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K3 | kimi-k3 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.7 Code | kimi-k2.7-code | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Kimi K2.6 | kimi-k2.6 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -309,14 +394,11 @@ You can also access Go models through the following API endpoints.
|
||||
| MiMo-V2.5-Pro | mimo-v2.5-pro | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Muse Spark 1.3 Contributor | muse-spark-1.3-contributor | `https://opencode.ai/zen/go/v1/responses` | `@ai-sdk/openai` |
|
||||
| Muse Spark 1.2 Contributor | muse-spark-1.2-contributor | `https://opencode.ai/zen/go/v1/responses` | `@ai-sdk/openai` |
|
||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Qwen3.8 Flash | qwen3.8-flash | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||
| Hy4 preview | hy4-preview | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Hy3 | hy3 | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
| Space Bunny Free | space-bunny-free | `https://opencode.ai/zen/go/v1/chat/completions` | `@ai-sdk/openai-compatible` |
|
||||
@@ -353,7 +435,6 @@ curl https://opencode.ai/zen/go/v1/models
|
||||
| GLM-5.3-Flash | Not used | 0 days |
|
||||
| GLM-5.3 | Not used | 0 days |
|
||||
| GLM-5.2 | Not used | 0 days |
|
||||
| GLM-5.1 | Not used | 0 days |
|
||||
| Kimi K3 | Not used | 0 days |
|
||||
| Kimi K2.7 Code | Not used | 0 days |
|
||||
| Kimi K2.6 | Not used | 0 days |
|
||||
@@ -364,9 +445,7 @@ curl https://opencode.ai/zen/go/v1/models
|
||||
| MiMo-V2.5 | Not used | 0 days |
|
||||
| Qwen3.8 Max | Not used | 0 days |
|
||||
| Qwen3.8 Flash | Not used | 0 days |
|
||||
| Qwen3.7 Max | Not used | 0 days |
|
||||
| Qwen3.7 Plus | Not used | 0 days |
|
||||
| Qwen3.6 Plus | Not used | 0 days |
|
||||
| MiniMax M3 | Not used | 0 days |
|
||||
| MiniMax M2.7 | Not used | 0 days |
|
||||
| Muse Spark 1.3 Contributor | Yes | Not ZDR |
|
||||
@@ -384,7 +463,7 @@ curl https://opencode.ai/zen/go/v1/models
|
||||
- **GPT 6 Luna / GPT 5.6 Luna:** Abuse monitoring logs are generated for all API feature usage and retained for up to 30 days. [Learn more](https://developers.openai.com/api/docs/guides/your-data#data-retention-controls-for-abuse-monitoring).
|
||||
- **Muse Spark 1.3 Contributor:** Heavily discounted token pricing in exchange for permission to use your prompts and completions to train future Meta models. Availability is limited to regions permitted by Meta's [Geographic Use Policy](https://ai.developer.meta.com/legal/geographic-use-policy). [Learn more](https://dev.meta.ai/docs/pricing-rate-limits#contributor-tier).
|
||||
- **Muse Spark 1.2 Contributor:** Heavily discounted token pricing in exchange for permission to use your prompts and completions to train future Meta models. Availability is limited to regions permitted by Meta's [Geographic Use Policy](https://ai.developer.meta.com/legal/geographic-use-policy). [Learn more](https://dev.meta.ai/docs/pricing-rate-limits#contributor-tier).
|
||||
- **DeepSeek:** ZDR agreement is renewed monthly. The current agreement is valid through September 30, 2026.
|
||||
- **DeepSeek:** ZDR agreement is renewed monthly. The current agreement is valid through October 31, 2026.
|
||||
|
||||
## Background
|
||||
|
||||
@@ -400,7 +479,7 @@ To fix this, we did a couple of things:
|
||||
2. We worked with a few providers to make sure these were being served correctly.
|
||||
3. We benchmarked the combination of the model/provider and came up with a list that we feel good recommending.
|
||||
|
||||
OpenCode Go gives you access to these models for **$10/month**.
|
||||
Both Go and Go Plus provide access to these models; choose the plan that fits how much you use them.
|
||||
|
||||
## Goals
|
||||
|
||||
|
||||
@@ -912,6 +912,48 @@ main {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.docs-plan-tabs {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.docs-plan-tabs-list {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.docs-plan-tabs-list button {
|
||||
margin-bottom: -1px;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 0;
|
||||
border-bottom: 2px solid transparent;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
font: inherit;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.docs-plan-tabs-list button[aria-selected="true"] {
|
||||
border-bottom-color: var(--foreground);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.docs-plan-tabs-list button:focus-visible {
|
||||
outline: 2px solid var(--link);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.docs-plan-tabs [role="tabpanel"] {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.docs-plan-tabs [role="tabpanel"] > :last-child,
|
||||
.docs-plan-tabs [role="tabpanel"] > :last-child > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.prose table {
|
||||
width: 100%;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
Reference in New Issue
Block a user