@jsonstudio/llms 0.6.375 → 0.6.473

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 (43) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.js +15 -1
  2. package/dist/conversion/compat/actions/iflow-web-search.d.ts +18 -0
  3. package/dist/conversion/compat/actions/iflow-web-search.js +87 -0
  4. package/dist/conversion/compat/profiles/chat-gemini.json +14 -15
  5. package/dist/conversion/compat/profiles/chat-glm.json +194 -190
  6. package/dist/conversion/compat/profiles/chat-iflow.json +199 -195
  7. package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -43
  8. package/dist/conversion/compat/profiles/chat-qwen.json +20 -20
  9. package/dist/conversion/compat/profiles/responses-c4m.json +42 -42
  10. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
  11. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +2 -0
  12. package/dist/conversion/hub/pipeline/hub-pipeline.js +5 -1
  13. package/dist/conversion/hub/pipeline/session-identifiers.d.ts +9 -0
  14. package/dist/conversion/hub/pipeline/session-identifiers.js +76 -0
  15. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +31 -2
  16. package/dist/conversion/hub/process/chat-process.js +89 -25
  17. package/dist/conversion/responses/responses-openai-bridge.js +75 -4
  18. package/dist/conversion/shared/anthropic-message-utils.js +41 -6
  19. package/dist/conversion/shared/errors.d.ts +20 -0
  20. package/dist/conversion/shared/errors.js +28 -0
  21. package/dist/conversion/shared/responses-conversation-store.js +30 -3
  22. package/dist/conversion/shared/responses-output-builder.js +68 -6
  23. package/dist/filters/special/request-toolcalls-stringify.d.ts +13 -0
  24. package/dist/filters/special/request-toolcalls-stringify.js +103 -3
  25. package/dist/filters/special/response-tool-text-canonicalize.d.ts +16 -0
  26. package/dist/filters/special/response-tool-text-canonicalize.js +27 -3
  27. package/dist/router/virtual-router/classifier.js +4 -2
  28. package/dist/router/virtual-router/engine.d.ts +30 -0
  29. package/dist/router/virtual-router/engine.js +618 -42
  30. package/dist/router/virtual-router/health-manager.d.ts +23 -0
  31. package/dist/router/virtual-router/health-manager.js +14 -0
  32. package/dist/router/virtual-router/provider-registry.d.ts +15 -0
  33. package/dist/router/virtual-router/provider-registry.js +40 -0
  34. package/dist/router/virtual-router/routing-instructions.d.ts +34 -0
  35. package/dist/router/virtual-router/routing-instructions.js +393 -0
  36. package/dist/router/virtual-router/sticky-session-store.d.ts +3 -0
  37. package/dist/router/virtual-router/sticky-session-store.js +110 -0
  38. package/dist/router/virtual-router/tool-signals.js +0 -22
  39. package/dist/router/virtual-router/types.d.ts +41 -0
  40. package/dist/servertool/engine.js +42 -1
  41. package/dist/servertool/handlers/web-search.js +157 -4
  42. package/dist/servertool/types.d.ts +6 -0
  43. package/package.json +1 -1
@@ -4,6 +4,7 @@ import { normalizeChatMessageContent } from '../shared/chat-output-normalizer.js
4
4
  import { mapBridgeToolsToChat } from '../shared/tool-mapping.js';
5
5
  import { prepareGeminiToolsForBridge } from '../shared/gemini-tool-utils.js';
6
6
  import { registerResponsesReasoning, consumeResponsesReasoning, registerResponsesOutputTextMeta, consumeResponsesOutputTextMeta, consumeResponsesPayloadSnapshot, registerResponsesPayloadSnapshot, consumeResponsesPassthrough, registerResponsesPassthrough } from '../shared/responses-reasoning-registry.js';
7
+ import { ProviderProtocolError } from '../shared/errors.js';
7
8
  const DUMMY_THOUGHT_SIGNATURE = 'skip_thought_signature_validator';
8
9
  function isObject(v) {
9
10
  return !!v && typeof v === 'object' && !Array.isArray(v);
@@ -179,6 +180,8 @@ export function buildOpenAIChatFromGeminiResponse(payload) {
179
180
  const primary = candidates[0] && typeof candidates[0] === 'object' ? candidates[0] : {};
180
181
  const content = primary?.content || {};
181
182
  const role = mapGeminiRoleToChat(content.role);
183
+ const rawFinishReason = primary?.finishReason;
184
+ const finishReasonUpper = typeof rawFinishReason === 'string' ? rawFinishReason.trim().toUpperCase() : '';
182
185
  const parts = Array.isArray(content.parts) ? content.parts : [];
183
186
  const textParts = [];
184
187
  const reasoningParts = [];
@@ -318,12 +321,23 @@ export function buildOpenAIChatFromGeminiResponse(payload) {
318
321
  }
319
322
  }
320
323
  const hasToolCalls = toolCalls.length > 0;
324
+ // 如果 Gemini 返回 UNEXPECTED_TOOL_CALL,且当前没有有效的工具调用可继续,
325
+ // 说明上游工具协议/声明与模型期望不一致,应视为 Provider 级错误而不是正常 stop,
326
+ // 由上层通过 ProviderErrorCenter / HTTP 4xx/5xx 显式反馈给客户端。
327
+ if (!hasToolCalls && finishReasonUpper === 'UNEXPECTED_TOOL_CALL') {
328
+ throw new ProviderProtocolError('Gemini returned finishReason=UNEXPECTED_TOOL_CALL; this usually indicates an incompatible or unexpected tool invocation.', {
329
+ code: 'TOOL_PROTOCOL_ERROR',
330
+ protocol: 'gemini-chat',
331
+ providerType: 'gemini',
332
+ details: { finishReason: rawFinishReason }
333
+ });
334
+ }
321
335
  const finish_reason = (() => {
322
336
  // If the model is emitting tool calls, treat this turn as a tool_calls
323
337
  // completion so downstream tool governance can continue the loop.
324
338
  if (hasToolCalls)
325
339
  return 'tool_calls';
326
- const fr = String(primary?.finishReason || '').toUpperCase();
340
+ const fr = finishReasonUpper;
327
341
  if (fr === 'MAX_TOKENS')
328
342
  return 'length';
329
343
  if (fr === 'STOP')
@@ -0,0 +1,18 @@
1
+ import type { JsonObject } from '../../hub/types/json.js';
2
+ import type { AdapterContext } from '../../hub/types/chat-envelope.js';
3
+ /**
4
+ * IFlow web_search 请求适配(作用于 openai-chat 兼容 payload):
5
+ *
6
+ * - 仅在 routeId 以 `web_search` 开头时生效(来自 AdapterContext.routeId);
7
+ * - 读取顶层的 `web_search` helper 对象 `{ query, recency, count, engine }`;
8
+ * - 当 query 为空或无效时:删除 helper,原样透传;
9
+ * - 当 query 有效时:构造一个标准的 OpenAI function tool:
10
+ * - name 固定为 `web_search`;
11
+ * - parameters 包含 query/recency/count 三个字段;
12
+ * - 将生成的 function tool 写入 `tools` 数组,并删除顶层 `web_search`。
13
+ *
14
+ * 注意:
15
+ * - 顶层 `web_search` 只在 servertool 的二跳请求中出现,用于驱动后端搜索;
16
+ * - 用户侧的工具调用仍然使用统一的 `web_search` function tool schema。
17
+ */
18
+ export declare function applyIflowWebSearchRequestTransform(payload: JsonObject, adapterContext?: AdapterContext): JsonObject;
@@ -0,0 +1,87 @@
1
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
2
+ const DEBUG_IFLOW_WEB_SEARCH = (process.env.ROUTECODEX_DEBUG_IFLOW_WEB_SEARCH || '').trim() === '1';
3
+ /**
4
+ * IFlow web_search 请求适配(作用于 openai-chat 兼容 payload):
5
+ *
6
+ * - 仅在 routeId 以 `web_search` 开头时生效(来自 AdapterContext.routeId);
7
+ * - 读取顶层的 `web_search` helper 对象 `{ query, recency, count, engine }`;
8
+ * - 当 query 为空或无效时:删除 helper,原样透传;
9
+ * - 当 query 有效时:构造一个标准的 OpenAI function tool:
10
+ * - name 固定为 `web_search`;
11
+ * - parameters 包含 query/recency/count 三个字段;
12
+ * - 将生成的 function tool 写入 `tools` 数组,并删除顶层 `web_search`。
13
+ *
14
+ * 注意:
15
+ * - 顶层 `web_search` 只在 servertool 的二跳请求中出现,用于驱动后端搜索;
16
+ * - 用户侧的工具调用仍然使用统一的 `web_search` function tool schema。
17
+ */
18
+ export function applyIflowWebSearchRequestTransform(payload, adapterContext) {
19
+ const routeId = typeof adapterContext?.routeId === 'string' ? adapterContext.routeId : '';
20
+ if (!routeId || !routeId.toLowerCase().startsWith('web_search')) {
21
+ return payload;
22
+ }
23
+ const root = structuredClone(payload);
24
+ const webSearchRaw = root.web_search;
25
+ if (!isRecord(webSearchRaw)) {
26
+ return root;
27
+ }
28
+ const webSearch = webSearchRaw;
29
+ const queryValue = webSearch.query;
30
+ const recencyValue = webSearch.recency;
31
+ const countValue = webSearch.count;
32
+ const query = typeof queryValue === 'string' ? queryValue.trim() : '';
33
+ const recency = typeof recencyValue === 'string' ? recencyValue.trim() : undefined;
34
+ let count;
35
+ if (typeof countValue === 'number' && Number.isFinite(countValue)) {
36
+ const normalized = Math.floor(countValue);
37
+ if (normalized >= 1 && normalized <= 50) {
38
+ count = normalized;
39
+ }
40
+ }
41
+ if (!query) {
42
+ // No meaningful search query, drop the helper object and passthrough.
43
+ delete root.web_search;
44
+ return root;
45
+ }
46
+ const tool = {
47
+ type: 'function',
48
+ function: {
49
+ name: 'web_search',
50
+ description: 'Perform web search over the public internet and return up-to-date results.',
51
+ parameters: {
52
+ type: 'object',
53
+ properties: {
54
+ query: {
55
+ type: 'string',
56
+ description: 'Search query string.'
57
+ },
58
+ recency: {
59
+ type: 'string',
60
+ description: 'Optional recency filter such as "day", "week", or "month".'
61
+ },
62
+ count: {
63
+ type: 'integer',
64
+ minimum: 1,
65
+ maximum: 50,
66
+ description: 'Maximum number of search results to retrieve (1-50).'
67
+ }
68
+ },
69
+ required: ['query']
70
+ }
71
+ }
72
+ };
73
+ root.tools = [tool];
74
+ delete root.web_search;
75
+ if (DEBUG_IFLOW_WEB_SEARCH) {
76
+ try {
77
+ // eslint-disable-next-line no-console
78
+ console.log('\x1b[38;5;27m[compat][iflow_web_search_request] applied web_search transform ' +
79
+ `query=${JSON.stringify(query).slice(0, 200)} ` +
80
+ `recency=${String(recency ?? '')}\x1b[0m`);
81
+ }
82
+ catch {
83
+ // logging best-effort
84
+ }
85
+ }
86
+ return root;
87
+ }
@@ -1,17 +1,16 @@
1
1
  {
2
- "id": "chat:gemini",
3
- "protocol": "gemini-chat",
4
- "request": {
5
- "mappings": [
6
- { "action": "snapshot", "phase": "compat-pre" },
7
- {
8
- "action": "gemini_web_search_request"
9
- },
10
- { "action": "snapshot", "phase": "compat-post" }
11
- ]
12
- },
13
- "response": {
14
- "mappings": []
15
- }
2
+ "id": "chat:gemini",
3
+ "protocol": "gemini-chat",
4
+ "request": {
5
+ "mappings": [
6
+ { "action": "snapshot", "phase": "compat-pre" },
7
+ {
8
+ "action": "gemini_web_search_request"
9
+ },
10
+ { "action": "snapshot", "phase": "compat-post" }
11
+ ]
12
+ },
13
+ "response": {
14
+ "mappings": []
15
+ }
16
16
  }
17
-
@@ -1,200 +1,204 @@
1
1
  {
2
- "id": "chat:glm",
3
- "protocol": "openai-chat",
4
- "request": {
5
- "mappings": [
6
- { "action": "snapshot", "phase": "compat-pre" },
7
- { "action": "dto_unwrap" },
8
- {
9
- "action": "glm_image_content"
10
- },
11
- {
12
- "action": "glm_vision_prompt"
13
- },
14
- {
15
- "action": "rename",
16
- "from": "response_format",
17
- "to": "metadata.generation.response_format"
18
- },
19
- {
20
- "action": "remove",
21
- "path": "metadata.clientModelId"
22
- },
23
- {
24
- "action": "shape_filter",
25
- "target": "request",
26
- "config": {
27
- "request": {
28
- "allowTopLevel": [
29
- "model", "messages", "stream", "thinking", "do_sample", "temperature", "top_p",
30
- "max_tokens", "tools", "tool_choice", "stop", "response_format", "web_search"
31
- ],
32
- "messages": {
33
- "allowedRoles": ["system", "user", "assistant", "tool"],
34
- "assistantWithToolCallsContentNull": true,
35
- "toolContentStringify": false
2
+ "id": "chat:glm",
3
+ "protocol": "openai-chat",
4
+ "request": {
5
+ "mappings": [
6
+ { "action": "snapshot", "phase": "compat-pre" },
7
+ { "action": "dto_unwrap" },
8
+ {
9
+ "action": "remove",
10
+ "path": "parallel_tool_calls"
36
11
  },
37
- "messagesRules": [],
38
- "tools": {
39
- "normalize": false,
40
- "forceToolChoiceAuto": true
12
+ {
13
+ "action": "glm_image_content"
41
14
  },
42
- "assistantToolCalls": { "functionArgumentsType": "string" }
43
- },
44
- "response": {
45
- "allowTopLevel": [
46
- "id", "request_id", "created", "model",
47
- "choices", "usage", "video_result", "web_search", "content_filter",
48
- "required_action", "output", "output_text", "status"
49
- ],
50
- "choices": {
51
- "required": true,
52
- "message": {
53
- "allow": ["role", "content", "reasoning_content", "audio", "tool_calls"],
54
- "roleDefault": "assistant",
55
- "contentNullWhenToolCalls": true,
56
- "tool_calls": { "function": { "nameRequired": true, "argumentsType": "string" } }
57
- },
58
- "finish_reason": ["stop", "tool_calls", "length", "sensitive", "network_error"]
15
+ {
16
+ "action": "glm_vision_prompt"
59
17
  },
60
- "usage": { "allow": ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "total_tokens"] }
61
- }
62
- }
63
- },
64
- {
65
- "action": "apply_rules",
66
- "config": {
67
- "tools": {
68
- "function": {
69
- "removeKeys": ["strict", "json_schema"]
70
- }
71
- },
72
- "messages": {
73
- "assistantToolCalls": {
74
- "function": {
75
- "removeKeys": ["strict"]
76
- }
77
- }
78
- },
79
- "topLevel": {
80
- "conditional": [
81
- { "when": { "tools": "empty" }, "remove": ["tool_choice"] }
82
- ]
83
- }
84
- }
85
- },
86
- {
87
- "action": "field_map",
88
- "direction": "incoming",
89
- "config": [
90
- { "sourcePath": "usage.prompt_tokens", "targetPath": "usage.input_tokens", "type": "number" },
91
- { "sourcePath": "usage.completion_tokens", "targetPath": "usage.output_tokens", "type": "number" },
92
- { "sourcePath": "created", "targetPath": "created_at", "type": "number" },
93
- { "sourcePath": "request_id", "targetPath": "request_id", "type": "string" },
94
- { "sourcePath": "model", "targetPath": "model", "type": "string", "transform": "normalizeModelName" },
95
- {
96
- "sourcePath": "choices[*].message.tool_calls[*].function.arguments",
97
- "targetPath": "choices[*].message.tool_calls[*].function.arguments",
98
- "type": "string"
99
- }
18
+ {
19
+ "action": "rename",
20
+ "from": "response_format",
21
+ "to": "metadata.generation.response_format"
22
+ },
23
+ {
24
+ "action": "remove",
25
+ "path": "metadata.clientModelId"
26
+ },
27
+ {
28
+ "action": "shape_filter",
29
+ "target": "request",
30
+ "config": {
31
+ "request": {
32
+ "allowTopLevel": [
33
+ "model", "messages", "stream", "thinking", "do_sample", "temperature", "top_p",
34
+ "max_tokens", "tools", "tool_choice", "stop", "response_format", "web_search"
35
+ ],
36
+ "messages": {
37
+ "allowedRoles": ["system", "user", "assistant", "tool"],
38
+ "assistantWithToolCallsContentNull": true,
39
+ "toolContentStringify": false
40
+ },
41
+ "messagesRules": [],
42
+ "tools": {
43
+ "normalize": false,
44
+ "forceToolChoiceAuto": true
45
+ },
46
+ "assistantToolCalls": { "functionArgumentsType": "string" }
47
+ },
48
+ "response": {
49
+ "allowTopLevel": [
50
+ "id", "request_id", "created", "model",
51
+ "choices", "usage", "video_result", "web_search", "content_filter",
52
+ "required_action", "output", "output_text", "status"
53
+ ],
54
+ "choices": {
55
+ "required": true,
56
+ "message": {
57
+ "allow": ["role", "content", "reasoning_content", "audio", "tool_calls"],
58
+ "roleDefault": "assistant",
59
+ "contentNullWhenToolCalls": true,
60
+ "tool_calls": { "function": { "nameRequired": true, "argumentsType": "string" } }
61
+ },
62
+ "finish_reason": ["stop", "tool_calls", "length", "sensitive", "network_error"]
63
+ },
64
+ "usage": { "allow": ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "total_tokens"] }
65
+ }
66
+ }
67
+ },
68
+ {
69
+ "action": "apply_rules",
70
+ "config": {
71
+ "tools": {
72
+ "function": {
73
+ "removeKeys": ["strict", "json_schema"]
74
+ }
75
+ },
76
+ "messages": {
77
+ "assistantToolCalls": {
78
+ "function": {
79
+ "removeKeys": ["strict"]
80
+ }
81
+ }
82
+ },
83
+ "topLevel": {
84
+ "conditional": [
85
+ { "when": { "tools": "empty" }, "remove": ["tool_choice"] }
86
+ ]
87
+ }
88
+ }
89
+ },
90
+ {
91
+ "action": "field_map",
92
+ "direction": "incoming",
93
+ "config": [
94
+ { "sourcePath": "usage.prompt_tokens", "targetPath": "usage.input_tokens", "type": "number" },
95
+ { "sourcePath": "usage.completion_tokens", "targetPath": "usage.output_tokens", "type": "number" },
96
+ { "sourcePath": "created", "targetPath": "created_at", "type": "number" },
97
+ { "sourcePath": "request_id", "targetPath": "request_id", "type": "string" },
98
+ { "sourcePath": "model", "targetPath": "model", "type": "string", "transform": "normalizeModelName" },
99
+ {
100
+ "sourcePath": "choices[*].message.tool_calls[*].function.arguments",
101
+ "targetPath": "choices[*].message.tool_calls[*].function.arguments",
102
+ "type": "string"
103
+ }
104
+ ]
105
+ },
106
+ { "action": "tool_schema_sanitize", "mode": "glm_shell" },
107
+ {
108
+ "action": "glm_web_search_request"
109
+ },
110
+ {
111
+ "action": "auto_thinking",
112
+ "config": {
113
+ "modelPrefixes": ["glm-4.7", "glm-4.6", "glm-4.5", "glm-z1"],
114
+ "excludePrefixes": ["glm-4.6v"]
115
+ }
116
+ },
117
+ { "action": "snapshot", "phase": "compat-post" },
118
+ { "action": "dto_rewrap" }
100
119
  ]
101
- },
102
- { "action": "tool_schema_sanitize", "mode": "glm_shell" },
103
- {
104
- "action": "glm_web_search_request"
105
- },
106
- {
107
- "action": "auto_thinking",
108
- "config": {
109
- "modelPrefixes": ["glm-4.7", "glm-4.6", "glm-4.5", "glm-z1"],
110
- "excludePrefixes": ["glm-4.6v"]
111
- }
112
- },
113
- { "action": "snapshot", "phase": "compat-post" },
114
- { "action": "dto_rewrap" }
115
- ]
116
- },
117
- "response": {
118
- "mappings": [
119
- { "action": "snapshot", "phase": "compat-pre" },
120
- { "action": "dto_unwrap" },
121
- {
122
- "action": "resp_blacklist",
123
- "config": {
124
- "paths": ["usage.prompt_tokens_details.cached_tokens"],
125
- "keepCritical": true
126
- }
127
- },
128
- {
129
- "action": "shape_filter",
130
- "target": "response",
131
- "config": {
132
- "request": {
133
- "allowTopLevel": [
134
- "model", "messages", "stream", "thinking", "do_sample", "temperature", "top_p",
135
- "max_tokens", "tools", "tool_choice", "stop", "response_format"
136
- ],
137
- "messages": {
138
- "allowedRoles": ["system", "user", "assistant", "tool"],
139
- "assistantWithToolCallsContentNull": true,
140
- "toolContentStringify": false
120
+ },
121
+ "response": {
122
+ "mappings": [
123
+ { "action": "snapshot", "phase": "compat-pre" },
124
+ { "action": "dto_unwrap" },
125
+ {
126
+ "action": "resp_blacklist",
127
+ "config": {
128
+ "paths": ["usage.prompt_tokens_details.cached_tokens"],
129
+ "keepCritical": true
130
+ }
141
131
  },
142
- "messagesRules": [],
143
- "tools": {
144
- "normalize": false,
145
- "forceToolChoiceAuto": true
132
+ {
133
+ "action": "shape_filter",
134
+ "target": "response",
135
+ "config": {
136
+ "request": {
137
+ "allowTopLevel": [
138
+ "model", "messages", "stream", "thinking", "do_sample", "temperature", "top_p",
139
+ "max_tokens", "tools", "tool_choice", "stop", "response_format"
140
+ ],
141
+ "messages": {
142
+ "allowedRoles": ["system", "user", "assistant", "tool"],
143
+ "assistantWithToolCallsContentNull": true,
144
+ "toolContentStringify": false
145
+ },
146
+ "messagesRules": [],
147
+ "tools": {
148
+ "normalize": false,
149
+ "forceToolChoiceAuto": true
150
+ },
151
+ "assistantToolCalls": { "functionArgumentsType": "string" }
152
+ },
153
+ "response": {
154
+ "allowTopLevel": [
155
+ "id", "request_id", "created", "model",
156
+ "choices", "usage", "video_result", "web_search", "content_filter",
157
+ "required_action", "output", "output_text", "status"
158
+ ],
159
+ "choices": {
160
+ "required": true,
161
+ "message": {
162
+ "allow": ["role", "content", "reasoning_content", "audio", "tool_calls"],
163
+ "roleDefault": "assistant",
164
+ "contentNullWhenToolCalls": true,
165
+ "tool_calls": { "function": { "nameRequired": true, "argumentsType": "string" } }
166
+ },
167
+ "finish_reason": ["stop", "tool_calls", "length", "sensitive", "network_error"]
168
+ },
169
+ "usage": { "allow": ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "total_tokens"] }
170
+ }
171
+ }
146
172
  },
147
- "assistantToolCalls": { "functionArgumentsType": "string" }
148
- },
149
- "response": {
150
- "allowTopLevel": [
151
- "id", "request_id", "created", "model",
152
- "choices", "usage", "video_result", "web_search", "content_filter",
153
- "required_action", "output", "output_text", "status"
154
- ],
155
- "choices": {
156
- "required": true,
157
- "message": {
158
- "allow": ["role", "content", "reasoning_content", "audio", "tool_calls"],
159
- "roleDefault": "assistant",
160
- "contentNullWhenToolCalls": true,
161
- "tool_calls": { "function": { "nameRequired": true, "argumentsType": "string" } }
162
- },
163
- "finish_reason": ["stop", "tool_calls", "length", "sensitive", "network_error"]
173
+ {
174
+ "action": "field_map",
175
+ "direction": "outgoing",
176
+ "config": [
177
+ { "sourcePath": "usage.input_tokens", "targetPath": "usage.prompt_tokens", "type": "number" },
178
+ { "sourcePath": "usage.output_tokens", "targetPath": "usage.completion_tokens", "type": "number" },
179
+ { "sourcePath": "usage.total_input_tokens", "targetPath": "usage.prompt_tokens", "type": "number" },
180
+ { "sourcePath": "usage.total_output_tokens", "targetPath": "usage.completion_tokens", "type": "number" },
181
+ { "sourcePath": "created_at", "targetPath": "created", "type": "number" },
182
+ { "sourcePath": "request_id", "targetPath": "request_id", "type": "string" },
183
+ {
184
+ "sourcePath": "choices[*].finish_reason",
185
+ "targetPath": "choices[*].finish_reason",
186
+ "type": "string",
187
+ "transform": "normalizeFinishReason"
188
+ },
189
+ {
190
+ "sourcePath": "choices[*].message.tool_calls[*].function.arguments",
191
+ "targetPath": "choices[*].message.tool_calls[*].function.arguments",
192
+ "type": "string"
193
+ }
194
+ ]
164
195
  },
165
- "usage": { "allow": ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "total_tokens"] }
166
- }
167
- }
168
- },
169
- {
170
- "action": "field_map",
171
- "direction": "outgoing",
172
- "config": [
173
- { "sourcePath": "usage.input_tokens", "targetPath": "usage.prompt_tokens", "type": "number" },
174
- { "sourcePath": "usage.output_tokens", "targetPath": "usage.completion_tokens", "type": "number" },
175
- { "sourcePath": "usage.total_input_tokens", "targetPath": "usage.prompt_tokens", "type": "number" },
176
- { "sourcePath": "usage.total_output_tokens", "targetPath": "usage.completion_tokens", "type": "number" },
177
- { "sourcePath": "created_at", "targetPath": "created", "type": "number" },
178
- { "sourcePath": "request_id", "targetPath": "request_id", "type": "string" },
179
- {
180
- "sourcePath": "choices[*].finish_reason",
181
- "targetPath": "choices[*].finish_reason",
182
- "type": "string",
183
- "transform": "normalizeFinishReason"
184
- },
185
- {
186
- "sourcePath": "choices[*].message.tool_calls[*].function.arguments",
187
- "targetPath": "choices[*].message.tool_calls[*].function.arguments",
188
- "type": "string"
189
- }
196
+ { "action": "tool_schema_sanitize", "mode": "glm_shell" },
197
+ { "action": "response_normalize" },
198
+ { "action": "extract_glm_tool_markup" },
199
+ { "action": "response_validate" },
200
+ { "action": "snapshot", "phase": "compat-post" },
201
+ { "action": "dto_rewrap" }
190
202
  ]
191
- },
192
- { "action": "tool_schema_sanitize", "mode": "glm_shell" },
193
- { "action": "response_normalize" },
194
- { "action": "extract_glm_tool_markup" },
195
- { "action": "response_validate" },
196
- { "action": "snapshot", "phase": "compat-post" },
197
- { "action": "dto_rewrap" }
198
- ]
199
- }
203
+ }
200
204
  }