@jsonstudio/llms 0.6.147 → 0.6.187

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.
Files changed (63) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.js +15 -1
  2. package/dist/conversion/compat/actions/auto-thinking.d.ts +6 -0
  3. package/dist/conversion/compat/actions/auto-thinking.js +25 -0
  4. package/dist/conversion/compat/actions/field-mapping.d.ts +14 -0
  5. package/dist/conversion/compat/actions/field-mapping.js +155 -0
  6. package/dist/conversion/compat/actions/qwen-transform.d.ts +3 -0
  7. package/dist/conversion/compat/actions/qwen-transform.js +209 -0
  8. package/dist/conversion/compat/actions/request-rules.d.ts +24 -0
  9. package/dist/conversion/compat/actions/request-rules.js +63 -0
  10. package/dist/conversion/compat/actions/response-blacklist.d.ts +14 -0
  11. package/dist/conversion/compat/actions/response-blacklist.js +85 -0
  12. package/dist/conversion/compat/actions/response-normalize.d.ts +5 -0
  13. package/dist/conversion/compat/actions/response-normalize.js +121 -0
  14. package/dist/conversion/compat/actions/response-validate.d.ts +5 -0
  15. package/dist/conversion/compat/actions/response-validate.js +76 -0
  16. package/dist/conversion/compat/actions/snapshot.d.ts +8 -0
  17. package/dist/conversion/compat/actions/snapshot.js +21 -0
  18. package/dist/conversion/compat/actions/tool-schema.d.ts +6 -0
  19. package/dist/conversion/compat/actions/tool-schema.js +91 -0
  20. package/dist/conversion/compat/actions/universal-shape-filter.d.ts +74 -0
  21. package/dist/conversion/compat/actions/universal-shape-filter.js +382 -0
  22. package/dist/conversion/compat/profiles/chat-glm.json +187 -13
  23. package/dist/conversion/compat/profiles/chat-iflow.json +177 -9
  24. package/dist/conversion/compat/profiles/chat-lmstudio.json +10 -2
  25. package/dist/conversion/compat/profiles/chat-qwen.json +14 -10
  26. package/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +7 -2
  27. package/dist/conversion/hub/pipeline/compat/compat-engine.js +409 -5
  28. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +47 -0
  29. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +2 -0
  30. package/dist/conversion/hub/pipeline/hub-pipeline.js +35 -1
  31. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
  32. package/dist/conversion/hub/pipeline/target-utils.js +3 -0
  33. package/dist/conversion/hub/response/response-runtime.js +23 -15
  34. package/dist/conversion/responses/responses-host-policy.d.ts +6 -0
  35. package/dist/conversion/responses/responses-host-policy.js +14 -0
  36. package/dist/conversion/responses/responses-openai-bridge.js +51 -2
  37. package/dist/conversion/shared/anthropic-message-utils.js +6 -0
  38. package/dist/conversion/shared/bridge-actions.js +1 -1
  39. package/dist/conversion/shared/bridge-policies.js +0 -1
  40. package/dist/conversion/shared/responses-conversation-store.js +3 -26
  41. package/dist/conversion/shared/responses-reasoning-registry.d.ts +4 -0
  42. package/dist/conversion/shared/responses-reasoning-registry.js +62 -1
  43. package/dist/conversion/shared/responses-response-utils.js +23 -1
  44. package/dist/conversion/shared/tool-canonicalizer.d.ts +2 -0
  45. package/dist/conversion/shared/tool-filter-pipeline.js +11 -0
  46. package/dist/router/virtual-router/bootstrap.js +218 -39
  47. package/dist/router/virtual-router/classifier.js +19 -51
  48. package/dist/router/virtual-router/context-advisor.d.ts +21 -0
  49. package/dist/router/virtual-router/context-advisor.js +76 -0
  50. package/dist/router/virtual-router/engine.d.ts +11 -27
  51. package/dist/router/virtual-router/engine.js +191 -396
  52. package/dist/router/virtual-router/features.js +24 -607
  53. package/dist/router/virtual-router/health-manager.js +2 -7
  54. package/dist/router/virtual-router/message-utils.d.ts +7 -0
  55. package/dist/router/virtual-router/message-utils.js +66 -0
  56. package/dist/router/virtual-router/provider-registry.js +6 -2
  57. package/dist/router/virtual-router/token-estimator.d.ts +2 -0
  58. package/dist/router/virtual-router/token-estimator.js +16 -0
  59. package/dist/router/virtual-router/tool-signals.d.ts +13 -0
  60. package/dist/router/virtual-router/tool-signals.js +403 -0
  61. package/dist/router/virtual-router/types.d.ts +21 -7
  62. package/dist/router/virtual-router/types.js +1 -0
  63. package/package.json +2 -2
@@ -0,0 +1,382 @@
1
+ const DEFAULT_TOOL_OUTPUT = 'Command succeeded (no output).';
2
+ function isRecord(value) {
3
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
4
+ }
5
+ function toRecord(value) {
6
+ return isRecord(value) ? value : {};
7
+ }
8
+ function toArray(value) {
9
+ return Array.isArray(value) ? value : [];
10
+ }
11
+ function hasArrayItems(value) {
12
+ return Array.isArray(value) && value.length > 0;
13
+ }
14
+ export class UniversalShapeFilter {
15
+ cfg;
16
+ constructor(config) {
17
+ this.cfg = config;
18
+ }
19
+ applyRequestFilter(payload) {
20
+ const cfg = this.cfg;
21
+ const allow = new Set(cfg.request.allowTopLevel);
22
+ const src = toRecord(payload);
23
+ const out = {};
24
+ for (const key of Object.keys(src)) {
25
+ if (allow.has(key)) {
26
+ out[key] = src[key];
27
+ }
28
+ }
29
+ const normalizedMessages = this.normalizeRequestMessages(out.messages, cfg.request);
30
+ out.messages = normalizedMessages;
31
+ this.normalizeTools(out, cfg.request);
32
+ this.cleanupToolChoice(out);
33
+ return out;
34
+ }
35
+ applyResponseFilter(payload, ctx) {
36
+ const envFlag = String(process.env.RCC_COMPAT_FILTER_OFF_RESPONSES || '1').toLowerCase();
37
+ const envBypass = !(envFlag === '0' || envFlag === 'false' || envFlag === 'off');
38
+ const entryEndpoint = ctx?.entryEndpoint ?? ctx?.endpoint;
39
+ const entry = typeof entryEndpoint === 'string' ? entryEndpoint.toLowerCase() : '';
40
+ if (entry === '/v1/responses' || envBypass) {
41
+ return payload;
42
+ }
43
+ const cfg = this.cfg;
44
+ const src = toRecord(payload);
45
+ const out = this.shallowPick(src, cfg.response.allowTopLevel);
46
+ const choices = Array.isArray(src.choices) ? src.choices : [];
47
+ out.choices = choices.map((choice, idx) => this.normalizeResponseChoice(choice, idx, cfg.response));
48
+ if (src.usage && typeof src.usage === 'object') {
49
+ out.usage = this.shallowPick(src.usage, cfg.response.usage?.allow || []);
50
+ }
51
+ return out;
52
+ }
53
+ shallowPick(obj, allow) {
54
+ if (!isRecord(obj)) {
55
+ return {};
56
+ }
57
+ const out = {};
58
+ for (const key of allow) {
59
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
60
+ out[key] = obj[key];
61
+ }
62
+ }
63
+ return out;
64
+ }
65
+ toObjectArgs(value) {
66
+ if (value === null || value === undefined) {
67
+ return {};
68
+ }
69
+ if (isRecord(value)) {
70
+ return value;
71
+ }
72
+ if (typeof value === 'string') {
73
+ try {
74
+ return JSON.parse(value);
75
+ }
76
+ catch {
77
+ return { raw: value };
78
+ }
79
+ }
80
+ return {};
81
+ }
82
+ toStringArgs(value) {
83
+ if (typeof value === 'string') {
84
+ return value;
85
+ }
86
+ try {
87
+ return JSON.stringify(value ?? {});
88
+ }
89
+ catch {
90
+ return '{}';
91
+ }
92
+ }
93
+ normalizeToolContent(value) {
94
+ if (typeof value === 'string') {
95
+ return value.trim().length ? value : DEFAULT_TOOL_OUTPUT;
96
+ }
97
+ if (value === null || value === undefined) {
98
+ return DEFAULT_TOOL_OUTPUT;
99
+ }
100
+ try {
101
+ const text = JSON.stringify(value);
102
+ return text && text.length ? text : DEFAULT_TOOL_OUTPUT;
103
+ }
104
+ catch {
105
+ return DEFAULT_TOOL_OUTPUT;
106
+ }
107
+ }
108
+ normalizeRequestMessages(messages, requestCfg) {
109
+ const entries = toArray(messages);
110
+ const normalized = entries.map(entry => this.normalizeSingleMessage(entry, requestCfg));
111
+ const withRules = this.applyMessageRules(normalized, requestCfg);
112
+ this.pairToolResults(withRules);
113
+ return withRules;
114
+ }
115
+ normalizeSingleMessage(message, requestCfg) {
116
+ const msg = toRecord(message);
117
+ const allowedRoles = requestCfg.messages.allowedRoles;
118
+ const requestedRole = typeof msg.role === 'string' ? msg.role : undefined;
119
+ const role = (requestedRole && allowedRoles.includes(requestedRole)) ? requestedRole : 'user';
120
+ const normalized = { role };
121
+ if (role === 'tool') {
122
+ normalized.content = this.normalizeToolContent(msg.content);
123
+ if (typeof msg.name === 'string') {
124
+ normalized.name = msg.name;
125
+ }
126
+ if (typeof msg.tool_call_id === 'string') {
127
+ normalized.tool_call_id = msg.tool_call_id;
128
+ }
129
+ }
130
+ else {
131
+ normalized.content = (msg.content !== null && msg.content !== undefined) ? String(msg.content) : '';
132
+ }
133
+ if (role === 'assistant' && hasArrayItems(msg.tool_calls)) {
134
+ normalized.tool_calls = this.normalizeAssistantToolCalls(msg.tool_calls, requestCfg);
135
+ if (requestCfg.messages.assistantWithToolCallsContentNull) {
136
+ normalized.content = null;
137
+ }
138
+ }
139
+ return normalized;
140
+ }
141
+ normalizeAssistantToolCalls(toolCalls, requestCfg) {
142
+ const entries = toArray(toolCalls);
143
+ return entries.map(call => {
144
+ const tc = toRecord(call);
145
+ const fn = toRecord(tc.function);
146
+ const name = typeof fn.name === 'string' ? fn.name : undefined;
147
+ const argsValue = requestCfg.assistantToolCalls?.functionArgumentsType === 'string'
148
+ ? this.toStringArgs(fn.arguments)
149
+ : this.toObjectArgs(fn.arguments);
150
+ const normalized = {
151
+ type: typeof tc.type === 'string' ? tc.type : 'function',
152
+ function: { ...(name ? { name } : {}), arguments: argsValue }
153
+ };
154
+ if (typeof tc.id === 'string') {
155
+ normalized.id = tc.id;
156
+ }
157
+ return normalized;
158
+ });
159
+ }
160
+ applyMessageRules(messages, requestCfg) {
161
+ const rules = Array.isArray(requestCfg.messagesRules) ? requestCfg.messagesRules : [];
162
+ if (!rules.length) {
163
+ if (requestCfg.messages.suppressAssistantToolCalls) {
164
+ return messages.filter(msg => !(msg.role === 'assistant' && hasArrayItems(msg.tool_calls)));
165
+ }
166
+ return messages;
167
+ }
168
+ const result = [];
169
+ for (const message of messages) {
170
+ let dropped = false;
171
+ for (const rule of rules) {
172
+ const when = rule.when || {};
173
+ const matchRole = when.role ? message.role === when.role : true;
174
+ const hasTools = hasArrayItems(message.tool_calls);
175
+ const matchTools = typeof when.hasToolCalls === 'boolean' ? hasTools === when.hasToolCalls : true;
176
+ if (matchRole && matchTools) {
177
+ if (rule.action === 'drop') {
178
+ dropped = true;
179
+ break;
180
+ }
181
+ if (rule.action === 'set' && rule.set && typeof rule.set === 'object') {
182
+ Object.assign(message, rule.set);
183
+ }
184
+ }
185
+ }
186
+ if (!dropped) {
187
+ result.push(message);
188
+ }
189
+ }
190
+ return result;
191
+ }
192
+ pairToolResults(messages) {
193
+ const nameById = new Map();
194
+ for (const message of messages) {
195
+ if (message.role === 'assistant' && hasArrayItems(message.tool_calls)) {
196
+ for (const call of message.tool_calls) {
197
+ const toolCall = toRecord(call);
198
+ if (typeof toolCall.id !== 'string') {
199
+ continue;
200
+ }
201
+ const fn = toRecord(toolCall.function);
202
+ if (typeof fn.name === 'string') {
203
+ nameById.set(toolCall.id, fn.name);
204
+ }
205
+ }
206
+ }
207
+ }
208
+ for (const message of messages) {
209
+ if (message.role !== 'tool') {
210
+ continue;
211
+ }
212
+ const name = typeof message.name === 'string' ? message.name.trim() : '';
213
+ if (name) {
214
+ continue;
215
+ }
216
+ const toolCallId = typeof message.tool_call_id === 'string' ? message.tool_call_id : undefined;
217
+ if (toolCallId && nameById.has(toolCallId)) {
218
+ message.name = nameById.get(toolCallId);
219
+ }
220
+ }
221
+ }
222
+ normalizeTools(container, requestCfg) {
223
+ if (!Array.isArray(container.tools)) {
224
+ return;
225
+ }
226
+ if (!requestCfg.tools?.normalize) {
227
+ if (requestCfg.tools?.forceToolChoiceAuto) {
228
+ container.tool_choice = 'auto';
229
+ }
230
+ return;
231
+ }
232
+ const normalized = [];
233
+ for (const toolEntry of container.tools) {
234
+ const normalizedTool = this.normalizeSingleTool(toolEntry);
235
+ normalized.push(normalizedTool);
236
+ }
237
+ container.tools = normalized;
238
+ if (requestCfg.tools?.forceToolChoiceAuto) {
239
+ container.tool_choice = 'auto';
240
+ }
241
+ }
242
+ normalizeSingleTool(toolEntry) {
243
+ const tool = toRecord(toolEntry);
244
+ const fnTop = {
245
+ name: typeof tool.name === 'string' ? tool.name : undefined,
246
+ description: typeof tool.description === 'string' ? tool.description : undefined,
247
+ parameters: tool.parameters
248
+ };
249
+ const fn = toRecord(tool.function);
250
+ const name = typeof fn.name === 'string' ? fn.name : fnTop.name;
251
+ const description = typeof fn.description === 'string' ? fn.description : fnTop.description;
252
+ let parameters = fn.parameters !== undefined ? fn.parameters : fnTop.parameters;
253
+ parameters = this.normalizeToolParameters(parameters, name);
254
+ const normalizedFn = {
255
+ ...(name ? { name } : {}),
256
+ ...(description ? { description } : {})
257
+ };
258
+ if (parameters !== undefined) {
259
+ normalizedFn.parameters = parameters;
260
+ }
261
+ return {
262
+ type: 'function',
263
+ function: normalizedFn
264
+ };
265
+ }
266
+ normalizeToolParameters(input, name) {
267
+ if (input === null || input === undefined) {
268
+ return undefined;
269
+ }
270
+ let params;
271
+ if (typeof input === 'string') {
272
+ try {
273
+ params = JSON.parse(input);
274
+ }
275
+ catch {
276
+ params = undefined;
277
+ }
278
+ }
279
+ else if (isRecord(input)) {
280
+ params = input;
281
+ }
282
+ if (!params) {
283
+ return undefined;
284
+ }
285
+ if (name && name.trim().toLowerCase() === 'shell') {
286
+ return this.enforceShellSchema(params);
287
+ }
288
+ return params;
289
+ }
290
+ enforceShellSchema(schema) {
291
+ const next = { ...schema };
292
+ if (typeof next.type !== 'string') {
293
+ next.type = 'object';
294
+ }
295
+ if (!isRecord(next.properties)) {
296
+ next.properties = {};
297
+ }
298
+ const props = next.properties;
299
+ const command = toRecord(props.command);
300
+ const hasOneOf = Array.isArray(command.oneOf);
301
+ if (!hasOneOf) {
302
+ const descText = typeof command.description === 'string'
303
+ ? command.description
304
+ : 'Shell command. Prefer a single string; an array of argv tokens is also accepted.';
305
+ props.command = {
306
+ description: descText,
307
+ oneOf: [
308
+ { type: 'string' },
309
+ { type: 'array', items: { type: 'string' } }
310
+ ]
311
+ };
312
+ const requiredList = Array.isArray(next.required)
313
+ ? next.required.filter((item) => typeof item === 'string')
314
+ : [];
315
+ if (!requiredList.includes('command')) {
316
+ requiredList.push('command');
317
+ }
318
+ next.required = requiredList;
319
+ if (typeof next.additionalProperties !== 'boolean') {
320
+ next.additionalProperties = false;
321
+ }
322
+ }
323
+ return next;
324
+ }
325
+ cleanupToolChoice(container) {
326
+ const toolsArray = Array.isArray(container.tools) ? container.tools : [];
327
+ if (!toolsArray.length && Object.prototype.hasOwnProperty.call(container, 'tool_choice')) {
328
+ delete container.tool_choice;
329
+ }
330
+ }
331
+ normalizeResponseChoice(choice, idx, responseCfg) {
332
+ const choiceRecord = toRecord(choice);
333
+ const normalized = {
334
+ index: typeof choiceRecord.index === 'number' ? choiceRecord.index : idx
335
+ };
336
+ const message = toRecord(choiceRecord.message);
337
+ normalized.message = this.normalizeResponseMessage(message, responseCfg);
338
+ const hasToolCalls = hasArrayItems(normalized.message.tool_calls);
339
+ normalized.finish_reason = choiceRecord.finish_reason ?? (hasToolCalls ? 'tool_calls' : null);
340
+ return normalized;
341
+ }
342
+ normalizeResponseMessage(message, responseCfg) {
343
+ const normalized = {};
344
+ normalized.role = typeof message.role === 'string'
345
+ ? message.role
346
+ : (responseCfg.choices.message.roleDefault || 'assistant');
347
+ if (hasArrayItems(message.tool_calls)) {
348
+ normalized.tool_calls = this.normalizeResponseToolCalls(message.tool_calls, responseCfg);
349
+ normalized.content = responseCfg.choices.message.contentNullWhenToolCalls ? null : message.content ?? '';
350
+ }
351
+ else {
352
+ normalized.content = message.content ?? '';
353
+ }
354
+ if (typeof message.reasoning_content === 'string') {
355
+ normalized.reasoning_content = message.reasoning_content;
356
+ }
357
+ if (message.audio) {
358
+ normalized.audio = message.audio;
359
+ }
360
+ return normalized;
361
+ }
362
+ normalizeResponseToolCalls(toolCalls, responseCfg) {
363
+ const entries = toArray(toolCalls);
364
+ return entries.map(call => {
365
+ const tc = toRecord(call);
366
+ const fn = toRecord(tc.function);
367
+ const name = typeof fn.name === 'string' ? fn.name : undefined;
368
+ const argsObject = this.toObjectArgs(fn.arguments);
369
+ const argsValue = responseCfg.choices.message.tool_calls?.function?.argumentsType === 'string'
370
+ ? this.toStringArgs(argsObject)
371
+ : argsObject;
372
+ const normalized = {
373
+ type: typeof tc.type === 'string' ? tc.type : 'function',
374
+ function: { ...(name ? { name } : {}), arguments: argsValue }
375
+ };
376
+ if (typeof tc.id === 'string') {
377
+ normalized.id = tc.id;
378
+ }
379
+ return normalized;
380
+ });
381
+ }
382
+ }
@@ -1,17 +1,191 @@
1
1
  {
2
2
  "id": "chat:glm",
3
3
  "protocol": "openai-chat",
4
- "direction": "request",
5
- "mappings": [
6
- {
7
- "action": "rename",
8
- "from": "response_format",
9
- "to": "metadata.generation.response_format"
10
- },
11
- {
12
- "action": "remove",
13
- "path": "metadata.clientModelId"
14
- }
15
- ],
16
- "filters": []
4
+ "request": {
5
+ "mappings": [
6
+ { "action": "snapshot", "phase": "compat-pre" },
7
+ { "action": "dto_unwrap" },
8
+ {
9
+ "action": "rename",
10
+ "from": "response_format",
11
+ "to": "metadata.generation.response_format"
12
+ },
13
+ {
14
+ "action": "remove",
15
+ "path": "metadata.clientModelId"
16
+ },
17
+ {
18
+ "action": "shape_filter",
19
+ "target": "request",
20
+ "config": {
21
+ "request": {
22
+ "allowTopLevel": [
23
+ "model", "messages", "stream", "thinking", "do_sample", "temperature", "top_p",
24
+ "max_tokens", "tools", "tool_choice", "stop", "response_format"
25
+ ],
26
+ "messages": {
27
+ "allowedRoles": ["system", "user", "assistant", "tool"],
28
+ "assistantWithToolCallsContentNull": true,
29
+ "toolContentStringify": false
30
+ },
31
+ "messagesRules": [],
32
+ "tools": {
33
+ "normalize": false,
34
+ "forceToolChoiceAuto": true
35
+ },
36
+ "assistantToolCalls": { "functionArgumentsType": "string" }
37
+ },
38
+ "response": {
39
+ "allowTopLevel": [
40
+ "id", "request_id", "created", "model",
41
+ "choices", "usage", "video_result", "web_search", "content_filter",
42
+ "required_action", "output", "output_text", "status"
43
+ ],
44
+ "choices": {
45
+ "required": true,
46
+ "message": {
47
+ "allow": ["role", "content", "reasoning_content", "audio", "tool_calls"],
48
+ "roleDefault": "assistant",
49
+ "contentNullWhenToolCalls": true,
50
+ "tool_calls": { "function": { "nameRequired": true, "argumentsType": "string" } }
51
+ },
52
+ "finish_reason": ["stop", "tool_calls", "length", "sensitive", "network_error"]
53
+ },
54
+ "usage": { "allow": ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "total_tokens"] }
55
+ }
56
+ }
57
+ },
58
+ {
59
+ "action": "apply_rules",
60
+ "config": {
61
+ "tools": {
62
+ "function": {
63
+ "removeKeys": ["strict", "json_schema"]
64
+ }
65
+ },
66
+ "messages": {
67
+ "assistantToolCalls": {
68
+ "function": {
69
+ "removeKeys": ["strict"]
70
+ }
71
+ }
72
+ },
73
+ "topLevel": {
74
+ "conditional": [
75
+ { "when": { "tools": "empty" }, "remove": ["tool_choice"] }
76
+ ]
77
+ }
78
+ }
79
+ },
80
+ {
81
+ "action": "field_map",
82
+ "direction": "incoming",
83
+ "config": [
84
+ { "sourcePath": "usage.prompt_tokens", "targetPath": "usage.input_tokens", "type": "number" },
85
+ { "sourcePath": "usage.completion_tokens", "targetPath": "usage.output_tokens", "type": "number" },
86
+ { "sourcePath": "created", "targetPath": "created_at", "type": "number" },
87
+ { "sourcePath": "request_id", "targetPath": "request_id", "type": "string" },
88
+ { "sourcePath": "model", "targetPath": "model", "type": "string", "transform": "normalizeModelName" },
89
+ {
90
+ "sourcePath": "choices[*].message.tool_calls[*].function.arguments",
91
+ "targetPath": "choices[*].message.tool_calls[*].function.arguments",
92
+ "type": "string"
93
+ }
94
+ ]
95
+ },
96
+ { "action": "tool_schema_sanitize", "mode": "glm_shell" },
97
+ {
98
+ "action": "auto_thinking",
99
+ "config": {
100
+ "modelPrefixes": ["glm-4.7", "glm-4.6", "glm-4.5", "glm-z1"],
101
+ "excludePrefixes": ["glm-4.6v"]
102
+ }
103
+ },
104
+ { "action": "snapshot", "phase": "compat-post" },
105
+ { "action": "dto_rewrap" }
106
+ ]
107
+ },
108
+ "response": {
109
+ "mappings": [
110
+ { "action": "snapshot", "phase": "compat-pre" },
111
+ { "action": "dto_unwrap" },
112
+ {
113
+ "action": "resp_blacklist",
114
+ "config": {
115
+ "paths": ["usage.prompt_tokens_details.cached_tokens"],
116
+ "keepCritical": true
117
+ }
118
+ },
119
+ {
120
+ "action": "shape_filter",
121
+ "target": "response",
122
+ "config": {
123
+ "request": {
124
+ "allowTopLevel": [
125
+ "model", "messages", "stream", "thinking", "do_sample", "temperature", "top_p",
126
+ "max_tokens", "tools", "tool_choice", "stop", "response_format"
127
+ ],
128
+ "messages": {
129
+ "allowedRoles": ["system", "user", "assistant", "tool"],
130
+ "assistantWithToolCallsContentNull": true,
131
+ "toolContentStringify": false
132
+ },
133
+ "messagesRules": [],
134
+ "tools": {
135
+ "normalize": false,
136
+ "forceToolChoiceAuto": true
137
+ },
138
+ "assistantToolCalls": { "functionArgumentsType": "string" }
139
+ },
140
+ "response": {
141
+ "allowTopLevel": [
142
+ "id", "request_id", "created", "model",
143
+ "choices", "usage", "video_result", "web_search", "content_filter",
144
+ "required_action", "output", "output_text", "status"
145
+ ],
146
+ "choices": {
147
+ "required": true,
148
+ "message": {
149
+ "allow": ["role", "content", "reasoning_content", "audio", "tool_calls"],
150
+ "roleDefault": "assistant",
151
+ "contentNullWhenToolCalls": true,
152
+ "tool_calls": { "function": { "nameRequired": true, "argumentsType": "string" } }
153
+ },
154
+ "finish_reason": ["stop", "tool_calls", "length", "sensitive", "network_error"]
155
+ },
156
+ "usage": { "allow": ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "total_tokens"] }
157
+ }
158
+ }
159
+ },
160
+ {
161
+ "action": "field_map",
162
+ "direction": "outgoing",
163
+ "config": [
164
+ { "sourcePath": "usage.input_tokens", "targetPath": "usage.prompt_tokens", "type": "number" },
165
+ { "sourcePath": "usage.output_tokens", "targetPath": "usage.completion_tokens", "type": "number" },
166
+ { "sourcePath": "usage.total_input_tokens", "targetPath": "usage.prompt_tokens", "type": "number" },
167
+ { "sourcePath": "usage.total_output_tokens", "targetPath": "usage.completion_tokens", "type": "number" },
168
+ { "sourcePath": "created_at", "targetPath": "created", "type": "number" },
169
+ { "sourcePath": "request_id", "targetPath": "request_id", "type": "string" },
170
+ {
171
+ "sourcePath": "choices[*].finish_reason",
172
+ "targetPath": "choices[*].finish_reason",
173
+ "type": "string",
174
+ "transform": "normalizeFinishReason"
175
+ },
176
+ {
177
+ "sourcePath": "choices[*].message.tool_calls[*].function.arguments",
178
+ "targetPath": "choices[*].message.tool_calls[*].function.arguments",
179
+ "type": "string"
180
+ }
181
+ ]
182
+ },
183
+ { "action": "tool_schema_sanitize", "mode": "glm_shell" },
184
+ { "action": "response_normalize" },
185
+ { "action": "extract_glm_tool_markup" },
186
+ { "action": "response_validate" },
187
+ { "action": "snapshot", "phase": "compat-post" },
188
+ { "action": "dto_rewrap" }
189
+ ]
190
+ }
17
191
  }