@namzu/zen 2.5.0 → 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 +28 -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 +274 -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 +44 -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 +304 -16
- package/src/models.review.json +5 -2
- package/src/models.ts +131 -54
- package/src/options.ts +39 -9
- package/src/prompt.ts +50 -4
- package/src/types.ts +8 -7
package/src/models.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface ZenModel {
|
|
|
20
20
|
readonly outputPrice: number
|
|
21
21
|
readonly supportsToolUse: boolean
|
|
22
22
|
readonly supportsStreaming: boolean
|
|
23
|
-
/** True only for
|
|
23
|
+
/** True only for verified direct-anonymous Zen models; zero price does not grant access. */
|
|
24
24
|
readonly supportsAnonymousAccess?: boolean
|
|
25
25
|
/** Exact advertised selectable effort levels; empty means no effort selector. */
|
|
26
26
|
readonly effortLevels?: readonly ReasoningEffort[]
|
|
@@ -35,7 +35,7 @@ export interface ZenModel {
|
|
|
35
35
|
* derived is an edit to src/catalogue/derive.ts. Both survive regeneration; a
|
|
36
36
|
* hand edit here does not.
|
|
37
37
|
*
|
|
38
|
-
* Refreshed: 2026-09-
|
|
38
|
+
* Refreshed: 2026-09-28
|
|
39
39
|
*
|
|
40
40
|
* This is the BUNDLED snapshot: what the driver knows with no network. A host
|
|
41
41
|
* can derive a fresher roster at run time from the same sources, by the same
|
|
@@ -44,7 +44,8 @@ export interface ZenModel {
|
|
|
44
44
|
*
|
|
45
45
|
* Routes come from each service's own documentation page, as the pair
|
|
46
46
|
* (endpoint, AI SDK package) that page states per model; both halves must agree
|
|
47
|
-
* or the run stops.
|
|
47
|
+
* or the run stops. Three Go ids have narrow reference-client exceptions in
|
|
48
|
+
* src/catalogue/derive.ts. Prices come from the per-1M-token table at the base
|
|
48
49
|
* tier, and a model with no price row there must be named in the page's
|
|
49
50
|
* free-model list and is then zero. Limits, tool support, modalities and effort
|
|
50
51
|
* options come from the matching provider entry in models.dev, where the
|
|
@@ -71,9 +72,9 @@ export interface ZenModel {
|
|
|
71
72
|
* does not expose those input kinds.
|
|
72
73
|
*
|
|
73
74
|
* Promotional free models with documented routes and complete metadata are
|
|
74
|
-
* included at their advertised zero price
|
|
75
|
-
*
|
|
76
|
-
*
|
|
75
|
+
* included at their advertised zero price. Direct anonymous admission is a
|
|
76
|
+
* separate, live-verified curation in src/catalogue/derive.ts and still requires
|
|
77
|
+
* an entry in the page's free-model list. OpenCode's own loader uses the public
|
|
77
78
|
* sentinel when credentials are absent:
|
|
78
79
|
* https://github.com/anomalyco/opencode/blob/16747470f976aca3d362ad730bcd3fe82ecc2c9a/packages/opencode/src/provider/provider.ts#L185
|
|
79
80
|
* Unknown IDs have no inferred protocol or limits. MiniMax and Qwen demonstrate
|
|
@@ -102,6 +103,32 @@ const ZEN_MODELS = freezeModels([
|
|
|
102
103
|
supportsStreaming: true,
|
|
103
104
|
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
104
105
|
},
|
|
106
|
+
{
|
|
107
|
+
id: 'gpt-6-sol',
|
|
108
|
+
name: 'GPT 6 Sol',
|
|
109
|
+
protocol: 'responses',
|
|
110
|
+
contextWindow: 1050000,
|
|
111
|
+
maxOutputTokens: 128000,
|
|
112
|
+
inputModalities: ['text', 'image', 'document'],
|
|
113
|
+
inputPrice: 2.0,
|
|
114
|
+
outputPrice: 10.0,
|
|
115
|
+
supportsToolUse: true,
|
|
116
|
+
supportsStreaming: true,
|
|
117
|
+
effortLevels: ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'gpt-6-luna',
|
|
121
|
+
name: 'GPT 6 Luna',
|
|
122
|
+
protocol: 'responses',
|
|
123
|
+
contextWindow: 1050000,
|
|
124
|
+
maxOutputTokens: 128000,
|
|
125
|
+
inputModalities: ['text', 'image', 'document'],
|
|
126
|
+
inputPrice: 0.1,
|
|
127
|
+
outputPrice: 0.5,
|
|
128
|
+
supportsToolUse: true,
|
|
129
|
+
supportsStreaming: true,
|
|
130
|
+
effortLevels: ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
131
|
+
},
|
|
105
132
|
{
|
|
106
133
|
id: 'gpt-5.6-sol',
|
|
107
134
|
name: 'GPT 5.6 Sol',
|
|
@@ -323,6 +350,19 @@ const ZEN_MODELS = freezeModels([
|
|
|
323
350
|
supportsStreaming: true,
|
|
324
351
|
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
325
352
|
},
|
|
353
|
+
{
|
|
354
|
+
id: 'claude-opus-5-5',
|
|
355
|
+
name: 'Claude Opus 5.5',
|
|
356
|
+
protocol: 'messages',
|
|
357
|
+
contextWindow: 1000000,
|
|
358
|
+
maxOutputTokens: 128000,
|
|
359
|
+
inputModalities: ['text', 'image', 'document'],
|
|
360
|
+
inputPrice: 4.0,
|
|
361
|
+
outputPrice: 20.0,
|
|
362
|
+
supportsToolUse: true,
|
|
363
|
+
supportsStreaming: true,
|
|
364
|
+
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
365
|
+
},
|
|
326
366
|
{
|
|
327
367
|
id: 'claude-opus-5',
|
|
328
368
|
name: 'Claude Opus 5',
|
|
@@ -596,6 +636,19 @@ const ZEN_MODELS = freezeModels([
|
|
|
596
636
|
supportsStreaming: true,
|
|
597
637
|
effortLevels: ['minimal', 'low', 'medium', 'high', 'xhigh'],
|
|
598
638
|
},
|
|
639
|
+
{
|
|
640
|
+
id: 'qwen3.8-max',
|
|
641
|
+
name: 'Qwen3.8 Max',
|
|
642
|
+
protocol: 'chat',
|
|
643
|
+
contextWindow: 262144,
|
|
644
|
+
maxOutputTokens: 131072,
|
|
645
|
+
inputModalities: ['text', 'image'],
|
|
646
|
+
inputPrice: 2.0,
|
|
647
|
+
outputPrice: 6.0,
|
|
648
|
+
supportsToolUse: true,
|
|
649
|
+
supportsStreaming: true,
|
|
650
|
+
effortLevels: [],
|
|
651
|
+
},
|
|
599
652
|
{
|
|
600
653
|
id: 'qwen3.8-flash',
|
|
601
654
|
name: 'Qwen3.8 Flash',
|
|
@@ -806,7 +859,6 @@ const ZEN_MODELS = freezeModels([
|
|
|
806
859
|
},
|
|
807
860
|
{
|
|
808
861
|
id: 'big-pickle',
|
|
809
|
-
supportsAnonymousAccess: true,
|
|
810
862
|
name: 'Big Pickle',
|
|
811
863
|
protocol: 'chat',
|
|
812
864
|
contextWindow: 200000,
|
|
@@ -819,8 +871,34 @@ const ZEN_MODELS = freezeModels([
|
|
|
819
871
|
effortLevels: [],
|
|
820
872
|
},
|
|
821
873
|
{
|
|
822
|
-
id: '
|
|
874
|
+
id: 'space-bunny-free',
|
|
823
875
|
supportsAnonymousAccess: true,
|
|
876
|
+
name: 'Space Bunny Free',
|
|
877
|
+
protocol: 'chat',
|
|
878
|
+
contextWindow: 1048576,
|
|
879
|
+
maxOutputTokens: 524288,
|
|
880
|
+
inputModalities: ['text', 'image'],
|
|
881
|
+
inputPrice: 0,
|
|
882
|
+
outputPrice: 0,
|
|
883
|
+
supportsToolUse: true,
|
|
884
|
+
supportsStreaming: true,
|
|
885
|
+
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
id: 'longcat-2.5-preview-free',
|
|
889
|
+
name: 'LongCat 2.5 Preview Free',
|
|
890
|
+
protocol: 'chat',
|
|
891
|
+
contextWindow: 1000000,
|
|
892
|
+
maxOutputTokens: 131072,
|
|
893
|
+
inputModalities: ['text', 'image'],
|
|
894
|
+
inputPrice: 0,
|
|
895
|
+
outputPrice: 0,
|
|
896
|
+
supportsToolUse: true,
|
|
897
|
+
supportsStreaming: true,
|
|
898
|
+
effortLevels: [],
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
id: 'mimo-v2.5-free',
|
|
824
902
|
name: 'MiMo-V2.5 Free',
|
|
825
903
|
protocol: 'chat',
|
|
826
904
|
contextWindow: 200000,
|
|
@@ -834,7 +912,6 @@ const ZEN_MODELS = freezeModels([
|
|
|
834
912
|
},
|
|
835
913
|
{
|
|
836
914
|
id: 'ling-3.0-flash-fin-free',
|
|
837
|
-
supportsAnonymousAccess: true,
|
|
838
915
|
name: 'Ling 3.0 Flash Fin Free',
|
|
839
916
|
protocol: 'chat',
|
|
840
917
|
contextWindow: 262144,
|
|
@@ -848,7 +925,6 @@ const ZEN_MODELS = freezeModels([
|
|
|
848
925
|
},
|
|
849
926
|
{
|
|
850
927
|
id: 'nemotron-3-ultra-free',
|
|
851
|
-
supportsAnonymousAccess: true,
|
|
852
928
|
name: 'Nemotron 3 Ultra Free',
|
|
853
929
|
protocol: 'chat',
|
|
854
930
|
contextWindow: 1000000,
|
|
@@ -862,7 +938,6 @@ const ZEN_MODELS = freezeModels([
|
|
|
862
938
|
},
|
|
863
939
|
{
|
|
864
940
|
id: 'nemotron-3.5-lightning-free',
|
|
865
|
-
supportsAnonymousAccess: true,
|
|
866
941
|
name: 'Nemotron 3.5 Lightning Free',
|
|
867
942
|
protocol: 'chat',
|
|
868
943
|
contextWindow: 262144,
|
|
@@ -876,7 +951,6 @@ const ZEN_MODELS = freezeModels([
|
|
|
876
951
|
},
|
|
877
952
|
{
|
|
878
953
|
id: 'muse-spark-1.3-contributor-free',
|
|
879
|
-
supportsAnonymousAccess: true,
|
|
880
954
|
name: 'Muse Spark 1.3 Contributor Free',
|
|
881
955
|
protocol: 'responses',
|
|
882
956
|
contextWindow: 1048576,
|
|
@@ -917,6 +991,19 @@ const GO_MODELS = freezeModels([
|
|
|
917
991
|
supportsStreaming: true,
|
|
918
992
|
effortLevels: ['low', 'medium', 'high', 'xhigh'],
|
|
919
993
|
},
|
|
994
|
+
{
|
|
995
|
+
id: 'gpt-6-luna',
|
|
996
|
+
name: 'GPT 6 Luna',
|
|
997
|
+
protocol: 'responses',
|
|
998
|
+
contextWindow: 1050000,
|
|
999
|
+
maxOutputTokens: 128000,
|
|
1000
|
+
inputModalities: ['text', 'image', 'document'],
|
|
1001
|
+
inputPrice: 0.1,
|
|
1002
|
+
outputPrice: 0.5,
|
|
1003
|
+
supportsToolUse: true,
|
|
1004
|
+
supportsStreaming: true,
|
|
1005
|
+
effortLevels: ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
1006
|
+
},
|
|
920
1007
|
{
|
|
921
1008
|
id: 'gpt-5.6-luna',
|
|
922
1009
|
name: 'GPT 5.6 Luna',
|
|
@@ -969,19 +1056,6 @@ const GO_MODELS = freezeModels([
|
|
|
969
1056
|
supportsStreaming: true,
|
|
970
1057
|
effortLevels: ['high', 'max'],
|
|
971
1058
|
},
|
|
972
|
-
{
|
|
973
|
-
id: 'glm-5.1',
|
|
974
|
-
name: 'GLM-5.1',
|
|
975
|
-
protocol: 'chat',
|
|
976
|
-
contextWindow: 202752,
|
|
977
|
-
maxOutputTokens: 32768,
|
|
978
|
-
inputModalities: ['text'],
|
|
979
|
-
inputPrice: 1.4,
|
|
980
|
-
outputPrice: 4.4,
|
|
981
|
-
supportsToolUse: true,
|
|
982
|
-
supportsStreaming: true,
|
|
983
|
-
effortLevels: [],
|
|
984
|
-
},
|
|
985
1059
|
{
|
|
986
1060
|
id: 'kimi-k3',
|
|
987
1061
|
name: 'Kimi K3',
|
|
@@ -1034,6 +1108,19 @@ const GO_MODELS = freezeModels([
|
|
|
1034
1108
|
supportsStreaming: true,
|
|
1035
1109
|
effortLevels: [],
|
|
1036
1110
|
},
|
|
1111
|
+
{
|
|
1112
|
+
id: 'longcat-2.5-preview-free',
|
|
1113
|
+
name: 'LongCat 2.5 Preview Free',
|
|
1114
|
+
protocol: 'chat',
|
|
1115
|
+
contextWindow: 1000000,
|
|
1116
|
+
maxOutputTokens: 131072,
|
|
1117
|
+
inputModalities: ['text', 'image'],
|
|
1118
|
+
inputPrice: 0,
|
|
1119
|
+
outputPrice: 0,
|
|
1120
|
+
supportsToolUse: true,
|
|
1121
|
+
supportsStreaming: true,
|
|
1122
|
+
effortLevels: [],
|
|
1123
|
+
},
|
|
1037
1124
|
{
|
|
1038
1125
|
id: 'deepseek-v4.1-flash',
|
|
1039
1126
|
name: 'DeepSeek V4.1 Flash',
|
|
@@ -1063,7 +1150,7 @@ const GO_MODELS = freezeModels([
|
|
|
1063
1150
|
{
|
|
1064
1151
|
id: 'deepseek-v4-flash',
|
|
1065
1152
|
name: 'DeepSeek V4 Flash',
|
|
1066
|
-
protocol: '
|
|
1153
|
+
protocol: 'responses',
|
|
1067
1154
|
contextWindow: 1000000,
|
|
1068
1155
|
maxOutputTokens: 384000,
|
|
1069
1156
|
inputModalities: ['text'],
|
|
@@ -1115,7 +1202,7 @@ const GO_MODELS = freezeModels([
|
|
|
1115
1202
|
{
|
|
1116
1203
|
id: 'minimax-m3',
|
|
1117
1204
|
name: 'MiniMax M3',
|
|
1118
|
-
protocol: '
|
|
1205
|
+
protocol: 'chat',
|
|
1119
1206
|
contextWindow: 1000000,
|
|
1120
1207
|
maxOutputTokens: 131072,
|
|
1121
1208
|
inputModalities: ['text', 'image'],
|
|
@@ -1128,7 +1215,7 @@ const GO_MODELS = freezeModels([
|
|
|
1128
1215
|
{
|
|
1129
1216
|
id: 'minimax-m2.7',
|
|
1130
1217
|
name: 'MiniMax M2.7',
|
|
1131
|
-
protocol: '
|
|
1218
|
+
protocol: 'chat',
|
|
1132
1219
|
contextWindow: 204800,
|
|
1133
1220
|
maxOutputTokens: 131072,
|
|
1134
1221
|
inputModalities: ['text'],
|
|
@@ -1190,19 +1277,6 @@ const GO_MODELS = freezeModels([
|
|
|
1190
1277
|
supportsStreaming: true,
|
|
1191
1278
|
effortLevels: ['low', 'medium', 'xhigh'],
|
|
1192
1279
|
},
|
|
1193
|
-
{
|
|
1194
|
-
id: 'qwen3.7-max',
|
|
1195
|
-
name: 'Qwen3.7 Max',
|
|
1196
|
-
protocol: 'messages',
|
|
1197
|
-
contextWindow: 1000000,
|
|
1198
|
-
maxOutputTokens: 65536,
|
|
1199
|
-
inputModalities: ['text'],
|
|
1200
|
-
inputPrice: 2.5,
|
|
1201
|
-
outputPrice: 7.5,
|
|
1202
|
-
supportsToolUse: true,
|
|
1203
|
-
supportsStreaming: true,
|
|
1204
|
-
effortLevels: [],
|
|
1205
|
-
},
|
|
1206
1280
|
{
|
|
1207
1281
|
id: 'qwen3.7-plus',
|
|
1208
1282
|
name: 'Qwen3.7 Plus',
|
|
@@ -1216,19 +1290,6 @@ const GO_MODELS = freezeModels([
|
|
|
1216
1290
|
supportsStreaming: true,
|
|
1217
1291
|
effortLevels: [],
|
|
1218
1292
|
},
|
|
1219
|
-
{
|
|
1220
|
-
id: 'qwen3.6-plus',
|
|
1221
|
-
name: 'Qwen3.6 Plus',
|
|
1222
|
-
protocol: 'messages',
|
|
1223
|
-
contextWindow: 1000000,
|
|
1224
|
-
maxOutputTokens: 65536,
|
|
1225
|
-
inputModalities: ['text', 'image'],
|
|
1226
|
-
inputPrice: 0.5,
|
|
1227
|
-
outputPrice: 3.0,
|
|
1228
|
-
supportsToolUse: true,
|
|
1229
|
-
supportsStreaming: true,
|
|
1230
|
-
effortLevels: [],
|
|
1231
|
-
},
|
|
1232
1293
|
{
|
|
1233
1294
|
id: 'hy4-preview',
|
|
1234
1295
|
name: 'Hy4 preview',
|
|
@@ -1255,6 +1316,19 @@ const GO_MODELS = freezeModels([
|
|
|
1255
1316
|
supportsStreaming: true,
|
|
1256
1317
|
effortLevels: ['none', 'low', 'high'],
|
|
1257
1318
|
},
|
|
1319
|
+
{
|
|
1320
|
+
id: 'space-bunny-free',
|
|
1321
|
+
name: 'Space Bunny Free',
|
|
1322
|
+
protocol: 'chat',
|
|
1323
|
+
contextWindow: 1048576,
|
|
1324
|
+
maxOutputTokens: 524288,
|
|
1325
|
+
inputModalities: ['text', 'image'],
|
|
1326
|
+
inputPrice: 0,
|
|
1327
|
+
outputPrice: 0,
|
|
1328
|
+
supportsToolUse: true,
|
|
1329
|
+
supportsStreaming: true,
|
|
1330
|
+
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
1331
|
+
},
|
|
1258
1332
|
])
|
|
1259
1333
|
|
|
1260
1334
|
/**
|
|
@@ -1265,6 +1339,7 @@ const GO_MODELS = freezeModels([
|
|
|
1265
1339
|
export const ZEN_OMITTED_MODELS: readonly string[] = Object.freeze([
|
|
1266
1340
|
'go/deepseek-flash',
|
|
1267
1341
|
'go/glm-5',
|
|
1342
|
+
'go/glm-5.1',
|
|
1268
1343
|
'go/grok-4.5',
|
|
1269
1344
|
'go/hy3-preview',
|
|
1270
1345
|
'go/kimi-k2.5',
|
|
@@ -1275,6 +1350,8 @@ export const ZEN_OMITTED_MODELS: readonly string[] = Object.freeze([
|
|
|
1275
1350
|
'go/minimax-m2.5',
|
|
1276
1351
|
'go/omen-alpha',
|
|
1277
1352
|
'go/qwen3.5-plus',
|
|
1353
|
+
'go/qwen3.6-plus',
|
|
1354
|
+
'go/qwen3.7-max',
|
|
1278
1355
|
'zen/claude-sonnet-4',
|
|
1279
1356
|
'zen/deepseek-v4-flash-free',
|
|
1280
1357
|
'zen/glm-5',
|
package/src/options.ts
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
import { getModelCapabilities as getMessagesCapabilities } from '@ai-sdk/anthropic/internal'
|
|
2
2
|
import type { LanguageModelV3CallOptions, SharedV3ProviderOptions } from '@ai-sdk/provider'
|
|
3
3
|
import { type ChatCompletionParams, ProviderRequestError, type ProviderRoute } from '@namzu/sdk'
|
|
4
|
-
import {
|
|
5
|
-
import { toModelPrompt } from './prompt.js'
|
|
4
|
+
import type { ZenModel, ZenProtocol, ZenService } from './models.js'
|
|
5
|
+
import { isMuseSparkResponsesModel, toModelPrompt } from './prompt.js'
|
|
6
|
+
|
|
7
|
+
function goRejectsToolChoice(service: ZenService, model: string): boolean {
|
|
8
|
+
if (service !== 'go') return false
|
|
9
|
+
if (model.startsWith('mimo-')) return true
|
|
10
|
+
if (model === 'deepseek-flash') return true
|
|
11
|
+
return (
|
|
12
|
+
/^deepseek-v4(?:\.\d+)?-(?:flash|pro)(?:$|-)/.test(model) &&
|
|
13
|
+
model !== 'deepseek-v4-flash-vision-exp'
|
|
14
|
+
)
|
|
15
|
+
}
|
|
6
16
|
|
|
7
17
|
export function createCallOptions(
|
|
8
18
|
params: ChatCompletionParams,
|
|
9
19
|
route: ProviderRoute,
|
|
10
20
|
service: ZenService,
|
|
11
21
|
protocol: ZenProtocol,
|
|
22
|
+
known: ZenModel | undefined,
|
|
12
23
|
): LanguageModelV3CallOptions {
|
|
13
24
|
const refuse = (detail: string): never => {
|
|
14
|
-
throw new ProviderRequestError({
|
|
25
|
+
throw new ProviderRequestError({
|
|
26
|
+
providerId: route.providerId,
|
|
27
|
+
kind: 'bad_request',
|
|
28
|
+
detail,
|
|
29
|
+
})
|
|
15
30
|
}
|
|
16
|
-
const known = findZenModel(service, params.model)
|
|
17
31
|
let maxOutputTokens = params.maxTokens ?? 4096
|
|
18
32
|
if (!Number.isSafeInteger(maxOutputTokens) || maxOutputTokens <= 0)
|
|
19
33
|
refuse('maxTokens must be a positive integer.')
|
|
@@ -46,7 +60,9 @@ export function createCallOptions(
|
|
|
46
60
|
: {}),
|
|
47
61
|
...(params.thinking ? { thinking: { type: params.thinking.type } } : {}),
|
|
48
62
|
...(params.responseFormat?.type === 'json_schema'
|
|
49
|
-
? {
|
|
63
|
+
? {
|
|
64
|
+
strictJsonSchema: params.responseFormat.json_schema.strict ?? true,
|
|
65
|
+
}
|
|
50
66
|
: {}),
|
|
51
67
|
}
|
|
52
68
|
break
|
|
@@ -95,7 +111,10 @@ export function createCallOptions(
|
|
|
95
111
|
refuse('This responses model does not support reasoning controls.')
|
|
96
112
|
options.openai = {
|
|
97
113
|
store: false,
|
|
98
|
-
|
|
114
|
+
// Muse Spark's gateway cannot replay caller-bound encrypted reasoning.
|
|
115
|
+
...(isMuseSparkResponsesModel(params.model, protocol)
|
|
116
|
+
? {}
|
|
117
|
+
: { include: ['reasoning.encrypted_content'] }),
|
|
99
118
|
...(known?.effortLevels?.length ? { forceReasoning: true } : {}),
|
|
100
119
|
...(params.effort !== undefined ? { reasoningEffort: params.effort } : {}),
|
|
101
120
|
...(params.thinking?.type === 'disabled' ? { reasoningEffort: 'none' } : {}),
|
|
@@ -104,7 +123,9 @@ export function createCallOptions(
|
|
|
104
123
|
? { parallelToolCalls: params.parallelToolCalls }
|
|
105
124
|
: {}),
|
|
106
125
|
...(params.responseFormat?.type === 'json_schema'
|
|
107
|
-
? {
|
|
126
|
+
? {
|
|
127
|
+
strictJsonSchema: params.responseFormat.json_schema.strict ?? true,
|
|
128
|
+
}
|
|
108
129
|
: {}),
|
|
109
130
|
}
|
|
110
131
|
break
|
|
@@ -211,7 +232,16 @@ export function createCallOptions(
|
|
|
211
232
|
// Some Responses upstreams accept only auto tool choice (observed on Muse).
|
|
212
233
|
// A no-tools turn can preserve its meaning by exposing no tools at all.
|
|
213
234
|
// Keep required/named choices explicit; never silently weaken them to auto.
|
|
214
|
-
const
|
|
235
|
+
const rejectsToolChoice = goRejectsToolChoice(service, params.model)
|
|
236
|
+
if (
|
|
237
|
+
rejectsToolChoice &&
|
|
238
|
+
params.toolChoice !== undefined &&
|
|
239
|
+
params.toolChoice !== 'auto' &&
|
|
240
|
+
params.toolChoice !== 'none'
|
|
241
|
+
)
|
|
242
|
+
refuse('This Zen Go model does not support required or named tool choice.')
|
|
243
|
+
const withoutTools =
|
|
244
|
+
(protocol === 'responses' || rejectsToolChoice) && params.toolChoice === 'none'
|
|
215
245
|
const enforced = new Set(params.enforceToolInputSchema)
|
|
216
246
|
return {
|
|
217
247
|
prompt: toModelPrompt(params, route, service, protocol),
|
|
@@ -241,7 +271,7 @@ export function createCallOptions(
|
|
|
241
271
|
})),
|
|
242
272
|
}
|
|
243
273
|
: {}),
|
|
244
|
-
...(params.toolChoice && !withoutTools
|
|
274
|
+
...(params.toolChoice && !withoutTools && !rejectsToolChoice
|
|
245
275
|
? {
|
|
246
276
|
toolChoice:
|
|
247
277
|
typeof params.toolChoice === 'string'
|
package/src/prompt.ts
CHANGED
|
@@ -24,6 +24,20 @@ type NativePart = Extract<LanguageModelV3Content, { type: 'text' | 'reasoning' |
|
|
|
24
24
|
type AssistantContent = Extract<LanguageModelV3Message, { role: 'assistant' }>['content']
|
|
25
25
|
type RichOutput = Extract<LanguageModelV3ToolResultOutput, { type: 'content' }>['value']
|
|
26
26
|
|
|
27
|
+
/** Muse Spark's Responses gateway cannot round-trip caller-bound encrypted reasoning. */
|
|
28
|
+
export function isMuseSparkResponsesModel(model: string, protocol: ZenProtocol): boolean {
|
|
29
|
+
return protocol === 'responses' && model.startsWith('muse-spark-')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Go's DeepSeek V4 Responses lane requires reasoning before each assistant turn. */
|
|
33
|
+
export function isGoDeepSeekV4ResponsesModel(
|
|
34
|
+
service: ZenService,
|
|
35
|
+
model: string,
|
|
36
|
+
protocol: ZenProtocol,
|
|
37
|
+
): boolean {
|
|
38
|
+
return service === 'go' && protocol === 'responses' && model === 'deepseek-v4-flash'
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
class HistoryConversionError extends Error {}
|
|
28
42
|
|
|
29
43
|
function record(value: unknown): Record<string, unknown> | undefined {
|
|
@@ -137,7 +151,14 @@ function prefixDigest(messages: readonly Message[]): string {
|
|
|
137
151
|
chainIndex: message.source.chainIndex,
|
|
138
152
|
},
|
|
139
153
|
}),
|
|
154
|
+
// Bookkeeping the kernel stamps, not content the prefix is defined
|
|
155
|
+
// by: `id` names which durable record a message came from, and two
|
|
156
|
+
// requests built from the exact same prefix disagree on it whenever
|
|
157
|
+
// one side is a fresh, never-recorded copy of the other (or a
|
|
158
|
+
// fork's copy, carrying a different session's id for identical
|
|
159
|
+
// content).
|
|
140
160
|
timestamp: undefined,
|
|
161
|
+
id: undefined,
|
|
141
162
|
})),
|
|
142
163
|
)
|
|
143
164
|
}
|
|
@@ -275,7 +296,23 @@ function replayContent(
|
|
|
275
296
|
state.projectionDigest !== digest(projection(message))
|
|
276
297
|
)
|
|
277
298
|
return undefined
|
|
278
|
-
|
|
299
|
+
// Validate the original completed turn before dropping Muse's reasoning items.
|
|
300
|
+
// Their encrypted content belongs to the gateway's upstream caller and fails
|
|
301
|
+
// when replayed in a later tool round. Text and tool calls still round-trip.
|
|
302
|
+
let replayParts = isMuseSparkResponsesModel(params.model, protocol)
|
|
303
|
+
? content.filter((part) => part.type !== 'reasoning')
|
|
304
|
+
: content
|
|
305
|
+
if (isGoDeepSeekV4ResponsesModel(service, params.model, protocol)) {
|
|
306
|
+
// The native adapter emits each part in source order. Go rejects an
|
|
307
|
+
// assistant message between a function_call and its output, so group
|
|
308
|
+
// captured reasoning and text before this turn's tool-call batch.
|
|
309
|
+
replayParts = [
|
|
310
|
+
...replayParts.filter((part) => part.type === 'reasoning'),
|
|
311
|
+
...replayParts.filter((part) => part.type === 'text'),
|
|
312
|
+
...replayParts.filter((part) => part.type === 'tool-call'),
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
return replayParts.map((part) => {
|
|
279
316
|
const providerOptions =
|
|
280
317
|
part.providerMetadata === undefined
|
|
281
318
|
? undefined
|
|
@@ -284,7 +321,10 @@ function replayContent(
|
|
|
284
321
|
// The compatible adapter reads Gemini call signatures from the google namespace.
|
|
285
322
|
const thoughtSignature = metadataString(part, 'thoughtSignature')
|
|
286
323
|
if (protocol === 'chat' && providerOptions && thoughtSignature !== undefined) {
|
|
287
|
-
providerOptions.google = {
|
|
324
|
+
providerOptions.google = {
|
|
325
|
+
...providerOptions.google,
|
|
326
|
+
thoughtSignature,
|
|
327
|
+
}
|
|
288
328
|
}
|
|
289
329
|
return {
|
|
290
330
|
type: 'tool-call',
|
|
@@ -294,7 +334,11 @@ function replayContent(
|
|
|
294
334
|
...(providerOptions && { providerOptions }),
|
|
295
335
|
}
|
|
296
336
|
}
|
|
297
|
-
return {
|
|
337
|
+
return {
|
|
338
|
+
type: part.type,
|
|
339
|
+
text: part.text,
|
|
340
|
+
...(providerOptions && { providerOptions }),
|
|
341
|
+
}
|
|
298
342
|
})
|
|
299
343
|
}
|
|
300
344
|
|
|
@@ -454,7 +498,9 @@ function isRequestOnlyContext(message: Message): boolean {
|
|
|
454
498
|
)
|
|
455
499
|
}
|
|
456
500
|
|
|
457
|
-
const BREAKPOINT = {
|
|
501
|
+
const BREAKPOINT = {
|
|
502
|
+
anthropic: { cacheControl: { type: 'ephemeral' } },
|
|
503
|
+
} as const
|
|
458
504
|
|
|
459
505
|
/**
|
|
460
506
|
* Messages-protocol cache breakpoints, block-level: after the last static
|
package/src/types.ts
CHANGED
|
@@ -2,11 +2,11 @@ import type { ZenCatalogue } from './catalogue/catalogue.js'
|
|
|
2
2
|
import type { ZenProtocol } from './models.js'
|
|
3
3
|
|
|
4
4
|
export interface ZenConfig {
|
|
5
|
-
/** Omit for
|
|
5
|
+
/** Omit for a model admitted to direct anonymous Zen access; `public` selects the same path. */
|
|
6
6
|
apiKey?: string
|
|
7
7
|
/** Stable conversation identity, shared by every turn and auxiliary request. */
|
|
8
8
|
sessionId?: string
|
|
9
|
-
/** Defaults to
|
|
9
|
+
/** Defaults to Space Bunny Free anonymously, or glm-5.3-flash with an API key. */
|
|
10
10
|
model?: string
|
|
11
11
|
/** Override the service base URL, primarily for a host-owned proxy. */
|
|
12
12
|
baseURL?: string
|
|
@@ -16,11 +16,12 @@ export interface ZenConfig {
|
|
|
16
16
|
protocol?: ZenProtocol
|
|
17
17
|
/**
|
|
18
18
|
* A runtime catalogue, from `@namzu/zen/catalogue`, consulted before the
|
|
19
|
-
* bundled snapshot for every lookup: routing,
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* bundled snapshot for every lookup: routing, model listing, context windows
|
|
20
|
+
* and effort levels. Anonymous admission also requires the driver's curated
|
|
21
|
+
* direct-access ID; a catalogue cannot add to that set. A function is called
|
|
22
|
+
* at each lookup, so a host can swap in a fresher catalogue for providers it
|
|
23
|
+
* already built. Returning `undefined` means the bundled snapshot alone.
|
|
24
|
+
* Omitted, the provider uses the bundled snapshot and fetches nothing.
|
|
24
25
|
*/
|
|
25
26
|
catalogue?: ZenCatalogue | (() => ZenCatalogue | undefined)
|
|
26
27
|
}
|