@namzu/zen 2.5.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +28 -14
- package/dist/catalogue/catalogue.d.ts.map +1 -1
- package/dist/catalogue/catalogue.js +13 -1
- package/dist/catalogue/catalogue.js.map +1 -1
- package/dist/catalogue/derive.d.ts +7 -1
- package/dist/catalogue/derive.d.ts.map +1 -1
- package/dist/catalogue/derive.js +69 -6
- package/dist/catalogue/derive.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +254 -15
- package/dist/client.js.map +1 -1
- package/dist/models.d.ts +1 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +130 -53
- package/dist/models.js.map +1 -1
- package/dist/options.d.ts +2 -2
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +35 -10
- package/dist/options.js.map +1 -1
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +37 -4
- package/dist/prompt.js.map +1 -1
- package/dist/types.d.ts +8 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/catalogue/catalogue.ts +15 -0
- package/src/catalogue/derive.ts +78 -6
- package/src/client.ts +283 -16
- package/src/models.review.json +5 -2
- package/src/models.ts +131 -54
- package/src/options.ts +39 -9
- package/src/prompt.ts +43 -4
- package/src/types.ts +8 -7
package/src/client.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
APICallError,
|
|
8
8
|
type LanguageModelV3,
|
|
9
9
|
type LanguageModelV3Content,
|
|
10
|
+
type LanguageModelV3Prompt,
|
|
10
11
|
type LanguageModelV3StreamPart,
|
|
11
12
|
type LanguageModelV3Usage,
|
|
12
13
|
type SharedV3ProviderMetadata,
|
|
@@ -31,9 +32,15 @@ import {
|
|
|
31
32
|
findZenCatalogueModel,
|
|
32
33
|
isUnroutedZenModel,
|
|
33
34
|
} from './catalogue/catalogue.js'
|
|
35
|
+
import { isDirectAnonymousZenId } from './catalogue/derive.js'
|
|
34
36
|
import type { ZenModel, ZenService } from './models.js'
|
|
35
37
|
import { createCallOptions } from './options.js'
|
|
36
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
createReplayState,
|
|
40
|
+
isGoDeepSeekV4ResponsesModel,
|
|
41
|
+
isMuseSparkResponsesModel,
|
|
42
|
+
toReasoningBlocks,
|
|
43
|
+
} from './prompt.js'
|
|
37
44
|
import type { ZenConfig, ZenGoConfig } from './types.js'
|
|
38
45
|
|
|
39
46
|
export const ZEN_CAPABILITIES: ProviderCapabilities = {
|
|
@@ -50,6 +57,219 @@ export const ZEN_CAPABILITIES: ProviderCapabilities = {
|
|
|
50
57
|
export const ZEN_BASE_URL = 'https://opencode.ai/zen/v1'
|
|
51
58
|
export const ZEN_GO_BASE_URL = 'https://opencode.ai/zen/go/v1'
|
|
52
59
|
|
|
60
|
+
function withoutMuseSparkEncryptedReasoning(init?: RequestInit): RequestInit {
|
|
61
|
+
// The pinned OpenAI adapter adds this include automatically whenever a
|
|
62
|
+
// reasoning model uses store:false, even if providerOptions omits it.
|
|
63
|
+
// Muse Spark cannot round-trip that gateway-issued ciphertext. Keep the
|
|
64
|
+
// request stateless and preserve every other include the adapter may add.
|
|
65
|
+
if (typeof init?.body !== 'string')
|
|
66
|
+
throw new Error('Zen Muse Spark adapter emitted a request body that cannot be checked.')
|
|
67
|
+
const body: unknown = JSON.parse(init.body)
|
|
68
|
+
if (typeof body !== 'object' || body === null || Array.isArray(body))
|
|
69
|
+
throw new Error('Zen Muse Spark adapter emitted an invalid request body.')
|
|
70
|
+
const request = body as Record<string, unknown>
|
|
71
|
+
if (request.include !== undefined) {
|
|
72
|
+
if (
|
|
73
|
+
!Array.isArray(request.include) ||
|
|
74
|
+
!request.include.every((item) => typeof item === 'string')
|
|
75
|
+
)
|
|
76
|
+
throw new Error('Zen Muse Spark adapter emitted an invalid include list.')
|
|
77
|
+
const include = request.include.filter((item) => item !== 'reasoning.encrypted_content')
|
|
78
|
+
if (include.length > 0) request.include = include
|
|
79
|
+
else request.include = undefined
|
|
80
|
+
}
|
|
81
|
+
return { ...init, body: JSON.stringify(request) }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isGoDeepSeekV4ChatModel(service: ZenService, model: string, protocol: string): boolean {
|
|
85
|
+
return (
|
|
86
|
+
service === 'go' &&
|
|
87
|
+
protocol === 'chat' &&
|
|
88
|
+
(model === 'deepseek-v4-pro' ||
|
|
89
|
+
model === 'deepseek-v4.1-flash' ||
|
|
90
|
+
model === 'deepseek-v4-flash')
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function adapterBody(init: RequestInit | undefined, model: string): Record<string, unknown> {
|
|
95
|
+
if (typeof init?.body !== 'string')
|
|
96
|
+
throw new Error(`Zen adapter emitted a non-JSON request body for ${model}.`)
|
|
97
|
+
const body: unknown = JSON.parse(init.body)
|
|
98
|
+
if (typeof body !== 'object' || body === null || Array.isArray(body))
|
|
99
|
+
throw new Error(`Zen adapter emitted an invalid request body for ${model}.`)
|
|
100
|
+
return body as Record<string, unknown>
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function withGoDeepSeekV4ChatHistory(init: RequestInit | undefined, model: string): RequestInit {
|
|
104
|
+
const body = adapterBody(init, model)
|
|
105
|
+
if (!Array.isArray(body.messages)) throw new Error('Go DeepSeek chat request has no messages.')
|
|
106
|
+
for (const value of body.messages) {
|
|
107
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
108
|
+
throw new Error('Go DeepSeek chat request has an invalid message.')
|
|
109
|
+
const message = value as Record<string, unknown>
|
|
110
|
+
if (message.role !== 'assistant') continue
|
|
111
|
+
if (message.content === null) message.content = ''
|
|
112
|
+
if (message.reasoning_content === undefined || message.reasoning_content === null)
|
|
113
|
+
message.reasoning_content = ''
|
|
114
|
+
else if (typeof message.reasoning_content !== 'string')
|
|
115
|
+
throw new Error('Go DeepSeek chat request has invalid reasoning content.')
|
|
116
|
+
}
|
|
117
|
+
return { ...init, body: JSON.stringify(body) }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function withGoDeepSeekV4ResponsesHistory(
|
|
121
|
+
init: RequestInit | undefined,
|
|
122
|
+
model: string,
|
|
123
|
+
prompt: LanguageModelV3Prompt,
|
|
124
|
+
): RequestInit {
|
|
125
|
+
const body = adapterBody(init, model)
|
|
126
|
+
if (!Array.isArray(body.input)) throw new Error('Go DeepSeek Responses request has no input.')
|
|
127
|
+
const input: Record<string, unknown>[] = []
|
|
128
|
+
let turn: Record<string, unknown>[] = []
|
|
129
|
+
const sourceTurns = prompt.filter((message) => message.role === 'assistant')
|
|
130
|
+
let sourceIndex = 0
|
|
131
|
+
const refuse = (): never => {
|
|
132
|
+
throw new ProviderRequestError({
|
|
133
|
+
providerId: 'zen-go',
|
|
134
|
+
kind: 'bad_request',
|
|
135
|
+
detail: `Go DeepSeek ${model} requires captured, nonempty reasoning_text for each previous assistant turn. This history cannot be replayed safely.`,
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
const flushTurn = () => {
|
|
139
|
+
if (turn.length === 0) return
|
|
140
|
+
const source = sourceTurns[sourceIndex++]
|
|
141
|
+
if (source?.role !== 'assistant') return refuse()
|
|
142
|
+
const messages = turn.filter((item) => item.role === 'assistant')
|
|
143
|
+
const calls = turn.filter((item) => item.type === 'function_call')
|
|
144
|
+
const sourceText = source.content
|
|
145
|
+
.filter((part) => part.type === 'text')
|
|
146
|
+
.map((part) => part.text)
|
|
147
|
+
const wireText = messages.flatMap((message) =>
|
|
148
|
+
Array.isArray(message.content)
|
|
149
|
+
? message.content
|
|
150
|
+
.filter(
|
|
151
|
+
(part) => typeof part === 'object' && part !== null && part.type === 'output_text',
|
|
152
|
+
)
|
|
153
|
+
.map((part) => part.text)
|
|
154
|
+
: [],
|
|
155
|
+
)
|
|
156
|
+
const sourceCalls = source.content
|
|
157
|
+
.filter((part) => part.type === 'tool-call')
|
|
158
|
+
.map((part) => part.toolCallId)
|
|
159
|
+
const wireCalls = calls.map((call) => call.call_id)
|
|
160
|
+
if (
|
|
161
|
+
JSON.stringify(sourceText) !== JSON.stringify(wireText) ||
|
|
162
|
+
JSON.stringify(sourceCalls) !== JSON.stringify(wireCalls)
|
|
163
|
+
)
|
|
164
|
+
refuse()
|
|
165
|
+
const captured = source.content
|
|
166
|
+
.filter((part) => part.type === 'reasoning')
|
|
167
|
+
.map((part) => part.text)
|
|
168
|
+
.filter((text) => text.trim().length > 0)
|
|
169
|
+
.join('\n')
|
|
170
|
+
if (!captured) refuse()
|
|
171
|
+
const reasoning = {
|
|
172
|
+
type: 'reasoning',
|
|
173
|
+
summary: [],
|
|
174
|
+
content: [{ type: 'reasoning_text', text: captured }],
|
|
175
|
+
}
|
|
176
|
+
// The adapter can replay a native [tool-call, text] turn as a call then
|
|
177
|
+
// an assistant message. Go rejects a message between calls and outputs.
|
|
178
|
+
input.push(reasoning, ...messages, ...calls)
|
|
179
|
+
turn = []
|
|
180
|
+
}
|
|
181
|
+
for (const value of body.input) {
|
|
182
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
183
|
+
throw new Error('Go DeepSeek Responses request has an invalid input item.')
|
|
184
|
+
const item = value as Record<string, unknown>
|
|
185
|
+
if (item.type === 'reasoning' || item.type === 'function_call' || item.role === 'assistant') {
|
|
186
|
+
turn.push(item)
|
|
187
|
+
} else {
|
|
188
|
+
flushTurn()
|
|
189
|
+
input.push(item)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
flushTurn()
|
|
193
|
+
if (sourceIndex !== sourceTurns.length) refuse()
|
|
194
|
+
body.input = input
|
|
195
|
+
return { ...init, body: JSON.stringify(body) }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function withGoDeepSeekReasoningTextEvents(response: Response): Response {
|
|
199
|
+
if (
|
|
200
|
+
!response.ok ||
|
|
201
|
+
!response.body ||
|
|
202
|
+
!response.headers.get('content-type')?.includes('text/event-stream')
|
|
203
|
+
)
|
|
204
|
+
return response
|
|
205
|
+
// @ai-sdk/openai 3.0.109 understands reasoning summary deltas but not the
|
|
206
|
+
// reasoning_text delta that Go DeepSeek streams. Translate that one event
|
|
207
|
+
// before the adapter parses SSE; the original text then enters durable replay.
|
|
208
|
+
const decoder = new TextDecoder()
|
|
209
|
+
const encoder = new TextEncoder()
|
|
210
|
+
let pending = ''
|
|
211
|
+
const rewrite = (event: string): string => {
|
|
212
|
+
const lines = event.split(/\r\n|\n|\r/)
|
|
213
|
+
const dataLines = lines.filter((line) => line.startsWith('data:'))
|
|
214
|
+
if (dataLines.length !== 1) return event
|
|
215
|
+
let value: unknown
|
|
216
|
+
try {
|
|
217
|
+
value = JSON.parse(dataLines[0]?.slice(5).trimStart() ?? '')
|
|
218
|
+
} catch {
|
|
219
|
+
return event
|
|
220
|
+
}
|
|
221
|
+
if (
|
|
222
|
+
typeof value !== 'object' ||
|
|
223
|
+
value === null ||
|
|
224
|
+
!('type' in value) ||
|
|
225
|
+
value.type !== 'response.reasoning_text.delta' ||
|
|
226
|
+
!('item_id' in value) ||
|
|
227
|
+
typeof value.item_id !== 'string' ||
|
|
228
|
+
!('delta' in value) ||
|
|
229
|
+
typeof value.delta !== 'string'
|
|
230
|
+
)
|
|
231
|
+
return event
|
|
232
|
+
const translated = {
|
|
233
|
+
...value,
|
|
234
|
+
type: 'response.reasoning_summary_text.delta',
|
|
235
|
+
summary_index: 0,
|
|
236
|
+
}
|
|
237
|
+
return lines
|
|
238
|
+
.map((line) => {
|
|
239
|
+
if (line.startsWith('data:')) return `data: ${JSON.stringify(translated)}`
|
|
240
|
+
if (line.startsWith('event:')) return 'event: response.reasoning_summary_text.delta'
|
|
241
|
+
return line
|
|
242
|
+
})
|
|
243
|
+
.join('\n')
|
|
244
|
+
}
|
|
245
|
+
const body = response.body.pipeThrough(
|
|
246
|
+
new TransformStream<Uint8Array, Uint8Array>({
|
|
247
|
+
transform(chunk, controller) {
|
|
248
|
+
pending += decoder.decode(chunk, { stream: true })
|
|
249
|
+
while (true) {
|
|
250
|
+
const boundary = /\r\n\r\n|\n\n|\r\r/.exec(pending)
|
|
251
|
+
if (!boundary) break
|
|
252
|
+
const event = pending.slice(0, boundary.index)
|
|
253
|
+
pending = pending.slice(boundary.index + boundary[0].length)
|
|
254
|
+
controller.enqueue(encoder.encode(`${rewrite(event)}\n\n`))
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
flush(controller) {
|
|
258
|
+
pending += decoder.decode()
|
|
259
|
+
if (pending) controller.enqueue(encoder.encode(pending))
|
|
260
|
+
},
|
|
261
|
+
}),
|
|
262
|
+
)
|
|
263
|
+
const headers = new Headers(response.headers)
|
|
264
|
+
headers.delete('content-length')
|
|
265
|
+
headers.delete('content-encoding')
|
|
266
|
+
return new Response(body, {
|
|
267
|
+
status: response.status,
|
|
268
|
+
statusText: response.statusText,
|
|
269
|
+
headers,
|
|
270
|
+
})
|
|
271
|
+
}
|
|
272
|
+
|
|
53
273
|
/**
|
|
54
274
|
* The words a backend's own finish reason uses, in `LanguageModelV3FinishReason.raw`,
|
|
55
275
|
* for "the model's context window, not its output-token budget, is what ran
|
|
@@ -135,12 +355,13 @@ export class ZenProvider implements LLMProvider {
|
|
|
135
355
|
|
|
136
356
|
async *chatStream(params: ChatCompletionParams): AsyncIterable<StreamChunk> {
|
|
137
357
|
const model =
|
|
138
|
-
params.model ||
|
|
139
|
-
this.config.model ||
|
|
140
|
-
(this.anonymous ? 'muse-spark-1.3-contributor-free' : 'glm-5.3-flash')
|
|
358
|
+
params.model || this.config.model || (this.anonymous ? 'space-bunny-free' : 'glm-5.3-flash')
|
|
141
359
|
const catalogue = this.runtimeCatalogue()
|
|
142
360
|
const info = findZenCatalogueModel(catalogue, this.service, model)
|
|
143
|
-
if (
|
|
361
|
+
if (
|
|
362
|
+
this.anonymous &&
|
|
363
|
+
(!isDirectAnonymousZenId(model) || info?.supportsAnonymousAccess !== true)
|
|
364
|
+
)
|
|
144
365
|
throw new ProviderRequestError({
|
|
145
366
|
providerId: this.id,
|
|
146
367
|
kind: 'auth',
|
|
@@ -156,7 +377,11 @@ export class ZenProvider implements LLMProvider {
|
|
|
156
377
|
? 'The service serves this model, but no source states its wire format. Configure protocol explicitly.'
|
|
157
378
|
: 'This model has no known wire format. Update the provider or configure protocol explicitly.',
|
|
158
379
|
})
|
|
159
|
-
const route = params.providerRoute ?? {
|
|
380
|
+
const route = params.providerRoute ?? {
|
|
381
|
+
providerId: this.id,
|
|
382
|
+
model,
|
|
383
|
+
chainIndex: 0,
|
|
384
|
+
}
|
|
160
385
|
const controller = new AbortController()
|
|
161
386
|
const signal = AbortSignal.any([
|
|
162
387
|
controller.signal,
|
|
@@ -168,7 +393,7 @@ export class ZenProvider implements LLMProvider {
|
|
|
168
393
|
signal.throwIfAborted()
|
|
169
394
|
let options: ReturnType<typeof createCallOptions>
|
|
170
395
|
try {
|
|
171
|
-
options = createCallOptions({ ...params, model }, route, this.service, protocol)
|
|
396
|
+
options = createCallOptions({ ...params, model }, route, this.service, protocol, info)
|
|
172
397
|
} catch (error) {
|
|
173
398
|
if (isProviderRequestError(error)) throw error
|
|
174
399
|
throw new ProviderRequestError({
|
|
@@ -193,8 +418,20 @@ export class ZenProvider implements LLMProvider {
|
|
|
193
418
|
body.generationConfig ??= {}
|
|
194
419
|
delete body.generationConfig.responseSchema
|
|
195
420
|
body.generationConfig.responseJsonSchema = params.responseFormat.json_schema.schema
|
|
196
|
-
return this.requestFetch(input, {
|
|
421
|
+
return this.requestFetch(input, {
|
|
422
|
+
...init,
|
|
423
|
+
body: JSON.stringify(body),
|
|
424
|
+
})
|
|
197
425
|
}
|
|
426
|
+
if (isMuseSparkResponsesModel(model, protocol))
|
|
427
|
+
return this.requestFetch(input, withoutMuseSparkEncryptedReasoning(init))
|
|
428
|
+
if (isGoDeepSeekV4ChatModel(this.service, model, protocol))
|
|
429
|
+
return this.requestFetch(input, withGoDeepSeekV4ChatHistory(init, model))
|
|
430
|
+
if (isGoDeepSeekV4ResponsesModel(this.service, model, protocol))
|
|
431
|
+
return this.requestFetch(
|
|
432
|
+
input,
|
|
433
|
+
withGoDeepSeekV4ResponsesHistory(init, model, options.prompt),
|
|
434
|
+
).then(withGoDeepSeekReasoningTextEvents)
|
|
198
435
|
return this.requestFetch(input, init)
|
|
199
436
|
}) as typeof fetch,
|
|
200
437
|
}
|
|
@@ -225,7 +462,10 @@ export class ZenProvider implements LLMProvider {
|
|
|
225
462
|
const texts = new Map<string, Extract<LanguageModelV3Content, { type: 'text' }>>()
|
|
226
463
|
const thoughts = new Map<
|
|
227
464
|
string,
|
|
228
|
-
{
|
|
465
|
+
{
|
|
466
|
+
index: number
|
|
467
|
+
value: Extract<LanguageModelV3Content, { type: 'reasoning' }>
|
|
468
|
+
}
|
|
229
469
|
>()
|
|
230
470
|
const tools = new Map<
|
|
231
471
|
string,
|
|
@@ -290,7 +530,10 @@ export class ZenProvider implements LLMProvider {
|
|
|
290
530
|
const index = thoughts.size
|
|
291
531
|
thoughts.set(part.id, { index, value })
|
|
292
532
|
content.push(value)
|
|
293
|
-
yield {
|
|
533
|
+
yield {
|
|
534
|
+
id,
|
|
535
|
+
delta: { reasoning: { index, type: 'thinking', text: '' } },
|
|
536
|
+
}
|
|
294
537
|
break
|
|
295
538
|
}
|
|
296
539
|
case 'reasoning-delta':
|
|
@@ -358,7 +601,9 @@ export class ZenProvider implements LLMProvider {
|
|
|
358
601
|
)
|
|
359
602
|
yield {
|
|
360
603
|
id,
|
|
361
|
-
delta: {
|
|
604
|
+
delta: {
|
|
605
|
+
toolCalls: [{ index: tool.index, function: { arguments: part.delta } }],
|
|
606
|
+
},
|
|
362
607
|
}
|
|
363
608
|
break
|
|
364
609
|
}
|
|
@@ -377,7 +622,12 @@ export class ZenProvider implements LLMProvider {
|
|
|
377
622
|
throw new Error('Provider-executed tools are not configured.')
|
|
378
623
|
let tool = tools.get(part.toolCallId)
|
|
379
624
|
if (!tool) {
|
|
380
|
-
tool = {
|
|
625
|
+
tool = {
|
|
626
|
+
index: tools.size,
|
|
627
|
+
input: '',
|
|
628
|
+
ended: false,
|
|
629
|
+
value: { ...part },
|
|
630
|
+
}
|
|
381
631
|
tools.set(part.toolCallId, tool)
|
|
382
632
|
content.push(tool.value)
|
|
383
633
|
yield {
|
|
@@ -401,7 +651,9 @@ export class ZenProvider implements LLMProvider {
|
|
|
401
651
|
if (suffix)
|
|
402
652
|
yield {
|
|
403
653
|
id,
|
|
404
|
-
delta: {
|
|
654
|
+
delta: {
|
|
655
|
+
toolCalls: [{ index: tool.index, function: { arguments: suffix } }],
|
|
656
|
+
},
|
|
405
657
|
}
|
|
406
658
|
tool.value.input = part.input
|
|
407
659
|
tool.value.providerMetadata = mergeMetadata(
|
|
@@ -409,7 +661,12 @@ export class ZenProvider implements LLMProvider {
|
|
|
409
661
|
part.providerMetadata,
|
|
410
662
|
)
|
|
411
663
|
tool.ended = true
|
|
412
|
-
yield {
|
|
664
|
+
yield {
|
|
665
|
+
id,
|
|
666
|
+
delta: {
|
|
667
|
+
toolCallEnd: { index: tool.index, id: part.toolCallId },
|
|
668
|
+
},
|
|
669
|
+
}
|
|
413
670
|
break
|
|
414
671
|
}
|
|
415
672
|
case 'finish':
|
|
@@ -493,7 +750,11 @@ export class ZenProvider implements LLMProvider {
|
|
|
493
750
|
seen.add(item.id)
|
|
494
751
|
const model = findZenCatalogueModel(catalogue, this.service, item.id)
|
|
495
752
|
if (model) {
|
|
496
|
-
if (
|
|
753
|
+
if (
|
|
754
|
+
!this.anonymous ||
|
|
755
|
+
(isDirectAnonymousZenId(item.id) && model.supportsAnonymousAccess === true)
|
|
756
|
+
)
|
|
757
|
+
result.push({ ...model })
|
|
497
758
|
continue
|
|
498
759
|
}
|
|
499
760
|
// Served, and named by the runtime catalogue as having no known wire:
|
|
@@ -603,7 +864,13 @@ export class ZenProvider implements LLMProvider {
|
|
|
603
864
|
const envelope = safeErrorEnvelope(error)
|
|
604
865
|
const body = scrub(JSON.stringify(envelope))
|
|
605
866
|
if (envelope.status !== undefined)
|
|
606
|
-
return named(
|
|
867
|
+
return named(
|
|
868
|
+
providerHttpError({
|
|
869
|
+
providerId: this.id,
|
|
870
|
+
status: envelope.status,
|
|
871
|
+
body,
|
|
872
|
+
}),
|
|
873
|
+
)
|
|
607
874
|
return named(
|
|
608
875
|
providerVendorError({
|
|
609
876
|
providerId: this.id,
|
package/src/models.review.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"omissions": {
|
|
3
3
|
"go/deepseek-flash": "Served by the Go service and named by no route row on either page; models.dev carries no entry for it either, so neither a wire nor its limits can be derived.",
|
|
4
|
-
"go/glm-5": "Served by Go and no longer routed by the Go page, which routes GLM 5.
|
|
4
|
+
"go/glm-5": "Served by Go and no longer routed by the Go page, which routes GLM 5.2, 5.3 and 5.3 Flash. The Zen page still routes this id, and one service's route is not the other's, so none is taken from it.",
|
|
5
|
+
"go/glm-5.1": "Still served by Go, but the current Go page states neither a route nor a price for it. The /models answer states no wire, so the old bundled route is not carried without current evidence.",
|
|
5
6
|
"go/grok-4.5": "Served by Go and absent from the Go page, which routes Grok 4.6. models.dev states an npm package for it, and that field is deliberately not used as a route, so none is carried.",
|
|
6
7
|
"go/hy3-preview": "Served by Go and named by no route row on either page; models.dev carries no entry for it, so neither a wire nor its limits can be derived.",
|
|
7
8
|
"go/kimi-k2.5": "Served by Go and absent from the Go page, which routes Kimi K2.6, K2.7 Code and K3. The Zen page still routes this id, and one service's route is not the other's, so none is taken from it.",
|
|
@@ -11,7 +12,9 @@
|
|
|
11
12
|
"go/mimo-v2.6-pro": "Routed by the Go page since 2026-09-21, and models.dev carries no `opencode-go` entry for it yet, so its limits cannot be derived and it is not carried.",
|
|
12
13
|
"go/minimax-m2.5": "Superseded on Go by MiniMax M2.7 and M3, which are both carried. The page still lists it.",
|
|
13
14
|
"go/omen-alpha": "Served by Go and no longer routed by the Go page, which is why this release dropped it from the catalogue. No source states a wire for it any more, so a caller that needs it must name the protocol itself.",
|
|
14
|
-
"go/qwen3.5-plus": "Served by Go and absent from the Go page, which routes Qwen 3.
|
|
15
|
+
"go/qwen3.5-plus": "Served by Go and absent from the Go page, which routes Qwen 3.7 Plus, 3.8 Flash and 3.8 Max. The Zen page routes this id on Messages, and one service's route is not the other's, so none is taken from it.",
|
|
16
|
+
"go/qwen3.6-plus": "Still served by Go, but the current Go page states neither a route nor a price for it. The /models answer states no wire, and the old bundled Messages route conflicts with the reference client's Chat route, so it is not carried by guessing.",
|
|
17
|
+
"go/qwen3.7-max": "Still served by Go, but the current Go page states neither a route nor a price for it. The /models answer states no wire, so the old bundled route is not carried without current evidence.",
|
|
15
18
|
"zen/claude-sonnet-4": "Served by Zen and named by no route row on either page: the Zen page routes Claude Sonnet 4.5 and 4.6. models.dev states an npm package for it, and that field is deliberately not used as a route, so none is carried.",
|
|
16
19
|
"zen/deepseek-v4-flash-free": "Served by Zen and absent from the Zen page, which routes DeepSeek V4 Flash. models.dev states a name and limits for this free id and no wire, so none is carried.",
|
|
17
20
|
"zen/glm-5": "Superseded by GLM 5.1, 5.2 and 5.3, which are all carried. The page still lists it.",
|