@prismshadow/mmsp 0.5.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/README.md +121 -0
- package/dist/ant_messages/client.d.ts +59 -0
- package/dist/ant_messages/client.d.ts.map +1 -0
- package/dist/ant_messages/client.js +419 -0
- package/dist/ant_messages/index.d.ts +2 -0
- package/dist/ant_messages/index.d.ts.map +1 -0
- package/dist/ant_messages/index.js +18 -0
- package/dist/anthropic_official/client.d.ts +63 -0
- package/dist/anthropic_official/client.d.ts.map +1 -0
- package/dist/anthropic_official/client.js +540 -0
- package/dist/anthropic_official/index.d.ts +2 -0
- package/dist/anthropic_official/index.d.ts.map +1 -0
- package/dist/anthropic_official/index.js +18 -0
- package/dist/autoClient.d.ts +96 -0
- package/dist/autoClient.d.ts.map +1 -0
- package/dist/autoClient.js +258 -0
- package/dist/baseClient.d.ts +111 -0
- package/dist/baseClient.d.ts.map +1 -0
- package/dist/baseClient.js +284 -0
- package/dist/deepseek_official/client.d.ts +60 -0
- package/dist/deepseek_official/client.d.ts.map +1 -0
- package/dist/deepseek_official/client.js +407 -0
- package/dist/deepseek_official/index.d.ts +2 -0
- package/dist/deepseek_official/index.d.ts.map +1 -0
- package/dist/deepseek_official/index.js +18 -0
- package/dist/errors.d.ts +84 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +140 -0
- package/dist/gemini_generate_content/client.d.ts +86 -0
- package/dist/gemini_generate_content/client.d.ts.map +1 -0
- package/dist/gemini_generate_content/client.js +809 -0
- package/dist/gemini_generate_content/index.d.ts +2 -0
- package/dist/gemini_generate_content/index.d.ts.map +1 -0
- package/dist/gemini_generate_content/index.js +18 -0
- package/dist/gemini_official/client.d.ts +87 -0
- package/dist/gemini_official/client.d.ts.map +1 -0
- package/dist/gemini_official/client.js +780 -0
- package/dist/gemini_official/index.d.ts +2 -0
- package/dist/gemini_official/index.d.ts.map +1 -0
- package/dist/gemini_official/index.js +18 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/integration/index.d.ts +1 -0
- package/dist/integration/index.d.ts.map +1 -0
- package/dist/integration/index.js +14 -0
- package/dist/integration/playground.d.ts +17 -0
- package/dist/integration/playground.d.ts.map +1 -0
- package/dist/integration/playground.js +3246 -0
- package/dist/integration/tracer.d.ts +188 -0
- package/dist/integration/tracer.d.ts.map +1 -0
- package/dist/integration/tracer.js +1928 -0
- package/dist/legacy.d.ts +13 -0
- package/dist/legacy.d.ts.map +1 -0
- package/dist/legacy.js +59 -0
- package/dist/minimax_official/client.d.ts +43 -0
- package/dist/minimax_official/client.d.ts.map +1 -0
- package/dist/minimax_official/client.js +346 -0
- package/dist/minimax_official/index.d.ts +2 -0
- package/dist/minimax_official/index.d.ts.map +1 -0
- package/dist/minimax_official/index.js +18 -0
- package/dist/moonshot_official/client.d.ts +73 -0
- package/dist/moonshot_official/client.d.ts.map +1 -0
- package/dist/moonshot_official/client.js +477 -0
- package/dist/moonshot_official/index.d.ts +2 -0
- package/dist/moonshot_official/index.d.ts.map +1 -0
- package/dist/moonshot_official/index.js +18 -0
- package/dist/openai_chat/client.d.ts +67 -0
- package/dist/openai_chat/client.d.ts.map +1 -0
- package/dist/openai_chat/client.js +419 -0
- package/dist/openai_chat/index.d.ts +2 -0
- package/dist/openai_chat/index.d.ts.map +1 -0
- package/dist/openai_chat/index.js +18 -0
- package/dist/openai_chat_vllm_adapter/client.d.ts +15 -0
- package/dist/openai_chat_vllm_adapter/client.d.ts.map +1 -0
- package/dist/openai_chat_vllm_adapter/client.js +118 -0
- package/dist/openai_chat_vllm_adapter/index.d.ts +2 -0
- package/dist/openai_chat_vllm_adapter/index.d.ts.map +1 -0
- package/dist/openai_chat_vllm_adapter/index.js +5 -0
- package/dist/openai_embedding/client.d.ts +46 -0
- package/dist/openai_embedding/client.d.ts.map +1 -0
- package/dist/openai_embedding/client.js +124 -0
- package/dist/openai_embedding/index.d.ts +2 -0
- package/dist/openai_embedding/index.d.ts.map +1 -0
- package/dist/openai_embedding/index.js +18 -0
- package/dist/openai_official/client.d.ts +61 -0
- package/dist/openai_official/client.d.ts.map +1 -0
- package/dist/openai_official/client.js +464 -0
- package/dist/openai_official/index.d.ts +2 -0
- package/dist/openai_official/index.d.ts.map +1 -0
- package/dist/openai_official/index.js +18 -0
- package/dist/openai_responses/client.d.ts +61 -0
- package/dist/openai_responses/client.d.ts.map +1 -0
- package/dist/openai_responses/client.js +449 -0
- package/dist/openai_responses/index.d.ts +2 -0
- package/dist/openai_responses/index.d.ts.map +1 -0
- package/dist/openai_responses/index.js +18 -0
- package/dist/registry.d.ts +49 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +798 -0
- package/dist/streamItems.d.ts +36 -0
- package/dist/streamItems.d.ts.map +1 -0
- package/dist/streamItems.js +183 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +37 -0
- package/dist/utils.d.ts +99 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +312 -0
- package/dist/zai_official/client.d.ts +78 -0
- package/dist/zai_official/client.d.ts.map +1 -0
- package/dist/zai_official/client.js +420 -0
- package/dist/zai_official/index.d.ts +2 -0
- package/dist/zai_official/index.d.ts.map +1 -0
- package/dist/zai_official/index.js +18 -0
- package/package.json +68 -0
package/dist/utils.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2025 Prism Shadow. and/or its affiliates
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.fixOpenrouterUsageMetadata = fixOpenrouterUsageMetadata;
|
|
17
|
+
exports.resolveCredentials = resolveCredentials;
|
|
18
|
+
exports.isDebugEnabled = isDebugEnabled;
|
|
19
|
+
exports.imageDimensions = imageDimensions;
|
|
20
|
+
exports.exceedsOpenaiPatchLimit = exceedsOpenaiPatchLimit;
|
|
21
|
+
exports.openaiImageDetail = openaiImageDetail;
|
|
22
|
+
exports.speakerTurns = speakerTurns;
|
|
23
|
+
/**
|
|
24
|
+
* Fix the usage metadata for OpenRouter.
|
|
25
|
+
*
|
|
26
|
+
* OpenRouter occasionally does not include the reasoning tokens to the completion tokens.
|
|
27
|
+
*
|
|
28
|
+
* @param usageMetadata - The usage metadata.
|
|
29
|
+
* @param baseUrl - The API URL.
|
|
30
|
+
* @returns The fixed usage metadata.
|
|
31
|
+
*/
|
|
32
|
+
function fixOpenrouterUsageMetadata(usageMetadata, baseUrl) {
|
|
33
|
+
const fixedUsageMetadata = { ...usageMetadata };
|
|
34
|
+
if (baseUrl.includes("openrouter.ai") &&
|
|
35
|
+
fixedUsageMetadata.response_tokens !== null &&
|
|
36
|
+
fixedUsageMetadata.response_tokens < 0) {
|
|
37
|
+
fixedUsageMetadata.response_tokens +=
|
|
38
|
+
fixedUsageMetadata.thoughts_tokens || 0;
|
|
39
|
+
}
|
|
40
|
+
return fixedUsageMetadata;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether MMSP_DEBUG asks the clients to fail loudly on output they do not recognize.
|
|
44
|
+
*
|
|
45
|
+
* Streaming clients skip an unrecognized event so that a gateway's own frames cannot
|
|
46
|
+
* kill a long generation. The same silence hides a genuinely new provider event, so the
|
|
47
|
+
* guards stay one environment variable away.
|
|
48
|
+
*
|
|
49
|
+
* @returns Whether debug mode is on.
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* The key and the endpoint of a client that reads the environment's OPENAI_API_KEY or
|
|
53
|
+
* ANTHROPIC_API_KEY. A key passed in is used as it is. Without one, the environment's key goes
|
|
54
|
+
* out only to the endpoint the environment names: a base URL passed in is another endpoint,
|
|
55
|
+
* which that key was not given for, so it needs a key of its own.
|
|
56
|
+
*
|
|
57
|
+
* @param client - The client's class name, for the error
|
|
58
|
+
* @param options - The key and the base URL passed to the client
|
|
59
|
+
* @param env - The environment variables that hold the key and the base URL
|
|
60
|
+
* @returns The key and the base URL to build the SDK client with, either of which may be undefined
|
|
61
|
+
*/
|
|
62
|
+
function resolveCredentials(client, options, env) {
|
|
63
|
+
if (options.apiKey) {
|
|
64
|
+
return {
|
|
65
|
+
apiKey: options.apiKey,
|
|
66
|
+
baseUrl: options.baseUrl || process.env[env.baseUrl] || undefined,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (options.baseUrl) {
|
|
70
|
+
throw new Error(`apiKey is required for ${client} with a baseUrl: ${env.key} is not sent to another endpoint.`);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
apiKey: process.env[env.key] || undefined,
|
|
74
|
+
baseUrl: process.env[env.baseUrl] || undefined,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function isDebugEnabled() {
|
|
78
|
+
const flag = (process.env.MMSP_DEBUG || "").trim().toLowerCase();
|
|
79
|
+
return !["", "0", "false", "no", "off"].includes(flag);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Read the pixel dimensions from the header of a PNG, JPEG, GIF or WebP image.
|
|
83
|
+
*
|
|
84
|
+
* Only the header is inspected, so the bytes may be a prefix of the file; a
|
|
85
|
+
* prefix that ends before the dimensions are reached reads as unrecognized.
|
|
86
|
+
*
|
|
87
|
+
* @param bytes - The image bytes, or a prefix of them.
|
|
88
|
+
* @returns The dimensions, or null when the bytes are not a recognized image.
|
|
89
|
+
*/
|
|
90
|
+
function imageDimensions(bytes) {
|
|
91
|
+
// PNG: an 8-byte signature, then the IHDR chunk with width and height
|
|
92
|
+
if (bytes.length >= 24 &&
|
|
93
|
+
bytes.toString("latin1", 0, 8) === "\x89PNG\r\n\x1a\n" &&
|
|
94
|
+
bytes.toString("latin1", 12, 16) === "IHDR") {
|
|
95
|
+
return { width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20) };
|
|
96
|
+
}
|
|
97
|
+
// GIF: the logical screen size follows the 6-byte signature
|
|
98
|
+
if (bytes.length >= 10 &&
|
|
99
|
+
["GIF87a", "GIF89a"].includes(bytes.toString("latin1", 0, 6))) {
|
|
100
|
+
return { width: bytes.readUInt16LE(6), height: bytes.readUInt16LE(8) };
|
|
101
|
+
}
|
|
102
|
+
// WebP: a RIFF container whose first chunk names the bitstream flavour
|
|
103
|
+
if (bytes.length >= 30 &&
|
|
104
|
+
bytes.toString("latin1", 0, 4) === "RIFF" &&
|
|
105
|
+
bytes.toString("latin1", 8, 12) === "WEBP") {
|
|
106
|
+
const chunk = bytes.toString("latin1", 12, 16);
|
|
107
|
+
if (chunk === "VP8 " &&
|
|
108
|
+
bytes[23] === 0x9d &&
|
|
109
|
+
bytes[24] === 0x01 &&
|
|
110
|
+
bytes[25] === 0x2a) {
|
|
111
|
+
// lossy: a 3-byte frame tag and the key frame start code precede the size,
|
|
112
|
+
// whose top two bits are a scaling hint
|
|
113
|
+
return {
|
|
114
|
+
width: bytes.readUInt16LE(26) & 0x3fff,
|
|
115
|
+
height: bytes.readUInt16LE(28) & 0x3fff,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (chunk === "VP8L" && bytes[20] === 0x2f) {
|
|
119
|
+
// lossless: 14 bits of width minus one, then 14 bits of height minus one
|
|
120
|
+
const bits = bytes.readUInt32LE(21);
|
|
121
|
+
return {
|
|
122
|
+
width: (bits & 0x3fff) + 1,
|
|
123
|
+
height: ((bits >>> 14) & 0x3fff) + 1,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (chunk === "VP8X") {
|
|
127
|
+
// extended: the canvas size minus one, 24 bits each, after the flags
|
|
128
|
+
return {
|
|
129
|
+
width: bytes.readUIntLE(24, 3) + 1,
|
|
130
|
+
height: bytes.readUIntLE(27, 3) + 1,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
// JPEG: walk the marker segments to the first frame header (SOFn)
|
|
136
|
+
if (bytes.length >= 4 && bytes[0] === 0xff && bytes[1] === 0xd8) {
|
|
137
|
+
let offset = 2;
|
|
138
|
+
while (offset + 3 < bytes.length) {
|
|
139
|
+
if (bytes[offset] !== 0xff) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const marker = bytes[offset + 1];
|
|
143
|
+
if (marker === 0xff) {
|
|
144
|
+
// fill byte ahead of a marker
|
|
145
|
+
offset += 1;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (marker === 0x01 ||
|
|
149
|
+
marker === 0xd8 ||
|
|
150
|
+
(marker >= 0xd0 && marker <= 0xd7)) {
|
|
151
|
+
// TEM, SOI and RSTn stand alone, without a length
|
|
152
|
+
offset += 2;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (marker === 0xd9 || marker === 0xda) {
|
|
156
|
+
// end of image, or scan data before any frame header
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const frameHeader = marker >= 0xc0 &&
|
|
160
|
+
marker <= 0xcf &&
|
|
161
|
+
marker !== 0xc4 &&
|
|
162
|
+
marker !== 0xc8 &&
|
|
163
|
+
marker !== 0xcc;
|
|
164
|
+
if (frameHeader) {
|
|
165
|
+
if (offset + 8 >= bytes.length) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
// length, precision, then height before width
|
|
169
|
+
return {
|
|
170
|
+
width: bytes.readUInt16BE(offset + 7),
|
|
171
|
+
height: bytes.readUInt16BE(offset + 5),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
offset += 2 + bytes.readUInt16BE(offset + 2);
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The patch count above which the OpenAI vision API rejects an image instead
|
|
182
|
+
* of resizing it (Images and vision guide, "Choose an image detail level":
|
|
183
|
+
* https://developers.openai.com/api/docs/guides/images-vision).
|
|
184
|
+
*/
|
|
185
|
+
const OPENAI_PATCH_LIMIT = 30000;
|
|
186
|
+
/**
|
|
187
|
+
* The longest side the OpenAI vision API keeps at `original` detail; a larger
|
|
188
|
+
* image is scaled down to fit it before the patches are counted (the same
|
|
189
|
+
* guide, model sizing table).
|
|
190
|
+
*/
|
|
191
|
+
const OPENAI_ORIGINAL_MAX_SIDE = 65535;
|
|
192
|
+
/**
|
|
193
|
+
* Base64 characters decoded first: 48 bytes, enough for any PNG, GIF or WebP
|
|
194
|
+
* header. A JPEG's frame header may sit behind metadata segments, so its
|
|
195
|
+
* window grows by the factor below until the header is found or the payload
|
|
196
|
+
* runs out.
|
|
197
|
+
*/
|
|
198
|
+
const HEADER_PROBE_CHARS = 64;
|
|
199
|
+
const HEADER_WINDOW_GROWTH = 4;
|
|
200
|
+
/**
|
|
201
|
+
* Locate the payload of a base64 data URL.
|
|
202
|
+
*
|
|
203
|
+
* @param dataUrl - The URL.
|
|
204
|
+
* @returns The index of the payload's first character, or -1 when the URL is
|
|
205
|
+
* not a base64 data URL.
|
|
206
|
+
*/
|
|
207
|
+
function base64PayloadStart(dataUrl) {
|
|
208
|
+
if (!dataUrl.startsWith("data:")) {
|
|
209
|
+
return -1;
|
|
210
|
+
}
|
|
211
|
+
const comma = dataUrl.indexOf(",");
|
|
212
|
+
if (comma < 0) {
|
|
213
|
+
return -1;
|
|
214
|
+
}
|
|
215
|
+
// the token is case-insensitive and may follow a space, as a browser reads it
|
|
216
|
+
const params = dataUrl
|
|
217
|
+
.slice(5, comma)
|
|
218
|
+
.split(";")
|
|
219
|
+
.map((param) => param.trim().toLowerCase());
|
|
220
|
+
return params.includes("base64") ? comma + 1 : -1;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Whether the OpenAI vision API would reject an image at `original` detail.
|
|
224
|
+
*
|
|
225
|
+
* The API covers an image with 32-pixel patches and rejects one that needs
|
|
226
|
+
* more than 30,000 of them after its own resizing; at `original` detail the
|
|
227
|
+
* only resizing is the 65,535-pixel cap on either side. Only a base64 data URL
|
|
228
|
+
* can be measured here: an HTTP(S) URL answers false. The Responses clients
|
|
229
|
+
* pass such a URL through for the API to fetch; the Chat client fetches it
|
|
230
|
+
* into a data URL first, so it measures the fetched bytes.
|
|
231
|
+
*
|
|
232
|
+
* @param imageUrl - The image URL.
|
|
233
|
+
* @returns Whether the API would reject the image.
|
|
234
|
+
*/
|
|
235
|
+
function exceedsOpenaiPatchLimit(imageUrl) {
|
|
236
|
+
const start = base64PayloadStart(imageUrl);
|
|
237
|
+
if (start < 0) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
// Decode a growing prefix rather than the whole payload. Node's decoder
|
|
241
|
+
// skips whitespace, accepts the URL-safe alphabet and yields the bytes that
|
|
242
|
+
// are complete at a cut, so the window is sliced by character count alone.
|
|
243
|
+
let size = null;
|
|
244
|
+
for (let chars = HEADER_PROBE_CHARS;; chars *= HEADER_WINDOW_GROWTH) {
|
|
245
|
+
const bytes = Buffer.from(imageUrl.slice(start, start + chars), "base64");
|
|
246
|
+
size = imageDimensions(bytes);
|
|
247
|
+
if (size !== null ||
|
|
248
|
+
bytes[0] !== 0xff ||
|
|
249
|
+
bytes[1] !== 0xd8 ||
|
|
250
|
+
start + chars >= imageUrl.length) {
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (size === null) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
let { width, height } = size;
|
|
258
|
+
const longest = Math.max(width, height);
|
|
259
|
+
if (longest > OPENAI_ORIGINAL_MAX_SIDE) {
|
|
260
|
+
width = Math.round((width * OPENAI_ORIGINAL_MAX_SIDE) / longest);
|
|
261
|
+
height = Math.round((height * OPENAI_ORIGINAL_MAX_SIDE) / longest);
|
|
262
|
+
}
|
|
263
|
+
return Math.ceil(width / 32) * Math.ceil(height / 32) > OPENAI_PATCH_LIMIT;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* The `detail` an OpenAI image part needs so that the API reads the image.
|
|
267
|
+
*
|
|
268
|
+
* GPT-5.6 reads the default `auto` detail as `original`, which keeps the
|
|
269
|
+
* image's own dimensions and rejects one over 30,000 patches instead of
|
|
270
|
+
* resizing it; `high` has the API fit it into 2,500 patches, so the image is
|
|
271
|
+
* read instead of refused. Every other model keeps a patch budget at every
|
|
272
|
+
* detail level, so no other model gets the field.
|
|
273
|
+
*
|
|
274
|
+
* @param model - The model id the request is sent with.
|
|
275
|
+
* @param imageUrl - The image URL as it goes on the wire.
|
|
276
|
+
* @returns `"high"` when the image needs it, otherwise undefined.
|
|
277
|
+
*/
|
|
278
|
+
function openaiImageDetail(model, imageUrl) {
|
|
279
|
+
return model.toLowerCase().includes("gpt-5.6") &&
|
|
280
|
+
exceedsOpenaiPatchLimit(imageUrl)
|
|
281
|
+
? "high"
|
|
282
|
+
: undefined;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Split a two-speaker TTS script into turns, for the TTS models that take each turn's speaker
|
|
286
|
+
* as metadata rather than as a label in the text.
|
|
287
|
+
*
|
|
288
|
+
* A line that starts with a configured speaker's name and a colon ("Joe: How's it going?")
|
|
289
|
+
* begins that speaker's turn; any other line continues the turn before it.
|
|
290
|
+
*
|
|
291
|
+
* @param text - The script, one "Name: line" per turn
|
|
292
|
+
* @param speakers - The speaker names the request configures
|
|
293
|
+
* @returns [speaker, text] per turn, in order
|
|
294
|
+
* @throws Error when the script does not begin with a speaker's label
|
|
295
|
+
*/
|
|
296
|
+
function speakerTurns(text, speakers) {
|
|
297
|
+
const turns = [];
|
|
298
|
+
for (const line of text.split(/\r?\n/)) {
|
|
299
|
+
const colon = line.indexOf(":");
|
|
300
|
+
const name = colon === -1 ? "" : line.slice(0, colon).trim();
|
|
301
|
+
if (colon !== -1 && speakers.includes(name)) {
|
|
302
|
+
turns.push([name, [line.slice(colon + 1).trim()]]);
|
|
303
|
+
}
|
|
304
|
+
else if (turns.length > 0) {
|
|
305
|
+
turns[turns.length - 1][1].push(line);
|
|
306
|
+
}
|
|
307
|
+
else if (line.trim()) {
|
|
308
|
+
throw new Error(`A two-speaker script must start each turn with a speaker's name and a colon, one of ${JSON.stringify(speakers)}.`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return turns.map(([speaker, lines]) => [speaker, lines.join("\n").trim()]);
|
|
312
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { ChatCompletionChunk, ChatCompletionMessageParam } from "openai/resources/chat/completions";
|
|
2
|
+
import { LLMClient } from "../baseClient";
|
|
3
|
+
import { UniConfig, UniEvent, UniMessage } from "../types";
|
|
4
|
+
/**
|
|
5
|
+
* Unified client for the GLM series, named for the newest generation it serves (5.3).
|
|
6
|
+
*
|
|
7
|
+
* The wire format is shared across GLM-5.1 through 5.3; only the thinking
|
|
8
|
+
* parameter contract differs per generation, handled model-by-model.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ZAIOfficialClient extends LLMClient {
|
|
11
|
+
protected _model: string;
|
|
12
|
+
private _client;
|
|
13
|
+
/**
|
|
14
|
+
* Initialize GLM client with model and API key.
|
|
15
|
+
*/
|
|
16
|
+
constructor(options: {
|
|
17
|
+
model: string;
|
|
18
|
+
apiKey?: string;
|
|
19
|
+
baseUrl?: string | null;
|
|
20
|
+
defaultHeaders?: Record<string, string>;
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Convert ThinkingLevel enum to GLM's thinking configuration.
|
|
24
|
+
*
|
|
25
|
+
* GLM-5.3 uses forced thinking and errors on {"type": "disabled"}, so NONE
|
|
26
|
+
* stays enabled there and degrades through the lightest reasoning effort
|
|
27
|
+
* instead (llmsdk_docs/glm5_3/docs/thinking.md).
|
|
28
|
+
*/
|
|
29
|
+
private _convertThinkingLevelToConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Convert ThinkingLevel enum to the reasoning_effort the model accepts.
|
|
32
|
+
*
|
|
33
|
+
* GLM-5.3 accepts only low/high/max and errors on anything else, so the
|
|
34
|
+
* client clamps to the closest value; NONE rides on low because 5.3 cannot
|
|
35
|
+
* disable thinking. Every earlier generation takes the vocabulary unchanged:
|
|
36
|
+
* 5.2 maps it server-side (low/medium to high, xhigh to max), and 5.1 and
|
|
37
|
+
* below accept the parameter and ignore it (verified live 2026-09-03 on
|
|
38
|
+
* Z.AI, OpenRouter and SiliconFlow), so the level is forwarded there rather
|
|
39
|
+
* than dropped. Outside 5.3 NONE disables thinking outright, which leaves no
|
|
40
|
+
* effort to send.
|
|
41
|
+
*/
|
|
42
|
+
private _convertThinkingLevelToReasoningEffort;
|
|
43
|
+
/**
|
|
44
|
+
* Convert ToolChoice to OpenAI's tool_choice format.
|
|
45
|
+
*/
|
|
46
|
+
private _convertToolChoice;
|
|
47
|
+
/**
|
|
48
|
+
* Transform universal configuration to GLM-specific configuration.
|
|
49
|
+
*/
|
|
50
|
+
transformUniConfigToModelConfig(config: UniConfig): any;
|
|
51
|
+
/**
|
|
52
|
+
* Transform universal message format to OpenAI's message format.
|
|
53
|
+
*/
|
|
54
|
+
transformUniMessageToModelInput(messages: UniMessage[], _signal?: AbortSignal): ChatCompletionMessageParam[];
|
|
55
|
+
/**
|
|
56
|
+
* Transform one GLM streaming chunk into a universal event.
|
|
57
|
+
*
|
|
58
|
+
* Chat Completions gives an item no identity, so each delta's item_id is the wire field
|
|
59
|
+
* that carried it: an item runs until a delta arrives from another field, or names the next
|
|
60
|
+
* tool call.
|
|
61
|
+
*/
|
|
62
|
+
transformModelOutputToUniEvent(modelOutput: ChatCompletionChunk): UniEvent;
|
|
63
|
+
/**
|
|
64
|
+
* Stream generate using GLM SDK with unified conversion methods.
|
|
65
|
+
*/
|
|
66
|
+
_streamingResponseInternal(options: {
|
|
67
|
+
messages: UniMessage[];
|
|
68
|
+
config: UniConfig;
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
}): AsyncGenerator<UniEvent>;
|
|
71
|
+
/**
|
|
72
|
+
* List the model ids the configured endpoint serves.
|
|
73
|
+
*
|
|
74
|
+
* @returns The model ids, in the order the endpoint returned them.
|
|
75
|
+
*/
|
|
76
|
+
listModels(): Promise<string[]>;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/zai_official/client.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,mBAAmB,EACnB,0BAA0B,EAE3B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAOL,SAAS,EACT,QAAQ,EACR,UAAU,EAEX,MAAM,UAAU,CAAC;AAGlB;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,OAAO,CAAS;IAExB;;OAEG;gBACS,OAAO,EAAE;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACzC;IAoBD;;;;;;OAMG;IACH,OAAO,CAAC,6BAA6B;IAerC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,sCAAsC;IAyB9C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IAEH,+BAA+B,CAAC,MAAM,EAAE,SAAS,GAAG,GAAG;IAiEvD;;OAEG;IACH,+BAA+B,CAC7B,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,CAAC,EAAE,WAAW,GACpB,0BAA0B,EAAE;IAyH/B;;;;;;OAMG;IACH,8BAA8B,CAAC,WAAW,EAAE,mBAAmB,GAAG,QAAQ;IAmH1E;;OAEG;IACI,0BAA0B,CAAC,OAAO,EAAE;QACzC,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,MAAM,EAAE,SAAS,CAAC;QAClB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,cAAc,CAAC,QAAQ,CAAC;IA6B5B;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAQtC"}
|