@namzu/zen 1.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 +61 -0
- package/LICENSE.md +110 -0
- package/README.md +142 -0
- package/dist/client.d.ts +30 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +517 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +29 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +1170 -0
- package/dist/models.js.map +1 -0
- package/dist/options.d.ts +5 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +221 -0
- package/dist/options.js.map +1 -0
- package/dist/prompt.d.ts +9 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +376 -0
- package/dist/prompt.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +80 -0
- package/src/client.ts +585 -0
- package/src/index.ts +36 -0
- package/src/models.ts +1199 -0
- package/src/options.ts +259 -0
- package/src/prompt.ts +456 -0
- package/src/types.ts +29 -0
package/src/options.ts
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { getModelCapabilities as getMessagesCapabilities } from '@ai-sdk/anthropic/internal'
|
|
2
|
+
import type { LanguageModelV3CallOptions, SharedV3ProviderOptions } from '@ai-sdk/provider'
|
|
3
|
+
import { type ChatCompletionParams, ProviderRequestError, type ProviderRoute } from '@namzu/sdk'
|
|
4
|
+
import { type ZenProtocol, type ZenService, findZenModel } from './models.js'
|
|
5
|
+
import { toModelPrompt } from './prompt.js'
|
|
6
|
+
|
|
7
|
+
export function createCallOptions(
|
|
8
|
+
params: ChatCompletionParams,
|
|
9
|
+
route: ProviderRoute,
|
|
10
|
+
service: ZenService,
|
|
11
|
+
protocol: ZenProtocol,
|
|
12
|
+
): LanguageModelV3CallOptions {
|
|
13
|
+
const refuse = (detail: string): never => {
|
|
14
|
+
throw new ProviderRequestError({ providerId: route.providerId, kind: 'bad_request', detail })
|
|
15
|
+
}
|
|
16
|
+
const known = findZenModel(service, params.model)
|
|
17
|
+
let maxOutputTokens = params.maxTokens ?? 4096
|
|
18
|
+
if (!Number.isSafeInteger(maxOutputTokens) || maxOutputTokens <= 0)
|
|
19
|
+
refuse('maxTokens must be a positive integer.')
|
|
20
|
+
if (
|
|
21
|
+
params.effort !== undefined &&
|
|
22
|
+
known?.effortLevels &&
|
|
23
|
+
!known.effortLevels.includes(params.effort)
|
|
24
|
+
)
|
|
25
|
+
refuse('The selected model does not advertise this reasoning effort level.')
|
|
26
|
+
if (params.repetitionPenalty !== undefined)
|
|
27
|
+
refuse('repetitionPenalty is not supported by this driver.')
|
|
28
|
+
if (params.topK !== undefined && protocol !== 'google' && protocol !== 'messages')
|
|
29
|
+
refuse('topK is not supported by this model protocol.')
|
|
30
|
+
if (
|
|
31
|
+
protocol === 'messages' &&
|
|
32
|
+
(params.frequencyPenalty !== undefined || params.presencePenalty !== undefined)
|
|
33
|
+
)
|
|
34
|
+
refuse('Frequency and presence penalties are not supported by the messages protocol.')
|
|
35
|
+
if (params.cacheControl?.type === 'ephemeral' && protocol !== 'messages')
|
|
36
|
+
refuse('Explicit ephemeral cache control requires the messages protocol.')
|
|
37
|
+
const options: SharedV3ProviderOptions = {}
|
|
38
|
+
switch (protocol) {
|
|
39
|
+
case 'chat': {
|
|
40
|
+
if (params.thinking?.budgetTokens !== undefined || params.thinking?.display !== undefined)
|
|
41
|
+
refuse('This chat protocol cannot honor a thinking budget or display setting.')
|
|
42
|
+
options.opencode = {
|
|
43
|
+
...(params.effort !== undefined ? { reasoningEffort: params.effort } : {}),
|
|
44
|
+
...(params.parallelToolCalls !== undefined
|
|
45
|
+
? { parallel_tool_calls: params.parallelToolCalls }
|
|
46
|
+
: {}),
|
|
47
|
+
...(params.thinking ? { thinking: { type: params.thinking.type } } : {}),
|
|
48
|
+
...(params.responseFormat?.type === 'json_schema'
|
|
49
|
+
? { strictJsonSchema: params.responseFormat.json_schema.strict ?? true }
|
|
50
|
+
: {}),
|
|
51
|
+
}
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
case 'responses': {
|
|
55
|
+
if (params.stop !== undefined)
|
|
56
|
+
refuse('Stop sequences are not supported by the responses protocol.')
|
|
57
|
+
if (params.frequencyPenalty !== undefined || params.presencePenalty !== undefined)
|
|
58
|
+
refuse('Frequency and presence penalties are not supported by the responses protocol.')
|
|
59
|
+
if (params.thinking?.type === 'enabled' || params.thinking?.budgetTokens !== undefined)
|
|
60
|
+
refuse('The responses protocol uses adaptive effort, not a fixed thinking budget.')
|
|
61
|
+
if (
|
|
62
|
+
params.thinking?.type === 'disabled' &&
|
|
63
|
+
params.effort !== undefined &&
|
|
64
|
+
params.effort !== 'none'
|
|
65
|
+
)
|
|
66
|
+
refuse('Disabled thinking conflicts with a non-none reasoning effort.')
|
|
67
|
+
if (
|
|
68
|
+
params.thinking?.type === 'disabled' &&
|
|
69
|
+
known?.effortLevels &&
|
|
70
|
+
!known.effortLevels.includes('none')
|
|
71
|
+
)
|
|
72
|
+
refuse('This model does not advertise disabled reasoning.')
|
|
73
|
+
// The pinned native adapter removes sampling parameters from reasoning
|
|
74
|
+
// requests. Its warning arrives after the POST, so reject that loss here.
|
|
75
|
+
const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/.exec(params.model)
|
|
76
|
+
const isReasoning =
|
|
77
|
+
Boolean(known?.effortLevels?.length) ||
|
|
78
|
+
/^o\d+(?:-|$)/.test(params.model) ||
|
|
79
|
+
(gpt !== null &&
|
|
80
|
+
Number(gpt[1]) >= 5 &&
|
|
81
|
+
!(gpt[2] === undefined && gpt[3]?.startsWith('chat')))
|
|
82
|
+
const effort = params.thinking?.type === 'disabled' ? 'none' : params.effort
|
|
83
|
+
const allowsSampling =
|
|
84
|
+
effort === 'none' && gpt !== null && Number(gpt[1]) === 5 && Number(gpt[2]) >= 1
|
|
85
|
+
if (
|
|
86
|
+
isReasoning &&
|
|
87
|
+
!allowsSampling &&
|
|
88
|
+
(params.temperature !== undefined || params.topP !== undefined)
|
|
89
|
+
)
|
|
90
|
+
refuse('Sampling parameters are not supported with this model’s reasoning configuration.')
|
|
91
|
+
if (
|
|
92
|
+
!isReasoning &&
|
|
93
|
+
(params.effort !== undefined || params.thinking?.display === 'summarized')
|
|
94
|
+
)
|
|
95
|
+
refuse('This responses model does not support reasoning controls.')
|
|
96
|
+
options.openai = {
|
|
97
|
+
store: false,
|
|
98
|
+
include: ['reasoning.encrypted_content'],
|
|
99
|
+
...(known?.effortLevels?.length ? { forceReasoning: true } : {}),
|
|
100
|
+
...(params.effort !== undefined ? { reasoningEffort: params.effort } : {}),
|
|
101
|
+
...(params.thinking?.type === 'disabled' ? { reasoningEffort: 'none' } : {}),
|
|
102
|
+
...(params.thinking?.display === 'summarized' ? { reasoningSummary: 'auto' } : {}),
|
|
103
|
+
...(params.parallelToolCalls !== undefined
|
|
104
|
+
? { parallelToolCalls: params.parallelToolCalls }
|
|
105
|
+
: {}),
|
|
106
|
+
...(params.responseFormat?.type === 'json_schema'
|
|
107
|
+
? { strictJsonSchema: params.responseFormat.json_schema.strict ?? true }
|
|
108
|
+
: {}),
|
|
109
|
+
}
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
case 'messages': {
|
|
113
|
+
const thinking = params.thinking
|
|
114
|
+
const capabilities = getMessagesCapabilities(params.model)
|
|
115
|
+
const isThinking = thinking?.type === 'enabled' || thinking?.type === 'adaptive'
|
|
116
|
+
if (
|
|
117
|
+
(isThinking || capabilities.rejectsSamplingParameters) &&
|
|
118
|
+
(params.temperature !== undefined || params.topP !== undefined || params.topK !== undefined)
|
|
119
|
+
)
|
|
120
|
+
refuse('Sampling parameters are not supported with this model’s thinking configuration.')
|
|
121
|
+
if (
|
|
122
|
+
params.temperature !== undefined &&
|
|
123
|
+
(!Number.isFinite(params.temperature) || params.temperature < 0 || params.temperature > 1)
|
|
124
|
+
)
|
|
125
|
+
refuse('The messages protocol requires temperature between 0 and 1.')
|
|
126
|
+
if (
|
|
127
|
+
params.model.includes('claude-') &&
|
|
128
|
+
params.temperature !== undefined &&
|
|
129
|
+
params.topP !== undefined
|
|
130
|
+
)
|
|
131
|
+
refuse('This model cannot honor temperature and topP together.')
|
|
132
|
+
if (
|
|
133
|
+
params.responseFormat?.type === 'json_schema' &&
|
|
134
|
+
params.responseFormat.json_schema.strict === false
|
|
135
|
+
)
|
|
136
|
+
refuse('The messages protocol cannot disable schema enforcement.')
|
|
137
|
+
if (params.responseFormat?.type === 'json_object')
|
|
138
|
+
refuse('The messages protocol requires a schema for JSON output.')
|
|
139
|
+
if (
|
|
140
|
+
capabilities.rejectsThinkingDisabledAboveHighEffort &&
|
|
141
|
+
thinking?.type === 'disabled' &&
|
|
142
|
+
(params.effort === 'xhigh' || params.effort === 'max')
|
|
143
|
+
)
|
|
144
|
+
refuse('This model cannot disable thinking at the requested effort level.')
|
|
145
|
+
if (thinking?.budgetTokens !== undefined && thinking.type !== 'enabled')
|
|
146
|
+
refuse('A thinking budget requires manual enabled thinking.')
|
|
147
|
+
if (thinking?.display !== undefined && thinking.type !== 'adaptive')
|
|
148
|
+
refuse('Thinking display selection requires adaptive thinking.')
|
|
149
|
+
if (capabilities.isKnownModel && maxOutputTokens > capabilities.maxOutputTokens)
|
|
150
|
+
refuse('maxTokens exceeds the selected model’s output limit.')
|
|
151
|
+
if (thinking?.type === 'enabled') {
|
|
152
|
+
// Namzu's cap includes thinking; the native adapter adds its budget
|
|
153
|
+
// to maxOutputTokens, including a default 1024 when omitted.
|
|
154
|
+
const budget = thinking.budgetTokens ?? 1024
|
|
155
|
+
if (!Number.isSafeInteger(budget) || budget < 1024 || budget >= maxOutputTokens)
|
|
156
|
+
refuse('Manual thinking requires a budget of at least 1024 below maxTokens.')
|
|
157
|
+
maxOutputTokens -= budget
|
|
158
|
+
}
|
|
159
|
+
if (
|
|
160
|
+
!capabilities.supportsStructuredOutput &&
|
|
161
|
+
params.responseFormat?.type === 'json_schema' &&
|
|
162
|
+
params.parallelToolCalls === true
|
|
163
|
+
)
|
|
164
|
+
refuse('This model cannot combine schema output with parallel tool calls.')
|
|
165
|
+
if (
|
|
166
|
+
!capabilities.supportsStructuredOutput &&
|
|
167
|
+
params.tools?.some((tool) => params.enforceToolInputSchema?.includes(tool.function.name))
|
|
168
|
+
)
|
|
169
|
+
refuse('This model protocol cannot enforce strict tool input schemas.')
|
|
170
|
+
options.anthropic = {
|
|
171
|
+
...(params.responseFormat?.type === 'json_schema'
|
|
172
|
+
? { structuredOutputMode: 'outputFormat' }
|
|
173
|
+
: {}),
|
|
174
|
+
sendReasoning: true,
|
|
175
|
+
...(thinking ? { thinking: { ...thinking } } : {}),
|
|
176
|
+
...(params.effort !== undefined ? { effort: params.effort } : {}),
|
|
177
|
+
...(params.parallelToolCalls !== undefined
|
|
178
|
+
? { disableParallelToolUse: !params.parallelToolCalls }
|
|
179
|
+
: {}),
|
|
180
|
+
...(params.cacheControl ? { cacheControl: { type: 'ephemeral' } } : {}),
|
|
181
|
+
}
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
case 'google': {
|
|
185
|
+
if (params.parallelToolCalls !== undefined)
|
|
186
|
+
refuse('The google protocol has no parallelToolCalls switch.')
|
|
187
|
+
if (params.thinking?.budgetTokens !== undefined && params.thinking.type !== 'enabled')
|
|
188
|
+
refuse('A thinking budget requires manual enabled thinking.')
|
|
189
|
+
if (
|
|
190
|
+
params.thinking?.type === 'disabled' &&
|
|
191
|
+
params.effort !== undefined &&
|
|
192
|
+
params.effort !== 'none'
|
|
193
|
+
)
|
|
194
|
+
refuse('Disabled thinking conflicts with a reasoning effort level.')
|
|
195
|
+
options.google = {
|
|
196
|
+
thinkingConfig: {
|
|
197
|
+
includeThoughts: params.thinking?.display !== 'omitted',
|
|
198
|
+
...(params.effort !== undefined ? { thinkingLevel: params.effort } : {}),
|
|
199
|
+
...(params.thinking?.type === 'disabled' ? { thinkingBudget: 0 } : {}),
|
|
200
|
+
...(params.thinking?.budgetTokens !== undefined
|
|
201
|
+
? { thinkingBudget: params.thinking.budgetTokens }
|
|
202
|
+
: {}),
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const enforced = new Set(params.enforceToolInputSchema)
|
|
209
|
+
return {
|
|
210
|
+
prompt: toModelPrompt(params, route, service, protocol),
|
|
211
|
+
maxOutputTokens,
|
|
212
|
+
...(params.temperature !== undefined ? { temperature: params.temperature } : {}),
|
|
213
|
+
...(params.topP !== undefined ? { topP: params.topP } : {}),
|
|
214
|
+
...(params.topK !== undefined ? { topK: params.topK } : {}),
|
|
215
|
+
...(params.frequencyPenalty !== undefined ? { frequencyPenalty: params.frequencyPenalty } : {}),
|
|
216
|
+
...(params.presencePenalty !== undefined ? { presencePenalty: params.presencePenalty } : {}),
|
|
217
|
+
...(params.stop ? { stopSequences: params.stop } : {}),
|
|
218
|
+
...(params.tools
|
|
219
|
+
? {
|
|
220
|
+
tools: params.tools.map((tool) => ({
|
|
221
|
+
type: 'function' as const,
|
|
222
|
+
name: tool.function.name,
|
|
223
|
+
description: tool.function.description,
|
|
224
|
+
inputSchema: tool.function.parameters,
|
|
225
|
+
// Responses strict schemas cannot express Namzu's general
|
|
226
|
+
// tool contract (including conditional edit shapes). Like
|
|
227
|
+
// the Codex driver, retain the schema and validate inputs
|
|
228
|
+
// at execution; enforcement is a capability-dependent hint.
|
|
229
|
+
...(protocol === 'responses'
|
|
230
|
+
? { strict: false }
|
|
231
|
+
: enforced.has(tool.function.name)
|
|
232
|
+
? { strict: true }
|
|
233
|
+
: {}),
|
|
234
|
+
})),
|
|
235
|
+
}
|
|
236
|
+
: {}),
|
|
237
|
+
...(params.toolChoice
|
|
238
|
+
? {
|
|
239
|
+
toolChoice:
|
|
240
|
+
typeof params.toolChoice === 'string'
|
|
241
|
+
? { type: params.toolChoice }
|
|
242
|
+
: { type: 'tool', toolName: params.toolChoice.function.name },
|
|
243
|
+
}
|
|
244
|
+
: {}),
|
|
245
|
+
...(params.responseFormat
|
|
246
|
+
? {
|
|
247
|
+
responseFormat:
|
|
248
|
+
params.responseFormat.type === 'json_object'
|
|
249
|
+
? { type: 'json' as const }
|
|
250
|
+
: {
|
|
251
|
+
type: 'json' as const,
|
|
252
|
+
schema: params.responseFormat.json_schema.schema,
|
|
253
|
+
name: params.responseFormat.json_schema.name,
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
: {}),
|
|
257
|
+
providerOptions: options,
|
|
258
|
+
}
|
|
259
|
+
}
|