@jsonstudio/llms 0.6.3379 → 0.6.3409

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 (38) hide show
  1. package/dist/conversion/compat/actions/claude-thinking-tools.d.ts +1 -14
  2. package/dist/conversion/compat/actions/claude-thinking-tools.js +3 -71
  3. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.d.ts +0 -8
  4. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +2 -57
  5. package/dist/conversion/compat/actions/normalize-tool-call-ids.d.ts +0 -9
  6. package/dist/conversion/compat/actions/normalize-tool-call-ids.js +6 -136
  7. package/dist/conversion/compat/actions/request-rules.js +2 -61
  8. package/dist/conversion/compat/actions/response-blacklist.d.ts +0 -4
  9. package/dist/conversion/compat/actions/response-blacklist.js +2 -77
  10. package/dist/conversion/compat/actions/response-normalize.js +2 -119
  11. package/dist/conversion/compat/actions/response-validate.js +2 -74
  12. package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +2 -150
  13. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +24 -1
  14. package/dist/conversion/hub/pipeline/hub-pipeline.js +91 -0
  15. package/dist/conversion/shared/reasoning-tool-parser.js +7 -8
  16. package/dist/conversion/shared/responses-response-utils.js +3 -48
  17. package/dist/conversion/shared/responses-tool-utils.js +22 -126
  18. package/dist/conversion/shared/tool-call-id-manager.js +18 -21
  19. package/dist/native/router_hotpath_napi.node +0 -0
  20. package/dist/router/virtual-router/bootstrap/routing-config.d.ts +2 -1
  21. package/dist/router/virtual-router/bootstrap/routing-config.js +47 -2
  22. package/dist/router/virtual-router/bootstrap/web-search-config.js +25 -0
  23. package/dist/router/virtual-router/bootstrap.js +21 -16
  24. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +6 -0
  25. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +171 -0
  26. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +11 -0
  27. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +5 -0
  28. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +137 -0
  29. package/dist/router/virtual-router/engine-selection/tier-load-balancing.d.ts +16 -0
  30. package/dist/router/virtual-router/engine-selection/tier-load-balancing.js +120 -0
  31. package/dist/router/virtual-router/engine-selection/tier-selection-quota-integration.d.ts +2 -0
  32. package/dist/router/virtual-router/engine-selection/tier-selection-quota-integration.js +44 -66
  33. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +53 -84
  34. package/dist/router/virtual-router/types.d.ts +39 -0
  35. package/dist/servertool/handlers/web-search.js +26 -1
  36. package/dist/servertool/server-side-tools.js +11 -2
  37. package/dist/servertool/types.d.ts +4 -0
  38. package/package.json +1 -1
@@ -1,15 +1,2 @@
1
1
  import type { JsonObject } from '../../hub/types/json.js';
2
- import type { AdapterContext } from '../../hub/types/chat-envelope.js';
3
- /**
4
- * Compat for Claude models routed via antigravity on gemini-chat.
5
- *
6
- * Anthropic requires tools[*].custom.input_schema to be valid JSON Schema draft 2020-12.
7
- * We currently send OpenAI-style parameters which may not fully conform, causing upstream
8
- * invalid_request_error on tools.N.custom.input_schema.
9
- *
10
- * For safety, when we detect the antigravity.*.claude-* path over gemini-chat,
11
- * we aggressively simplify Gemini functionDeclarations[*].parameters to a minimal
12
- * but valid object schema, letting RouteCodex govern tool semantics while keeping
13
- * Anthropic's schema validator happy.
14
- */
15
- export declare function applyClaudeThinkingToolSchemaCompat(payload: JsonObject, adapterContext?: AdapterContext): JsonObject;
2
+ export declare function applyClaudeThinkingToolSchemaCompat(payload: JsonObject): JsonObject;
@@ -1,72 +1,4 @@
1
- const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
2
- /**
3
- * Compat for Claude models routed via antigravity on gemini-chat.
4
- *
5
- * Anthropic requires tools[*].custom.input_schema to be valid JSON Schema draft 2020-12.
6
- * We currently send OpenAI-style parameters which may not fully conform, causing upstream
7
- * invalid_request_error on tools.N.custom.input_schema.
8
- *
9
- * For safety, when we detect the antigravity.*.claude-* path over gemini-chat,
10
- * we aggressively simplify Gemini functionDeclarations[*].parameters to a minimal
11
- * but valid object schema, letting RouteCodex govern tool semantics while keeping
12
- * Anthropic's schema validator happy.
13
- */
14
- export function applyClaudeThinkingToolSchemaCompat(payload, adapterContext) {
15
- const modelRaw = payload.model;
16
- const modelId = typeof modelRaw === 'string' ? modelRaw.trim() : '';
17
- // Only apply on Claude models.
18
- // Upstream Anthropic enforces strict JSON Schema 2020-12 on custom.input_schema for these models.
19
- if (!modelId.startsWith('claude-')) {
20
- return payload;
21
- }
22
- const root = structuredClone(payload);
23
- // Support both shapes:
24
- // - Provider envelope: { model, request: { tools, ... } }
25
- // - Gemini mapper request: { model, tools, ... }
26
- const requestNode = isRecord(root.request)
27
- ? root.request
28
- : root;
29
- const toolsRaw = requestNode.tools;
30
- if (!Array.isArray(toolsRaw)) {
31
- return root;
32
- }
33
- const nextTools = [];
34
- for (const entry of toolsRaw) {
35
- if (!isRecord(entry)) {
36
- nextTools.push(entry);
37
- continue;
38
- }
39
- const decls = Array.isArray(entry.functionDeclarations)
40
- ? entry.functionDeclarations
41
- : undefined;
42
- if (!decls || !decls.length) {
43
- // Non functionDeclarations-based tools (e.g. googleSearch) are left as-is.
44
- nextTools.push(entry);
45
- continue;
46
- }
47
- const nextDecls = [];
48
- for (const fn of decls) {
49
- if (!isRecord(fn)) {
50
- nextDecls.push(fn);
51
- continue;
52
- }
53
- const fnCopy = { ...fn };
54
- // Replace parameters with a minimal, always-valid object schema.
55
- fnCopy.parameters = {
56
- type: 'object',
57
- properties: {},
58
- additionalProperties: true
59
- };
60
- // Drop strict flag to avoid upstream schema incompatibilities.
61
- if (Object.prototype.hasOwnProperty.call(fnCopy, 'strict')) {
62
- delete fnCopy.strict;
63
- }
64
- nextDecls.push(fnCopy);
65
- }
66
- nextTools.push({
67
- functionDeclarations: nextDecls
68
- });
69
- }
70
- requestNode.tools = nextTools;
71
- return root;
1
+ import { applyClaudeThinkingToolSchemaCompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
2
+ export function applyClaudeThinkingToolSchemaCompat(payload) {
3
+ return applyClaudeThinkingToolSchemaCompatWithNative(payload);
72
4
  }
@@ -1,10 +1,2 @@
1
1
  import type { JsonObject } from '../../hub/types/json.js';
2
- /**
3
- * LM Studio compat:
4
- * Some LM Studio OpenAI-Responses endpoints accept tool-call items but are picky about ids:
5
- * - `call_id` should remain `call_*` (used to link tool outputs)
6
- * - `id` for `function_call` / `function_call_output` should be `fc_*` (OpenAI Responses convention)
7
- *
8
- * We apply this in compat (profile: chat:lmstudio) to keep provider layer transport-only.
9
- */
10
2
  export declare function enforceLmstudioResponsesFcToolCallIds(payload: JsonObject): JsonObject;
@@ -1,59 +1,4 @@
1
- import { normalizeFunctionCallId, normalizeFunctionCallOutputId, normalizeResponsesCallId } from '../../bridge-id-utils.js';
2
- function isRecord(value) {
3
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
4
- }
5
- function normalizeToCallId(raw, fallback) {
6
- const rawStr = typeof raw === 'string' ? raw.trim() : '';
7
- return normalizeResponsesCallId({ callId: rawStr || undefined, fallback });
8
- }
9
- /**
10
- * LM Studio compat:
11
- * Some LM Studio OpenAI-Responses endpoints accept tool-call items but are picky about ids:
12
- * - `call_id` should remain `call_*` (used to link tool outputs)
13
- * - `id` for `function_call` / `function_call_output` should be `fc_*` (OpenAI Responses convention)
14
- *
15
- * We apply this in compat (profile: chat:lmstudio) to keep provider layer transport-only.
16
- */
1
+ import { enforceLmstudioResponsesFcToolCallIdsWithNative } from '../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
17
2
  export function enforceLmstudioResponsesFcToolCallIds(payload) {
18
- try {
19
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
20
- return payload;
21
- }
22
- const root = structuredClone(payload);
23
- const input = root.input;
24
- if (!Array.isArray(input) || input.length === 0) {
25
- return root;
26
- }
27
- let callCounter = 0;
28
- for (const item of input) {
29
- if (!isRecord(item))
30
- continue;
31
- const type = typeof item.type === 'string' ? item.type.trim().toLowerCase() : '';
32
- if (type !== 'function_call' && type !== 'function_call_output') {
33
- continue;
34
- }
35
- const rawCallId = (item.call_id ?? item.tool_call_id ?? item.id);
36
- const callId = normalizeToCallId(rawCallId, `call_${++callCounter}`);
37
- item.call_id = callId;
38
- if (type === 'function_call') {
39
- const normalizedItemId = normalizeFunctionCallId({
40
- callId,
41
- fallback: `fc_${callId}`
42
- });
43
- item.id = normalizedItemId;
44
- continue;
45
- }
46
- // function_call_output
47
- const normalizedOutputId = normalizeFunctionCallOutputId({
48
- callId,
49
- fallback: typeof item.id === 'string' && item.id.trim().length ? item.id.trim() : `fc_tool_${callCounter}`
50
- });
51
- item.id = normalizedOutputId;
52
- }
53
- root.input = input;
54
- return root;
55
- }
56
- catch {
57
- return payload;
58
- }
3
+ return enforceLmstudioResponsesFcToolCallIdsWithNative(payload);
59
4
  }
@@ -1,11 +1,2 @@
1
1
  import type { JsonObject } from '../../hub/types/json.js';
2
- /**
3
- * Normalize tool-call identifiers across OpenAI-compatible payload variants.
4
- *
5
- * Goal: field-level normalization only (no rewriting/rehashing IDs):
6
- * - Mirror `call_id` ↔ `tool_call_id` when one side is missing.
7
- * - For Responses `input` / `output` function_call items, ensure `id` and `call_id` are aligned.
8
- *
9
- * This is intended to live in compatibility profiles (per-provider), not in host/provider code.
10
- */
11
2
  export declare function normalizeToolCallIdsInPlace(root: JsonObject): void;
@@ -1,140 +1,10 @@
1
- function isRecord(value) {
2
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
3
- }
4
- function ensureRecord(parent, key) {
5
- const existing = parent[key];
6
- if (isRecord(existing)) {
7
- return existing;
8
- }
9
- const next = {};
10
- parent[key] = next;
11
- return next;
12
- }
13
- function pickString(...candidates) {
14
- for (const c of candidates) {
15
- if (typeof c === 'string' && c.trim().length) {
16
- return c.trim();
17
- }
18
- }
19
- return undefined;
20
- }
21
- function normalizeResponsesInputItem(item) {
22
- const type = typeof item.type === 'string' ? item.type : '';
23
- if (type === 'function_call') {
24
- const id = pickString(item.id);
25
- const callId = pickString(item.call_id);
26
- if (callId && !id) {
27
- item.id = callId;
28
- }
29
- else if (id && !callId) {
30
- item.call_id = id;
31
- }
32
- return;
33
- }
34
- if (type === 'function_call_output' || type === 'tool_result' || type === 'tool_message') {
35
- const id = pickString(item.id);
36
- const callId = pickString(item.call_id);
37
- const toolCallId = pickString(item.tool_call_id);
38
- const resolvedCall = pickString(callId, toolCallId);
39
- if (resolvedCall && !callId) {
40
- item.call_id = resolvedCall;
41
- }
42
- if (id && !callId && !toolCallId) {
43
- // Some upstreams only provide an id for tool output items; mirror it to both fields.
44
- item.call_id = id;
45
- item.tool_call_id = id;
46
- }
47
- }
48
- }
49
- function normalizeResponsesOutputItem(item) {
50
- const type = typeof item.type === 'string' ? item.type : '';
51
- if (type === 'function_call') {
52
- const id = pickString(item.id, item.item_id);
53
- const callId = pickString(item.call_id);
54
- if (callId && !id) {
55
- item.id = callId;
56
- }
57
- else if (id && !callId) {
58
- item.call_id = id;
59
- }
60
- }
61
- }
62
- function normalizeToolOutputsArray(toolOutputs) {
63
- if (!Array.isArray(toolOutputs))
64
- return;
65
- for (const entry of toolOutputs) {
66
- if (!isRecord(entry))
67
- continue;
68
- const toolCallId = pickString(entry.tool_call_id);
69
- const callId = pickString(entry.call_id);
70
- const resolved = pickString(toolCallId, callId, entry.id);
71
- if (resolved) {
72
- entry.tool_call_id = resolved;
73
- entry.call_id = resolved;
74
- }
75
- }
76
- }
77
- function normalizeChatMessages(messages) {
78
- if (!Array.isArray(messages))
79
- return;
80
- for (const msg of messages) {
81
- if (!isRecord(msg))
82
- continue;
83
- // chat tool message uses tool_call_id; mirror to call_id for downstream consumers that only look at call_id.
84
- const toolCallId = pickString(msg.tool_call_id);
85
- const callId = pickString(msg.call_id);
86
- const resolved = pickString(toolCallId, callId);
87
- if (resolved) {
88
- msg.tool_call_id = resolved;
89
- msg.call_id = resolved;
90
- }
91
- const toolCalls = msg.tool_calls;
92
- if (Array.isArray(toolCalls)) {
93
- for (const call of toolCalls) {
94
- if (!isRecord(call))
95
- continue;
96
- const id = pickString(call.id);
97
- const callId2 = pickString(call.call_id);
98
- if (id && !callId2) {
99
- call.call_id = id;
100
- }
101
- else if (callId2 && !id) {
102
- call.id = callId2;
103
- }
104
- }
105
- }
106
- }
107
- }
108
- /**
109
- * Normalize tool-call identifiers across OpenAI-compatible payload variants.
110
- *
111
- * Goal: field-level normalization only (no rewriting/rehashing IDs):
112
- * - Mirror `call_id` ↔ `tool_call_id` when one side is missing.
113
- * - For Responses `input` / `output` function_call items, ensure `id` and `call_id` are aligned.
114
- *
115
- * This is intended to live in compatibility profiles (per-provider), not in host/provider code.
116
- */
1
+ import { normalizeToolCallIdsWithNative } from '../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
117
2
  export function normalizeToolCallIdsInPlace(root) {
118
- const record = root;
119
- // Responses request
120
- if (Array.isArray(record.input)) {
121
- for (const item of record.input) {
122
- if (isRecord(item)) {
123
- normalizeResponsesInputItem(item);
124
- }
125
- }
126
- }
127
- // Responses submit_tool_outputs request (common alias)
128
- normalizeToolOutputsArray(record.tool_outputs);
129
- normalizeToolOutputsArray(record.toolOutputs);
130
- // Responses response
131
- if (Array.isArray(record.output)) {
132
- for (const item of record.output) {
133
- if (isRecord(item)) {
134
- normalizeResponsesOutputItem(item);
135
- }
3
+ const normalized = normalizeToolCallIdsWithNative(root);
4
+ for (const key of Object.keys(root)) {
5
+ if (!(key in normalized)) {
6
+ delete root[key];
136
7
  }
137
8
  }
138
- // Chat request/response
139
- normalizeChatMessages(record.messages);
9
+ Object.assign(root, normalized);
140
10
  }
@@ -1,63 +1,4 @@
1
- const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
1
+ import { applyRequestRulesWithNative } from '../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
2
2
  export function applyRequestRules(payload, config) {
3
- if (!config) {
4
- return payload;
5
- }
6
- const cloned = { ...payload };
7
- const toolRule = config.tools?.['function'];
8
- if (toolRule?.removeKeys && Array.isArray(cloned.tools)) {
9
- for (const toolEntry of cloned.tools) {
10
- if (!isRecord(toolEntry)) {
11
- continue;
12
- }
13
- const fnNode = toolEntry.function;
14
- if (!isRecord(fnNode)) {
15
- continue;
16
- }
17
- for (const key of toolRule.removeKeys) {
18
- delete fnNode[key];
19
- }
20
- }
21
- }
22
- const assistantRule = config.messages?.assistantToolCalls?.['function'];
23
- if (Array.isArray(cloned.messages) && assistantRule?.removeKeys) {
24
- for (const message of cloned.messages) {
25
- if (!isRecord(message) || !Array.isArray(message.tool_calls)) {
26
- continue;
27
- }
28
- for (const callEntry of message.tool_calls) {
29
- if (!isRecord(callEntry)) {
30
- continue;
31
- }
32
- const fnNode = callEntry.function;
33
- if (!isRecord(fnNode)) {
34
- continue;
35
- }
36
- for (const key of assistantRule.removeKeys) {
37
- delete fnNode[key];
38
- }
39
- }
40
- }
41
- }
42
- if (Array.isArray(config.topLevel?.conditional)) {
43
- for (const rule of config.topLevel.conditional) {
44
- if (!rule)
45
- continue;
46
- if (rule.when?.tools === 'empty') {
47
- if (Array.isArray(cloned.tools) && cloned.tools.length === 0) {
48
- for (const field of rule.remove || []) {
49
- delete cloned[field];
50
- }
51
- }
52
- }
53
- if (rule.when?.tools === 'present') {
54
- if (Array.isArray(cloned.tools) && cloned.tools.length > 0) {
55
- for (const field of rule.remove || []) {
56
- delete cloned[field];
57
- }
58
- }
59
- }
60
- }
61
- }
62
- return cloned;
3
+ return applyRequestRulesWithNative(payload, (config ?? {}));
63
4
  }
@@ -5,10 +5,6 @@ export interface ResponseBlacklistConfig {
5
5
  }
6
6
  export declare class ResponseBlacklistSanitizer {
7
7
  private readonly cfg;
8
- private readonly criticalPaths;
9
8
  constructor(config: ResponseBlacklistConfig);
10
- private isCritical;
11
- private unwrapPayload;
12
- private deleteByPath;
13
9
  apply(payload: JsonObject): JsonObject;
14
10
  }
@@ -1,85 +1,10 @@
1
- function isRecord(value) {
2
- return typeof value === 'object' && value !== null && !Array.isArray(value);
3
- }
4
- function isTraversable(value) {
5
- return Array.isArray(value) || isRecord(value);
6
- }
1
+ import { applyResponseBlacklistWithNative } from '../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
7
2
  export class ResponseBlacklistSanitizer {
8
3
  cfg;
9
- criticalPaths = new Set([
10
- 'status',
11
- 'output',
12
- 'output_text',
13
- 'required_action',
14
- 'choices[].message.content',
15
- 'choices[].message.tool_calls',
16
- 'choices[].finish_reason'
17
- ]);
18
4
  constructor(config) {
19
5
  this.cfg = config;
20
6
  }
21
- isCritical(pathStr) {
22
- if (!this.cfg.keepCritical) {
23
- return false;
24
- }
25
- return this.criticalPaths.has(pathStr);
26
- }
27
- unwrapPayload(payload) {
28
- if (!isRecord(payload)) {
29
- return {};
30
- }
31
- const rootCandidate = payload.data;
32
- return isRecord(rootCandidate) ? rootCandidate : payload;
33
- }
34
- deleteByPath(obj, pathStr) {
35
- const tokens = pathStr.split('.');
36
- const recurse = (current, idx) => {
37
- if (!isTraversable(current) || idx >= tokens.length) {
38
- return;
39
- }
40
- const token = tokens[idx];
41
- const isArrayWildcard = token.endsWith('[]');
42
- const key = isArrayWildcard ? token.slice(0, -2) : token;
43
- if (idx === tokens.length - 1) {
44
- if (!isArrayWildcard && isRecord(current) && Object.prototype.hasOwnProperty.call(current, key)) {
45
- delete current[key];
46
- }
47
- return;
48
- }
49
- if (isArrayWildcard) {
50
- if (isRecord(current)) {
51
- const arr = current[key];
52
- if (Array.isArray(arr)) {
53
- for (const item of arr) {
54
- recurse(item, idx + 1);
55
- }
56
- }
57
- }
58
- }
59
- else if (isRecord(current)) {
60
- recurse(current[key], idx + 1);
61
- }
62
- };
63
- recurse(obj, 0);
64
- }
65
7
  apply(payload) {
66
- const out = isRecord(payload) ? payload : {};
67
- try {
68
- const root = this.unwrapPayload(out);
69
- const configPaths = Array.isArray(this.cfg?.paths) ? this.cfg.paths ?? [] : [];
70
- for (const p of configPaths) {
71
- if (typeof p !== 'string' || !p) {
72
- continue;
73
- }
74
- if (this.isCritical(p)) {
75
- continue;
76
- }
77
- this.deleteByPath(root, p);
78
- }
79
- return out;
80
- }
81
- catch {
82
- return out;
83
- }
8
+ return applyResponseBlacklistWithNative(payload, this.cfg);
84
9
  }
85
10
  }
@@ -1,121 +1,4 @@
1
- const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
1
+ import { normalizeResponsePayloadWithNative } from '../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
2
2
  export function normalizeResponsePayload(payload, config) {
3
- const normalized = { ...payload };
4
- if (isRecord(normalized.usage)) {
5
- normalized.usage = normalizeUsageFields(normalized.usage);
6
- }
7
- if (typeof normalized.created_at === 'number') {
8
- normalized.created = normalized.created_at;
9
- delete normalized.created_at;
10
- }
11
- if (Array.isArray(normalized.choices)) {
12
- normalized.choices = normalizeChoices(normalized.choices, config);
13
- }
14
- return normalized;
15
- }
16
- function normalizeUsageFields(usage) {
17
- const normalizedUsage = { ...usage };
18
- const fieldMappings = {
19
- input_tokens: 'prompt_tokens',
20
- output_tokens: 'completion_tokens',
21
- total_input_tokens: 'prompt_tokens',
22
- total_output_tokens: 'completion_tokens'
23
- };
24
- for (const [glmField, standardField] of Object.entries(fieldMappings)) {
25
- const value = normalizedUsage[glmField];
26
- if (typeof value === 'number') {
27
- normalizedUsage[standardField] = value;
28
- delete normalizedUsage[glmField];
29
- }
30
- }
31
- const promptTokens = typeof normalizedUsage.prompt_tokens === 'number' ? normalizedUsage.prompt_tokens : 0;
32
- const completionTokens = typeof normalizedUsage.completion_tokens === 'number'
33
- ? normalizedUsage.completion_tokens
34
- : 0;
35
- normalizedUsage.prompt_tokens = promptTokens;
36
- normalizedUsage.completion_tokens = completionTokens;
37
- if (typeof normalizedUsage.total_tokens !== 'number') {
38
- normalizedUsage.total_tokens = promptTokens + completionTokens;
39
- }
40
- return normalizedUsage;
41
- }
42
- function normalizeChoices(choices, config) {
43
- const map = config?.finishReasonMap || {};
44
- return choices.map((choice, idx) => {
45
- if (!isRecord(choice)) {
46
- return choice;
47
- }
48
- const normalizedChoice = {
49
- index: typeof choice.index === 'number' ? choice.index : idx,
50
- ...choice
51
- };
52
- if (typeof normalizedChoice.finish_reason === 'string') {
53
- normalizedChoice.finish_reason = map[normalizedChoice.finish_reason] ?? normalizedChoice.finish_reason;
54
- }
55
- if (isRecord(normalizedChoice.message)) {
56
- normalizedChoice.message = normalizeChoiceMessage(normalizedChoice.message);
57
- }
58
- return normalizedChoice;
59
- });
60
- }
61
- function normalizeChoiceMessage(message) {
62
- const normalizedMessage = { ...message };
63
- if (normalizedMessage.content !== undefined && typeof normalizedMessage.content !== 'string') {
64
- normalizedMessage.content = normalizeMessageContent(normalizedMessage.content);
65
- }
66
- if (Array.isArray(normalizedMessage.tool_calls)) {
67
- normalizedMessage.tool_calls = normalizeToolCalls(normalizedMessage.tool_calls);
68
- }
69
- return normalizedMessage;
70
- }
71
- function normalizeMessageContent(content) {
72
- if (typeof content === 'string') {
73
- return content;
74
- }
75
- if (Array.isArray(content)) {
76
- return content.map(item => (typeof item === 'string' ? item : JSON.stringify(item))).join('');
77
- }
78
- if (isRecord(content)) {
79
- return JSON.stringify(content);
80
- }
81
- return String(content ?? '');
82
- }
83
- function normalizeToolCalls(toolCalls) {
84
- return toolCalls.map(toolCall => {
85
- if (!isRecord(toolCall)) {
86
- return toolCall;
87
- }
88
- const normalizedToolCall = { ...toolCall };
89
- const func = normalizedToolCall.function;
90
- if (isRecord(func) && func.arguments !== undefined) {
91
- func.arguments = normalizeToolArguments(func.arguments);
92
- normalizedToolCall.function = func;
93
- }
94
- return normalizedToolCall;
95
- });
96
- }
97
- function normalizeToolArguments(args) {
98
- let normalized = '';
99
- if (typeof args === 'string') {
100
- normalized = args;
101
- }
102
- else {
103
- try {
104
- normalized = JSON.stringify(args ?? {});
105
- }
106
- catch {
107
- normalized = String(args ?? '');
108
- }
109
- }
110
- const trimmed = normalized.trim();
111
- if (!trimmed.length) {
112
- return '';
113
- }
114
- try {
115
- const parsed = JSON.parse(trimmed);
116
- return JSON.stringify(parsed);
117
- }
118
- catch {
119
- return trimmed;
120
- }
3
+ return normalizeResponsePayloadWithNative(payload, (config ?? {}));
121
4
  }