@jsonstudio/llms 0.6.199 → 0.6.203
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/dist/conversion/compat/actions/glm-tool-extraction.d.ts +2 -0
- package/dist/conversion/compat/actions/glm-tool-extraction.js +264 -0
- package/dist/conversion/compat/profiles/chat-glm.json +181 -181
- package/dist/conversion/compat/profiles/chat-iflow.json +195 -195
- package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -43
- package/dist/conversion/compat/profiles/chat-qwen.json +20 -20
- package/dist/conversion/compat/profiles/responses-c4m.json +42 -42
- package/dist/conversion/hub/pipeline/compat/compat-engine.js +3 -1087
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.d.ts +9 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +845 -0
- package/package.json +1 -1
|
@@ -1,1091 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { UniversalShapeFilter } from '../../../compat/actions/universal-shape-filter.js';
|
|
3
|
-
import { ResponseBlacklistSanitizer } from '../../../compat/actions/response-blacklist.js';
|
|
4
|
-
import { applyFieldMappings } from '../../../compat/actions/field-mapping.js';
|
|
5
|
-
import { sanitizeToolSchema } from '../../../compat/actions/tool-schema.js';
|
|
6
|
-
import { applyRequestRules } from '../../../compat/actions/request-rules.js';
|
|
7
|
-
import { applyAutoThinking as runAutoThinking } from '../../../compat/actions/auto-thinking.js';
|
|
8
|
-
import { normalizeResponsePayload } from '../../../compat/actions/response-normalize.js';
|
|
9
|
-
import { validateResponsePayload } from '../../../compat/actions/response-validate.js';
|
|
10
|
-
import { writeCompatSnapshot } from '../../../compat/actions/snapshot.js';
|
|
11
|
-
import { applyQwenRequestTransform, applyQwenResponseTransform } from '../../../compat/actions/qwen-transform.js';
|
|
12
|
-
const RATE_LIMIT_ERROR = 'ERR_COMPAT_RATE_LIMIT_DETECTED';
|
|
13
|
-
const INTERNAL_STATE = Symbol('compat.internal_state');
|
|
1
|
+
import { runRequestCompatPipeline, runResponseCompatPipeline } from './compat-pipeline-executor.js';
|
|
14
2
|
export function applyRequestCompat(profileId, payload, options) {
|
|
15
|
-
|
|
16
|
-
if (!profile) {
|
|
17
|
-
return { payload };
|
|
18
|
-
}
|
|
19
|
-
const stage = pickStageConfig(profile, 'request');
|
|
20
|
-
if (!stage) {
|
|
21
|
-
return { payload };
|
|
22
|
-
}
|
|
23
|
-
const mutated = structuredClone(payload);
|
|
24
|
-
const state = initializeInternalState(mutated, 'request', options?.adapterContext);
|
|
25
|
-
if (Array.isArray(stage.mappings)) {
|
|
26
|
-
for (const mapping of stage.mappings) {
|
|
27
|
-
applyMapping(mutated, mapping, state);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
payload: mutated,
|
|
32
|
-
appliedProfile: profile.id
|
|
33
|
-
};
|
|
3
|
+
return runRequestCompatPipeline(profileId, payload, options);
|
|
34
4
|
}
|
|
35
5
|
export function applyResponseCompat(profileId, payload, options) {
|
|
36
|
-
|
|
37
|
-
if (!profile) {
|
|
38
|
-
return { payload };
|
|
39
|
-
}
|
|
40
|
-
const stage = pickStageConfig(profile, 'response');
|
|
41
|
-
if (!stage) {
|
|
42
|
-
return { payload };
|
|
43
|
-
}
|
|
44
|
-
const mutated = structuredClone(payload);
|
|
45
|
-
const state = initializeInternalState(mutated, 'response', options?.adapterContext);
|
|
46
|
-
if (Array.isArray(stage.mappings)) {
|
|
47
|
-
for (const mapping of stage.mappings) {
|
|
48
|
-
applyMapping(mutated, mapping, state);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (Array.isArray(stage.filters)) {
|
|
52
|
-
for (const filter of stage.filters) {
|
|
53
|
-
applyFilter(mutated, filter);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const requestIdFallback = state.originalRequestId || state.adapterContext?.requestId;
|
|
57
|
-
if (requestIdFallback && typeof mutated.request_id !== 'string') {
|
|
58
|
-
mutated.request_id = requestIdFallback;
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
payload: mutated,
|
|
62
|
-
appliedProfile: profile.id
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
function pickStageConfig(profile, stage) {
|
|
66
|
-
if (!profile) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
if (stage === 'request' && profile.request) {
|
|
70
|
-
return profile.request;
|
|
71
|
-
}
|
|
72
|
-
if (stage === 'response' && profile.response) {
|
|
73
|
-
return profile.response;
|
|
74
|
-
}
|
|
75
|
-
if (profile.direction && profile.direction !== stage) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
if (profile.mappings || profile.filters) {
|
|
79
|
-
return {
|
|
80
|
-
mappings: profile.mappings,
|
|
81
|
-
filters: profile.filters
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
function applyMapping(root, mapping, state) {
|
|
87
|
-
switch (mapping.action) {
|
|
88
|
-
case 'remove':
|
|
89
|
-
removePath(root, mapping.path);
|
|
90
|
-
break;
|
|
91
|
-
case 'rename':
|
|
92
|
-
renamePath(root, mapping.from, mapping.to);
|
|
93
|
-
break;
|
|
94
|
-
case 'set':
|
|
95
|
-
setPath(root, mapping.path, mapping.value);
|
|
96
|
-
break;
|
|
97
|
-
case 'stringify':
|
|
98
|
-
stringifyPath(root, mapping.path, mapping.fallback);
|
|
99
|
-
break;
|
|
100
|
-
case 'parse_json':
|
|
101
|
-
parseJsonPath(root, mapping.path, mapping.fallback);
|
|
102
|
-
break;
|
|
103
|
-
case 'set_default':
|
|
104
|
-
setDefaultPath(root, mapping.path, mapping.value, mapping.valueSource);
|
|
105
|
-
break;
|
|
106
|
-
case 'normalize_tool_choice':
|
|
107
|
-
normalizeToolChoice(root, mapping);
|
|
108
|
-
break;
|
|
109
|
-
case 'inject_instruction':
|
|
110
|
-
injectInstruction(root, mapping);
|
|
111
|
-
break;
|
|
112
|
-
case 'convert_responses_output_to_choices':
|
|
113
|
-
convertResponsesOutputToChoices(root);
|
|
114
|
-
break;
|
|
115
|
-
case 'extract_glm_tool_markup':
|
|
116
|
-
extractGlmToolMarkup(root);
|
|
117
|
-
break;
|
|
118
|
-
case 'dto_unwrap':
|
|
119
|
-
dtoUnwrap(root, state);
|
|
120
|
-
break;
|
|
121
|
-
case 'dto_rewrap':
|
|
122
|
-
dtoRewrap(root, state);
|
|
123
|
-
break;
|
|
124
|
-
case 'shape_filter':
|
|
125
|
-
applyShapeFilterMapping(root, mapping, state);
|
|
126
|
-
break;
|
|
127
|
-
case 'field_map':
|
|
128
|
-
applyFieldMap(root, mapping, state);
|
|
129
|
-
break;
|
|
130
|
-
case 'tool_schema_sanitize':
|
|
131
|
-
applyToolSchemaSanitize(root, mapping);
|
|
132
|
-
break;
|
|
133
|
-
case 'apply_rules':
|
|
134
|
-
applyRules(root, mapping, state);
|
|
135
|
-
break;
|
|
136
|
-
case 'auto_thinking':
|
|
137
|
-
applyAutoThinkingAction(root, mapping, state);
|
|
138
|
-
break;
|
|
139
|
-
case 'snapshot':
|
|
140
|
-
triggerSnapshot(root, mapping, state);
|
|
141
|
-
break;
|
|
142
|
-
case 'resp_blacklist':
|
|
143
|
-
applyResponseBlacklist(root, mapping, state);
|
|
144
|
-
break;
|
|
145
|
-
case 'response_normalize':
|
|
146
|
-
applyResponseNormalize(root, mapping, state);
|
|
147
|
-
break;
|
|
148
|
-
case 'response_validate':
|
|
149
|
-
if (state.direction === 'response') {
|
|
150
|
-
validateResponsePayload(root, mapping.config);
|
|
151
|
-
}
|
|
152
|
-
break;
|
|
153
|
-
case 'qwen_request_transform':
|
|
154
|
-
replaceRoot(root, applyQwenRequestTransform(root));
|
|
155
|
-
break;
|
|
156
|
-
case 'qwen_response_transform':
|
|
157
|
-
replaceRoot(root, applyQwenResponseTransform(root));
|
|
158
|
-
break;
|
|
159
|
-
default:
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
function applyFilter(payload, filter) {
|
|
164
|
-
if (filter.action === 'rate_limit_text') {
|
|
165
|
-
if (detectRateLimitText(payload, filter.needle)) {
|
|
166
|
-
const err = new Error('Provider returned rate limit notice');
|
|
167
|
-
err.code = RATE_LIMIT_ERROR;
|
|
168
|
-
err.statusCode = 429;
|
|
169
|
-
throw err;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
function initializeInternalState(root, direction, adapterContext) {
|
|
174
|
-
const state = {
|
|
175
|
-
direction,
|
|
176
|
-
adapterContext,
|
|
177
|
-
originalRequestId: direction === 'response' ? extractRequestId(root) : undefined
|
|
178
|
-
};
|
|
179
|
-
Object.defineProperty(root, INTERNAL_STATE, {
|
|
180
|
-
value: state,
|
|
181
|
-
enumerable: false,
|
|
182
|
-
configurable: true
|
|
183
|
-
});
|
|
184
|
-
return state;
|
|
185
|
-
}
|
|
186
|
-
function replaceRoot(target, source) {
|
|
187
|
-
if (target === source) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
for (const key of Object.keys(target)) {
|
|
191
|
-
delete target[key];
|
|
192
|
-
}
|
|
193
|
-
for (const [key, value] of Object.entries(source)) {
|
|
194
|
-
target[key] = value;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function dtoUnwrap(root, state) {
|
|
198
|
-
const original = structuredClone(root);
|
|
199
|
-
if (isRecord(original.data)) {
|
|
200
|
-
state.dtoEnvelope = { original, isDto: true };
|
|
201
|
-
replaceRoot(root, original.data);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
state.dtoEnvelope = { original, isDto: false };
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
function dtoRewrap(root, state) {
|
|
208
|
-
const envelope = state.dtoEnvelope;
|
|
209
|
-
if (!envelope) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
if (!envelope.isDto) {
|
|
213
|
-
state.dtoEnvelope = undefined;
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const rebuilt = structuredClone(envelope.original);
|
|
217
|
-
rebuilt.data = structuredClone(root);
|
|
218
|
-
replaceRoot(root, rebuilt);
|
|
219
|
-
state.dtoEnvelope = undefined;
|
|
220
|
-
}
|
|
221
|
-
function applyShapeFilterMapping(root, mapping, state) {
|
|
222
|
-
const target = mapping.target ?? state.direction;
|
|
223
|
-
const filter = new UniversalShapeFilter(mapping.config);
|
|
224
|
-
const filtered = target === 'request'
|
|
225
|
-
? filter.applyRequestFilter(root)
|
|
226
|
-
: filter.applyResponseFilter(root, state.adapterContext);
|
|
227
|
-
if (filtered === root) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
replaceRoot(root, filtered);
|
|
231
|
-
}
|
|
232
|
-
function applyFieldMap(root, mapping, state) {
|
|
233
|
-
const direction = mapping.direction ?? state.direction;
|
|
234
|
-
const result = applyFieldMappings(root, mapping.config);
|
|
235
|
-
replaceRoot(root, result);
|
|
236
|
-
}
|
|
237
|
-
function applyToolSchemaSanitize(root, mapping) {
|
|
238
|
-
const sanitized = sanitizeToolSchema(root, mapping.mode ?? 'glm_shell');
|
|
239
|
-
replaceRoot(root, sanitized);
|
|
240
|
-
}
|
|
241
|
-
function applyRules(root, mapping, state) {
|
|
242
|
-
if (state.direction !== 'request') {
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const result = applyRequestRules(root, mapping.config);
|
|
246
|
-
replaceRoot(root, result);
|
|
247
|
-
}
|
|
248
|
-
function applyAutoThinkingAction(root, mapping, state) {
|
|
249
|
-
if (state.direction !== 'request') {
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
runAutoThinking(root, mapping.config);
|
|
253
|
-
}
|
|
254
|
-
function triggerSnapshot(root, mapping, state) {
|
|
255
|
-
void writeCompatSnapshot({
|
|
256
|
-
phase: mapping.phase,
|
|
257
|
-
requestId: state.adapterContext?.requestId,
|
|
258
|
-
entryEndpoint: state.adapterContext?.entryEndpoint,
|
|
259
|
-
data: structuredClone(root)
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
function applyResponseBlacklist(root, mapping, state) {
|
|
263
|
-
if (state.direction !== 'response') {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
const sanitizer = new ResponseBlacklistSanitizer(mapping.config);
|
|
267
|
-
const result = sanitizer.apply(root);
|
|
268
|
-
replaceRoot(root, result);
|
|
269
|
-
}
|
|
270
|
-
function applyResponseNormalize(root, mapping, state) {
|
|
271
|
-
if (state.direction !== 'response') {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const result = normalizeResponsePayload(root, mapping.config);
|
|
275
|
-
replaceRoot(root, result);
|
|
276
|
-
}
|
|
277
|
-
function detectRateLimitText(payload, needle) {
|
|
278
|
-
if (!needle || !payload) {
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
const normalizedNeedle = needle.toLowerCase();
|
|
282
|
-
const stack = [payload];
|
|
283
|
-
while (stack.length) {
|
|
284
|
-
const current = stack.pop();
|
|
285
|
-
if (typeof current === 'string') {
|
|
286
|
-
if (current.toLowerCase().includes(normalizedNeedle)) {
|
|
287
|
-
return true;
|
|
288
|
-
}
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
if (Array.isArray(current)) {
|
|
292
|
-
for (const entry of current) {
|
|
293
|
-
stack.push(entry);
|
|
294
|
-
}
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
if (isRecord(current)) {
|
|
298
|
-
for (const value of Object.values(current)) {
|
|
299
|
-
stack.push(value);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
function parsePath(path) {
|
|
306
|
-
if (!path || typeof path !== 'string') {
|
|
307
|
-
return [];
|
|
308
|
-
}
|
|
309
|
-
return path
|
|
310
|
-
.split('.')
|
|
311
|
-
.map((segment) => segment.trim())
|
|
312
|
-
.filter(Boolean)
|
|
313
|
-
.map((segment) => {
|
|
314
|
-
if (segment.endsWith('[*]')) {
|
|
315
|
-
return {
|
|
316
|
-
key: segment.slice(0, -3),
|
|
317
|
-
wildcard: true
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
key: segment,
|
|
322
|
-
wildcard: false
|
|
323
|
-
};
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
function removePath(root, path) {
|
|
327
|
-
const steps = parsePath(path);
|
|
328
|
-
if (!steps.length) {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
const parentSteps = steps.slice(0, -1);
|
|
332
|
-
const finalStep = steps[steps.length - 1];
|
|
333
|
-
const targets = resolveTargets(root, parentSteps);
|
|
334
|
-
for (const target of targets) {
|
|
335
|
-
if (isRecord(target) && finalStep && finalStep.key in target) {
|
|
336
|
-
delete target[finalStep.key];
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
function renamePath(root, fromPath, toPath) {
|
|
341
|
-
const value = getPathValue(root, fromPath);
|
|
342
|
-
if (value === undefined) {
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
setPath(root, toPath, value);
|
|
346
|
-
removePath(root, fromPath);
|
|
347
|
-
}
|
|
348
|
-
function setPath(root, path, value) {
|
|
349
|
-
const steps = parsePath(path);
|
|
350
|
-
if (!steps.length) {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
let cursor = root;
|
|
354
|
-
for (let i = 0; i < steps.length; i++) {
|
|
355
|
-
const step = steps[i];
|
|
356
|
-
if (step.wildcard) {
|
|
357
|
-
throw new Error(`Cannot set wildcard path: ${path}`);
|
|
358
|
-
}
|
|
359
|
-
if (i === steps.length - 1) {
|
|
360
|
-
if (isRecord(cursor)) {
|
|
361
|
-
cursor[step.key] = structuredClone(value);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
if (!isRecord(cursor[step.key])) {
|
|
366
|
-
cursor[step.key] = {};
|
|
367
|
-
}
|
|
368
|
-
cursor = cursor[step.key];
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function getPathValue(root, path) {
|
|
373
|
-
const steps = parsePath(path);
|
|
374
|
-
if (!steps.length) {
|
|
375
|
-
return undefined;
|
|
376
|
-
}
|
|
377
|
-
let cursor = root;
|
|
378
|
-
for (const step of steps) {
|
|
379
|
-
if (step.wildcard) {
|
|
380
|
-
if (!isRecord(cursor)) {
|
|
381
|
-
return undefined;
|
|
382
|
-
}
|
|
383
|
-
const arr = cursor[step.key];
|
|
384
|
-
if (!Array.isArray(arr) || !arr.length) {
|
|
385
|
-
return undefined;
|
|
386
|
-
}
|
|
387
|
-
cursor = arr[0];
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
if (!isRecord(cursor)) {
|
|
391
|
-
return undefined;
|
|
392
|
-
}
|
|
393
|
-
cursor = cursor[step.key];
|
|
394
|
-
if (cursor === undefined) {
|
|
395
|
-
return undefined;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
return cursor;
|
|
399
|
-
}
|
|
400
|
-
function stringifyPath(root, path, fallback) {
|
|
401
|
-
const steps = parsePath(path);
|
|
402
|
-
if (!steps.length) {
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
const parentSteps = steps.slice(0, -1);
|
|
406
|
-
const finalStep = steps[steps.length - 1];
|
|
407
|
-
const targets = resolveTargets(root, parentSteps);
|
|
408
|
-
for (const target of targets) {
|
|
409
|
-
if (!isRecord(target) || !finalStep) {
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
const current = target[finalStep.key];
|
|
413
|
-
if (typeof current === 'string') {
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
try {
|
|
417
|
-
if (current === undefined) {
|
|
418
|
-
target[finalStep.key] = JSON.stringify(fallback ?? {});
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
target[finalStep.key] = JSON.stringify(current);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
catch {
|
|
425
|
-
try {
|
|
426
|
-
target[finalStep.key] = JSON.stringify(fallback ?? {});
|
|
427
|
-
}
|
|
428
|
-
catch {
|
|
429
|
-
target[finalStep.key] = '{}';
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
function parseJsonPath(root, path, fallback) {
|
|
435
|
-
const steps = parsePath(path);
|
|
436
|
-
if (!steps.length) {
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const parentSteps = steps.slice(0, -1);
|
|
440
|
-
const finalStep = steps[steps.length - 1];
|
|
441
|
-
const targets = resolveTargets(root, parentSteps);
|
|
442
|
-
for (const target of targets) {
|
|
443
|
-
if (!isRecord(target) || !finalStep) {
|
|
444
|
-
continue;
|
|
445
|
-
}
|
|
446
|
-
const current = target[finalStep.key];
|
|
447
|
-
if (current === undefined || current === null) {
|
|
448
|
-
if (fallback !== undefined) {
|
|
449
|
-
target[finalStep.key] = structuredClone(fallback);
|
|
450
|
-
}
|
|
451
|
-
continue;
|
|
452
|
-
}
|
|
453
|
-
if (typeof current !== 'string') {
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
const trimmed = current.trim();
|
|
457
|
-
if (!trimmed) {
|
|
458
|
-
if (fallback !== undefined) {
|
|
459
|
-
target[finalStep.key] = structuredClone(fallback);
|
|
460
|
-
}
|
|
461
|
-
continue;
|
|
462
|
-
}
|
|
463
|
-
try {
|
|
464
|
-
target[finalStep.key] = JSON.parse(trimmed);
|
|
465
|
-
}
|
|
466
|
-
catch {
|
|
467
|
-
if (fallback !== undefined) {
|
|
468
|
-
target[finalStep.key] = structuredClone(fallback);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
function setDefaultPath(root, path, value, source) {
|
|
474
|
-
const current = getPathValue(root, path);
|
|
475
|
-
if (current !== undefined) {
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
let finalValue = value;
|
|
479
|
-
if (source === 'timestamp_seconds') {
|
|
480
|
-
finalValue = Math.floor(Date.now() / 1000);
|
|
481
|
-
}
|
|
482
|
-
else if (source === 'chat_completion_id') {
|
|
483
|
-
finalValue = `chatcmpl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
484
|
-
}
|
|
485
|
-
if (finalValue === undefined) {
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
setPath(root, path, finalValue);
|
|
489
|
-
}
|
|
490
|
-
function normalizeToolChoice(root, mapping) {
|
|
491
|
-
const path = mapping.path || 'tool_choice';
|
|
492
|
-
const current = getPathValue(root, path);
|
|
493
|
-
if (!current || typeof current !== 'object' || Array.isArray(current)) {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
const replacement = mapping.objectReplacement ?? 'required';
|
|
497
|
-
setPath(root, path, replacement);
|
|
498
|
-
}
|
|
499
|
-
function injectInstruction(root, mapping) {
|
|
500
|
-
const raw = getPathValue(root, mapping.sourcePath);
|
|
501
|
-
removePath(root, mapping.sourcePath);
|
|
502
|
-
const value = typeof raw === 'string' ? raw.trim() : '';
|
|
503
|
-
if (!value) {
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
let text = value;
|
|
507
|
-
if (mapping.stripHtml) {
|
|
508
|
-
text = stripHtml(text);
|
|
509
|
-
}
|
|
510
|
-
const maxLength = resolveMaxLength(mapping.maxLengthEnv);
|
|
511
|
-
if (maxLength && text.length > maxLength) {
|
|
512
|
-
text = text.slice(0, maxLength);
|
|
513
|
-
}
|
|
514
|
-
if (!text) {
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
const targetPath = mapping.targetPath || 'input';
|
|
518
|
-
const targetArray = ensureArray(root, targetPath);
|
|
519
|
-
const message = {
|
|
520
|
-
type: 'message',
|
|
521
|
-
role: mapping.role || 'system',
|
|
522
|
-
content: [
|
|
523
|
-
{
|
|
524
|
-
type: mapping.contentType || 'input_text',
|
|
525
|
-
text
|
|
526
|
-
}
|
|
527
|
-
]
|
|
528
|
-
};
|
|
529
|
-
targetArray.unshift(message);
|
|
530
|
-
}
|
|
531
|
-
function resolveTargets(root, steps) {
|
|
532
|
-
if (!steps.length) {
|
|
533
|
-
return [root];
|
|
534
|
-
}
|
|
535
|
-
const results = [];
|
|
536
|
-
const traverse = (node, index) => {
|
|
537
|
-
const step = steps[index];
|
|
538
|
-
if (!step || !isRecord(node)) {
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
const next = node[step.key];
|
|
542
|
-
if (step.wildcard && Array.isArray(next)) {
|
|
543
|
-
if (index === steps.length - 1) {
|
|
544
|
-
for (const child of next) {
|
|
545
|
-
if (isRecord(child)) {
|
|
546
|
-
results.push(child);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
for (const child of next) {
|
|
552
|
-
traverse(child, index + 1);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
if (!step.wildcard && isRecord(next)) {
|
|
558
|
-
if (index === steps.length - 1) {
|
|
559
|
-
results.push(next);
|
|
560
|
-
}
|
|
561
|
-
else {
|
|
562
|
-
traverse(next, index + 1);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
traverse(root, 0);
|
|
567
|
-
return results;
|
|
568
|
-
}
|
|
569
|
-
function isRecord(value) {
|
|
570
|
-
return typeof value === 'object' && value !== null;
|
|
571
|
-
}
|
|
572
|
-
function ensureArray(root, path) {
|
|
573
|
-
const steps = parsePath(path);
|
|
574
|
-
if (!steps.length) {
|
|
575
|
-
throw new Error(`Invalid array path: ${path}`);
|
|
576
|
-
}
|
|
577
|
-
let cursor = root;
|
|
578
|
-
for (let i = 0; i < steps.length; i++) {
|
|
579
|
-
const step = steps[i];
|
|
580
|
-
if (step.wildcard) {
|
|
581
|
-
throw new Error(`Array path does not support wildcards: ${path}`);
|
|
582
|
-
}
|
|
583
|
-
if (i === steps.length - 1) {
|
|
584
|
-
if (!Array.isArray(cursor[step.key])) {
|
|
585
|
-
cursor[step.key] = [];
|
|
586
|
-
}
|
|
587
|
-
if (!Array.isArray(cursor[step.key])) {
|
|
588
|
-
cursor[step.key] = [];
|
|
589
|
-
}
|
|
590
|
-
return cursor[step.key];
|
|
591
|
-
}
|
|
592
|
-
if (!isRecord(cursor[step.key])) {
|
|
593
|
-
cursor[step.key] = {};
|
|
594
|
-
}
|
|
595
|
-
cursor = cursor[step.key];
|
|
596
|
-
}
|
|
597
|
-
throw new Error(`Failed to resolve array path: ${path}`);
|
|
598
|
-
}
|
|
599
|
-
function stripHtml(value) {
|
|
600
|
-
return value.replace(/<\/?[^>]+(>|$)/g, '');
|
|
601
|
-
}
|
|
602
|
-
function resolveMaxLength(envVars) {
|
|
603
|
-
if (!envVars || !envVars.length) {
|
|
604
|
-
return undefined;
|
|
605
|
-
}
|
|
606
|
-
for (const envName of envVars) {
|
|
607
|
-
if (!envName)
|
|
608
|
-
continue;
|
|
609
|
-
const raw = process.env[envName];
|
|
610
|
-
if (!raw) {
|
|
611
|
-
continue;
|
|
612
|
-
}
|
|
613
|
-
const parsed = Number(raw);
|
|
614
|
-
if (Number.isFinite(parsed) && parsed > 0) {
|
|
615
|
-
return Math.floor(parsed);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return undefined;
|
|
619
|
-
}
|
|
620
|
-
function convertResponsesOutputToChoices(root) {
|
|
621
|
-
if (!isRecord(root)) {
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
const existingChoices = root.choices;
|
|
625
|
-
if (Array.isArray(existingChoices) && existingChoices.length > 0) {
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
const choicesFromOutput = buildChoicesFromResponsesOutput(root);
|
|
629
|
-
if (choicesFromOutput.length > 0) {
|
|
630
|
-
root.choices = choicesFromOutput;
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
const fallbackText = extractOutputText(root);
|
|
634
|
-
if (typeof fallbackText === 'string') {
|
|
635
|
-
root.choices = [
|
|
636
|
-
{
|
|
637
|
-
index: 0,
|
|
638
|
-
finish_reason: normalizeFinishReason(typeof root.status === 'string' ? root.status : 'stop'),
|
|
639
|
-
message: {
|
|
640
|
-
role: 'assistant',
|
|
641
|
-
content: fallbackText
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
];
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
function buildChoicesFromResponsesOutput(root) {
|
|
648
|
-
const outputEntries = Array.isArray(root.output) ? root.output : [];
|
|
649
|
-
const choices = [];
|
|
650
|
-
outputEntries.forEach((entry) => {
|
|
651
|
-
if (!isRecord(entry)) {
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
if ('type' in entry && typeof entry.type === 'string' && entry.type !== 'message') {
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
const choice = convertOutputEntryToChoice(entry, choices.length, root);
|
|
658
|
-
if (choice) {
|
|
659
|
-
choices.push(choice);
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
return choices;
|
|
663
|
-
}
|
|
664
|
-
function convertOutputEntryToChoice(entry, index, root) {
|
|
665
|
-
const message = buildMessageFromOutputEntry(entry, index);
|
|
666
|
-
if (!message) {
|
|
667
|
-
return null;
|
|
668
|
-
}
|
|
669
|
-
const finishReasonCandidate = (typeof entry.stop_reason === 'string' && entry.stop_reason) ||
|
|
670
|
-
(typeof entry.finish_reason === 'string' && entry.finish_reason) ||
|
|
671
|
-
(typeof entry.status === 'string' && entry.status) ||
|
|
672
|
-
(typeof root.status === 'string' && root.status) ||
|
|
673
|
-
'stop';
|
|
674
|
-
return {
|
|
675
|
-
index,
|
|
676
|
-
finish_reason: normalizeFinishReason(finishReasonCandidate),
|
|
677
|
-
message
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
function buildMessageFromOutputEntry(entry, choiceIndex) {
|
|
681
|
-
const role = normalizeRole(typeof entry.role === 'string' ? entry.role : 'assistant');
|
|
682
|
-
const contentArray = Array.isArray(entry.content) ? entry.content : [];
|
|
683
|
-
const textSegments = [];
|
|
684
|
-
const toolCalls = [];
|
|
685
|
-
contentArray.forEach((part, partIndex) => {
|
|
686
|
-
if (!isRecord(part)) {
|
|
687
|
-
const fallback = coerceText(part);
|
|
688
|
-
if (fallback) {
|
|
689
|
-
textSegments.push(fallback);
|
|
690
|
-
}
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
const type = typeof part.type === 'string'
|
|
694
|
-
? part.type
|
|
695
|
-
: typeof part.content_type === 'string'
|
|
696
|
-
? part.content_type
|
|
697
|
-
: '';
|
|
698
|
-
switch (type) {
|
|
699
|
-
case 'output_text': {
|
|
700
|
-
const txt = typeof part.text === 'string'
|
|
701
|
-
? part.text
|
|
702
|
-
: coerceText(part.text);
|
|
703
|
-
if (txt) {
|
|
704
|
-
textSegments.push(txt);
|
|
705
|
-
}
|
|
706
|
-
break;
|
|
707
|
-
}
|
|
708
|
-
case 'tool_call': {
|
|
709
|
-
const normalized = normalizeToolCall(part, choiceIndex, toolCalls.length);
|
|
710
|
-
if (normalized) {
|
|
711
|
-
toolCalls.push(normalized);
|
|
712
|
-
}
|
|
713
|
-
break;
|
|
714
|
-
}
|
|
715
|
-
case 'input_text':
|
|
716
|
-
case 'reasoning_content': {
|
|
717
|
-
const txt = typeof part.text === 'string'
|
|
718
|
-
? part.text
|
|
719
|
-
: coerceText(part.text);
|
|
720
|
-
if (txt) {
|
|
721
|
-
textSegments.push(txt);
|
|
722
|
-
}
|
|
723
|
-
break;
|
|
724
|
-
}
|
|
725
|
-
default: {
|
|
726
|
-
const fallback = coerceText(part);
|
|
727
|
-
if (fallback) {
|
|
728
|
-
textSegments.push(fallback);
|
|
729
|
-
}
|
|
730
|
-
break;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
});
|
|
734
|
-
const message = {
|
|
735
|
-
role,
|
|
736
|
-
content: textSegments.join('')
|
|
737
|
-
};
|
|
738
|
-
if (toolCalls.length) {
|
|
739
|
-
message.tool_calls = toolCalls;
|
|
740
|
-
if (typeof message.content !== 'string') {
|
|
741
|
-
message.content = '';
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
return message;
|
|
745
|
-
}
|
|
746
|
-
function normalizeToolCall(part, choiceIndex, callIndex) {
|
|
747
|
-
const payload = isRecord(part.tool_call) ? part.tool_call : part;
|
|
748
|
-
const fnPayload = isRecord(payload.function)
|
|
749
|
-
? payload.function
|
|
750
|
-
: isRecord(part.function)
|
|
751
|
-
? part.function
|
|
752
|
-
: null;
|
|
753
|
-
const name = typeof fnPayload?.name === 'string' ? fnPayload.name : undefined;
|
|
754
|
-
if (!name) {
|
|
755
|
-
return null;
|
|
756
|
-
}
|
|
757
|
-
const rawArgs = fnPayload?.arguments;
|
|
758
|
-
let argString;
|
|
759
|
-
if (typeof rawArgs === 'string') {
|
|
760
|
-
argString = rawArgs;
|
|
761
|
-
}
|
|
762
|
-
else {
|
|
763
|
-
try {
|
|
764
|
-
argString = JSON.stringify(rawArgs ?? {});
|
|
765
|
-
}
|
|
766
|
-
catch {
|
|
767
|
-
argString = '{}';
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
const idCandidate = (typeof payload.id === 'string' && payload.id) ||
|
|
771
|
-
(typeof payload.tool_call_id === 'string'
|
|
772
|
-
? payload.tool_call_id
|
|
773
|
-
: undefined) ||
|
|
774
|
-
(typeof payload.call_id === 'string'
|
|
775
|
-
? payload.call_id
|
|
776
|
-
: undefined);
|
|
777
|
-
return {
|
|
778
|
-
id: idCandidate || `call_${choiceIndex}_${callIndex}`,
|
|
779
|
-
type: 'function',
|
|
780
|
-
function: {
|
|
781
|
-
name,
|
|
782
|
-
arguments: argString
|
|
783
|
-
}
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
function extractOutputText(root) {
|
|
787
|
-
const textCandidate = root.output_text;
|
|
788
|
-
if (typeof textCandidate === 'string' && textCandidate.length > 0) {
|
|
789
|
-
return textCandidate;
|
|
790
|
-
}
|
|
791
|
-
return undefined;
|
|
792
|
-
}
|
|
793
|
-
function normalizeRole(role) {
|
|
794
|
-
const normalized = role.toLowerCase();
|
|
795
|
-
if (normalized === 'assistant' || normalized === 'system' || normalized === 'user' || normalized === 'tool') {
|
|
796
|
-
return normalized;
|
|
797
|
-
}
|
|
798
|
-
return 'assistant';
|
|
799
|
-
}
|
|
800
|
-
function coerceText(value) {
|
|
801
|
-
if (typeof value === 'string') {
|
|
802
|
-
return value;
|
|
803
|
-
}
|
|
804
|
-
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
805
|
-
return String(value);
|
|
806
|
-
}
|
|
807
|
-
if (Array.isArray(value)) {
|
|
808
|
-
return value.map((entry) => coerceText(entry)).join('');
|
|
809
|
-
}
|
|
810
|
-
if (isRecord(value)) {
|
|
811
|
-
try {
|
|
812
|
-
return JSON.stringify(value);
|
|
813
|
-
}
|
|
814
|
-
catch {
|
|
815
|
-
return '';
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
return '';
|
|
819
|
-
}
|
|
820
|
-
function normalizeFinishReason(reason) {
|
|
821
|
-
const normalized = reason.toLowerCase();
|
|
822
|
-
if (normalized.includes('tool')) {
|
|
823
|
-
return 'tool_calls';
|
|
824
|
-
}
|
|
825
|
-
if (normalized.includes('length') || normalized.includes('max_token') || normalized.includes('in_progress')) {
|
|
826
|
-
return 'length';
|
|
827
|
-
}
|
|
828
|
-
if (normalized.includes('filter')) {
|
|
829
|
-
return 'content_filter';
|
|
830
|
-
}
|
|
831
|
-
return 'stop';
|
|
832
|
-
}
|
|
833
|
-
function extractRequestId(node) {
|
|
834
|
-
if (typeof node.request_id === 'string') {
|
|
835
|
-
return node.request_id;
|
|
836
|
-
}
|
|
837
|
-
const dataNode = isRecord(node.data)
|
|
838
|
-
? node.data
|
|
839
|
-
: undefined;
|
|
840
|
-
if (dataNode && typeof dataNode.request_id === 'string') {
|
|
841
|
-
return dataNode.request_id;
|
|
842
|
-
}
|
|
843
|
-
return undefined;
|
|
844
|
-
}
|
|
845
|
-
function extractGlmToolMarkup(root) {
|
|
846
|
-
const choicesRaw = root?.choices;
|
|
847
|
-
const choices = Array.isArray(choicesRaw) ? choicesRaw : [];
|
|
848
|
-
choices.forEach((choice, index) => {
|
|
849
|
-
if (!choice || typeof choice !== 'object') {
|
|
850
|
-
return;
|
|
851
|
-
}
|
|
852
|
-
const message = choice.message;
|
|
853
|
-
if (!message || typeof message !== 'object') {
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
const msgRecord = message;
|
|
857
|
-
const contentField = msgRecord.content;
|
|
858
|
-
const reasoningField = msgRecord.reasoning_content;
|
|
859
|
-
const text = contentField !== undefined
|
|
860
|
-
? flattenContent(contentField)
|
|
861
|
-
: (typeof reasoningField === 'string' ? reasoningField : '');
|
|
862
|
-
if (!text) {
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
const extraction = extractToolCallsFromText(text, index + 1);
|
|
866
|
-
if (!extraction) {
|
|
867
|
-
return;
|
|
868
|
-
}
|
|
869
|
-
if (extraction.toolCalls.length) {
|
|
870
|
-
msgRecord.tool_calls = extraction.toolCalls;
|
|
871
|
-
if (contentField !== undefined) {
|
|
872
|
-
msgRecord.content = null;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
if (extraction.reasoningText) {
|
|
876
|
-
msgRecord.reasoning_content = extraction.reasoningText;
|
|
877
|
-
}
|
|
878
|
-
else if ('reasoning_content' in msgRecord) {
|
|
879
|
-
delete msgRecord.reasoning_content;
|
|
880
|
-
}
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
function flattenContent(content, depth = 0) {
|
|
884
|
-
if (depth > 4 || content == null) {
|
|
885
|
-
return '';
|
|
886
|
-
}
|
|
887
|
-
if (typeof content === 'string') {
|
|
888
|
-
return content;
|
|
889
|
-
}
|
|
890
|
-
if (Array.isArray(content)) {
|
|
891
|
-
return content.map((entry) => flattenContent(entry, depth + 1)).join('');
|
|
892
|
-
}
|
|
893
|
-
if (typeof content === 'object') {
|
|
894
|
-
const record = content;
|
|
895
|
-
if (typeof record.text === 'string') {
|
|
896
|
-
return record.text;
|
|
897
|
-
}
|
|
898
|
-
if (record.content !== undefined) {
|
|
899
|
-
return flattenContent(record.content, depth + 1);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
return '';
|
|
903
|
-
}
|
|
904
|
-
const GLM_CUSTOM_TAG = /<tool_call(?:\s+name="([^"]+)")?>([\s\S]*?)<\/tool_call>/gi;
|
|
905
|
-
const GLM_TAGGED_SEQUENCE = /<tool_call(?:\s+name="([^"]+)")?\s*>([\s\S]*?)(?:<\/tool_call>|(?=<tool_call)|$)/gi;
|
|
906
|
-
const GLM_TAGGED_BLOCK = /<arg_key>([\s\S]*?)<\/arg_key>\s*<arg_value>([\s\S]*?)<\/arg_value>/gi;
|
|
907
|
-
const GLM_INLINE_NAME = /^[\s\r\n]*([A-Za-z0-9_.:-]+)/;
|
|
908
|
-
const GENERIC_PATTERNS = [
|
|
909
|
-
[/```(?:tool|function|tool_call|function_call)?\s*([\s\S]*?)\s*```/gi, (match) => ({ body: match[1] ?? '' })],
|
|
910
|
-
[/\[(tool_call|function_call)(?:\s+name="([^"]+)")?\]([\s\S]*?)\[\/\1\]/gi, (match) => ({ body: match[3] ?? '', nameHint: match[2] })],
|
|
911
|
-
[/(tool_call|function_call)\s*[:=]\s*({[\s\S]+?})/gi, (match) => ({ body: match[2] ?? '' })]
|
|
912
|
-
];
|
|
913
|
-
function extractToolCallsFromText(text, choiceIndex) {
|
|
914
|
-
const matches = [];
|
|
915
|
-
const applyPattern = (pattern, factory) => {
|
|
916
|
-
pattern.lastIndex = 0;
|
|
917
|
-
let exec;
|
|
918
|
-
while ((exec = pattern.exec(text))) {
|
|
919
|
-
const payload = factory(exec);
|
|
920
|
-
if (!payload)
|
|
921
|
-
continue;
|
|
922
|
-
const parsed = parseToolCall(payload.body, payload.nameHint);
|
|
923
|
-
if (!parsed)
|
|
924
|
-
continue;
|
|
925
|
-
matches.push({
|
|
926
|
-
start: exec.index,
|
|
927
|
-
end: exec.index + exec[0].length,
|
|
928
|
-
call: parsed
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
};
|
|
932
|
-
applyPattern(GLM_CUSTOM_TAG, (match) => ({ body: match[2] ?? '', nameHint: match[1] }));
|
|
933
|
-
for (const [pattern, factory] of GENERIC_PATTERNS) {
|
|
934
|
-
applyPattern(pattern, factory);
|
|
935
|
-
}
|
|
936
|
-
applyTaggedArgPatterns(text, matches);
|
|
937
|
-
if (!matches.length && typeof text === 'string' && text.includes('<arg_key>')) {
|
|
938
|
-
GLM_INLINE_NAME.lastIndex = 0;
|
|
939
|
-
const inline = GLM_INLINE_NAME.exec(text);
|
|
940
|
-
GLM_INLINE_NAME.lastIndex = 0;
|
|
941
|
-
if (inline && inline[1]) {
|
|
942
|
-
const name = inline[1].trim();
|
|
943
|
-
const block = text.slice(inline[0].length);
|
|
944
|
-
const argsRecord = parseTaggedArgBlock(block);
|
|
945
|
-
if (name && argsRecord) {
|
|
946
|
-
matches.push({
|
|
947
|
-
start: 0,
|
|
948
|
-
end: text.length,
|
|
949
|
-
call: {
|
|
950
|
-
name,
|
|
951
|
-
args: safeStringify(argsRecord)
|
|
952
|
-
}
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
matches.sort((a, b) => a.start - b.start);
|
|
958
|
-
const toolCalls = matches.map((entry, idx) => ({
|
|
959
|
-
id: `glm_tool_${choiceIndex}_${idx + 1}`,
|
|
960
|
-
type: 'function',
|
|
961
|
-
function: {
|
|
962
|
-
name: entry.call.name,
|
|
963
|
-
arguments: entry.call.args
|
|
964
|
-
}
|
|
965
|
-
}));
|
|
966
|
-
matches.sort((a, b) => b.start - a.start);
|
|
967
|
-
let cleaned = text;
|
|
968
|
-
for (const entry of matches) {
|
|
969
|
-
cleaned = cleaned.slice(0, entry.start) + cleaned.slice(entry.end);
|
|
970
|
-
}
|
|
971
|
-
const reasoningText = cleaned.trim();
|
|
972
|
-
return {
|
|
973
|
-
toolCalls,
|
|
974
|
-
reasoningText: reasoningText.length ? reasoningText : undefined
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
function parseToolCall(body, nameHint) {
|
|
978
|
-
if (!body || typeof body !== 'string') {
|
|
979
|
-
return null;
|
|
980
|
-
}
|
|
981
|
-
const trimmed = body.trim();
|
|
982
|
-
if (!trimmed.length) {
|
|
983
|
-
return null;
|
|
984
|
-
}
|
|
985
|
-
try {
|
|
986
|
-
const parsed = JSON.parse(trimmed);
|
|
987
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
988
|
-
return null;
|
|
989
|
-
}
|
|
990
|
-
const record = parsed;
|
|
991
|
-
const candidateName = (typeof record.name === 'string' && record.name.trim().length ? record.name.trim() : undefined) ??
|
|
992
|
-
(typeof record.tool_name === 'string' && record.tool_name.trim().length ? record.tool_name.trim() : undefined) ??
|
|
993
|
-
(typeof record.tool === 'string' && record.tool.trim().length ? record.tool.trim() : undefined) ??
|
|
994
|
-
(nameHint && nameHint.trim().length ? nameHint.trim() : undefined);
|
|
995
|
-
if (!candidateName) {
|
|
996
|
-
return null;
|
|
997
|
-
}
|
|
998
|
-
const argsSource = record.arguments ??
|
|
999
|
-
record.input ??
|
|
1000
|
-
record.params ??
|
|
1001
|
-
record.parameters ??
|
|
1002
|
-
record.payload ??
|
|
1003
|
-
{};
|
|
1004
|
-
let args = '{}';
|
|
1005
|
-
if (typeof argsSource === 'string' && argsSource.trim().length) {
|
|
1006
|
-
args = argsSource.trim();
|
|
1007
|
-
}
|
|
1008
|
-
else {
|
|
1009
|
-
try {
|
|
1010
|
-
args = JSON.stringify(argsSource ?? {});
|
|
1011
|
-
}
|
|
1012
|
-
catch {
|
|
1013
|
-
args = '{}';
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
return { name: candidateName, args };
|
|
1017
|
-
}
|
|
1018
|
-
catch {
|
|
1019
|
-
return null;
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
function applyTaggedArgPatterns(text, matches) {
|
|
1023
|
-
if (!text || typeof text !== 'string') {
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
GLM_TAGGED_SEQUENCE.lastIndex = 0;
|
|
1027
|
-
let exec;
|
|
1028
|
-
while ((exec = GLM_TAGGED_SEQUENCE.exec(text))) {
|
|
1029
|
-
let name = typeof exec[1] === 'string' ? exec[1].trim() : '';
|
|
1030
|
-
let block = exec[2] ?? '';
|
|
1031
|
-
if (!name) {
|
|
1032
|
-
const inline = GLM_INLINE_NAME.exec(block);
|
|
1033
|
-
if (inline && inline[1]) {
|
|
1034
|
-
name = inline[1].trim();
|
|
1035
|
-
block = block.slice(inline[0].length);
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
if (!name) {
|
|
1039
|
-
continue;
|
|
1040
|
-
}
|
|
1041
|
-
const argsRecord = parseTaggedArgBlock(block);
|
|
1042
|
-
if (!argsRecord) {
|
|
1043
|
-
continue;
|
|
1044
|
-
}
|
|
1045
|
-
matches.push({
|
|
1046
|
-
start: exec.index,
|
|
1047
|
-
end: exec.index + exec[0].length,
|
|
1048
|
-
call: {
|
|
1049
|
-
name,
|
|
1050
|
-
args: safeStringify(argsRecord)
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
function parseTaggedArgBlock(block) {
|
|
1056
|
-
if (!block || typeof block !== 'string') {
|
|
1057
|
-
return null;
|
|
1058
|
-
}
|
|
1059
|
-
const record = {};
|
|
1060
|
-
GLM_TAGGED_BLOCK.lastIndex = 0;
|
|
1061
|
-
let exec;
|
|
1062
|
-
while ((exec = GLM_TAGGED_BLOCK.exec(block))) {
|
|
1063
|
-
const key = typeof exec[1] === 'string' ? exec[1].trim() : '';
|
|
1064
|
-
if (!key) {
|
|
1065
|
-
continue;
|
|
1066
|
-
}
|
|
1067
|
-
const rawValue = typeof exec[2] === 'string' ? exec[2].trim() : '';
|
|
1068
|
-
record[key] = coerceTaggedValue(rawValue);
|
|
1069
|
-
}
|
|
1070
|
-
return Object.keys(record).length ? record : null;
|
|
1071
|
-
}
|
|
1072
|
-
function coerceTaggedValue(raw) {
|
|
1073
|
-
if (!raw) {
|
|
1074
|
-
return '';
|
|
1075
|
-
}
|
|
1076
|
-
const trimmed = raw.trim();
|
|
1077
|
-
try {
|
|
1078
|
-
return JSON.parse(trimmed);
|
|
1079
|
-
}
|
|
1080
|
-
catch {
|
|
1081
|
-
return trimmed;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
function safeStringify(value) {
|
|
1085
|
-
try {
|
|
1086
|
-
return JSON.stringify(value ?? {});
|
|
1087
|
-
}
|
|
1088
|
-
catch {
|
|
1089
|
-
return '{}';
|
|
1090
|
-
}
|
|
6
|
+
return runResponseCompatPipeline(profileId, payload, options);
|
|
1091
7
|
}
|