@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.
- package/dist/conversion/compat/actions/claude-thinking-tools.d.ts +1 -14
- package/dist/conversion/compat/actions/claude-thinking-tools.js +3 -71
- package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.d.ts +0 -8
- package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +2 -57
- package/dist/conversion/compat/actions/normalize-tool-call-ids.d.ts +0 -9
- package/dist/conversion/compat/actions/normalize-tool-call-ids.js +6 -136
- package/dist/conversion/compat/actions/request-rules.js +2 -61
- package/dist/conversion/compat/actions/response-blacklist.d.ts +0 -4
- package/dist/conversion/compat/actions/response-blacklist.js +2 -77
- package/dist/conversion/compat/actions/response-normalize.js +2 -119
- package/dist/conversion/compat/actions/response-validate.js +2 -74
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +2 -150
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +24 -1
- package/dist/conversion/hub/pipeline/hub-pipeline.js +91 -0
- package/dist/conversion/shared/reasoning-tool-parser.js +7 -8
- package/dist/conversion/shared/responses-response-utils.js +3 -48
- package/dist/conversion/shared/responses-tool-utils.js +22 -126
- package/dist/conversion/shared/tool-call-id-manager.js +18 -21
- package/dist/native/router_hotpath_napi.node +0 -0
- package/dist/router/virtual-router/bootstrap/routing-config.d.ts +2 -1
- package/dist/router/virtual-router/bootstrap/routing-config.js +47 -2
- package/dist/router/virtual-router/bootstrap/web-search-config.js +25 -0
- package/dist/router/virtual-router/bootstrap.js +21 -16
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +6 -0
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +171 -0
- package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +11 -0
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +5 -0
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +137 -0
- package/dist/router/virtual-router/engine-selection/tier-load-balancing.d.ts +16 -0
- package/dist/router/virtual-router/engine-selection/tier-load-balancing.js +120 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-quota-integration.d.ts +2 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-quota-integration.js +44 -66
- package/dist/router/virtual-router/engine-selection/tier-selection-select.js +53 -84
- package/dist/router/virtual-router/types.d.ts +39 -0
- package/dist/servertool/handlers/web-search.js +26 -1
- package/dist/servertool/server-side-tools.js +11 -2
- package/dist/servertool/types.d.ts +4 -0
- package/package.json +1 -1
|
@@ -1,76 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { validateResponsePayloadWithNative } from '../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
|
|
2
2
|
export function validateResponsePayload(payload, _config) {
|
|
3
|
-
|
|
4
|
-
if (!payload.id || typeof payload.id !== 'string') {
|
|
5
|
-
errors.push('响应缺少有效的id字段');
|
|
6
|
-
}
|
|
7
|
-
if (!payload.created || typeof payload.created !== 'number') {
|
|
8
|
-
errors.push('响应缺少有效的created字段');
|
|
9
|
-
}
|
|
10
|
-
if (!payload.model || typeof payload.model !== 'string') {
|
|
11
|
-
errors.push('响应缺少有效的model字段');
|
|
12
|
-
}
|
|
13
|
-
if (!Array.isArray(payload.choices) || payload.choices.length === 0) {
|
|
14
|
-
errors.push('choices数组不能为空');
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
payload.choices.forEach((choice, idx) => {
|
|
18
|
-
if (!isRecord(choice)) {
|
|
19
|
-
errors.push(`choices[${idx}]必须是对象`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
if (!choice.message || typeof choice.message !== 'object') {
|
|
23
|
-
errors.push(`choices[${idx}].message字段必须是对象`);
|
|
24
|
-
}
|
|
25
|
-
else if (Array.isArray(choice.message.tool_calls)) {
|
|
26
|
-
choice.message.tool_calls.forEach((toolCall, tIdx) => {
|
|
27
|
-
if (!isRecord(toolCall)) {
|
|
28
|
-
errors.push(`choices[${idx}].message.tool_calls[${tIdx}]必须是对象`);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (!toolCall.function || typeof toolCall.function !== 'object') {
|
|
32
|
-
errors.push(`choices[${idx}].message.tool_calls[${tIdx}].function字段必须是对象`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
const fn = toolCall.function;
|
|
36
|
-
if (typeof fn.name !== 'string') {
|
|
37
|
-
errors.push(`choices[${idx}].message.tool_calls[${tIdx}].function.name字段必须是字符串`);
|
|
38
|
-
}
|
|
39
|
-
if (typeof fn.arguments !== 'string') {
|
|
40
|
-
errors.push(`choices[${idx}].message.tool_calls[${tIdx}].function.arguments字段必须是字符串`);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
try {
|
|
44
|
-
JSON.parse(fn.arguments);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
errors.push(`choices[${idx}].message.tool_calls[${tIdx}].function.arguments必须是有效JSON`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
if (payload.usage && typeof payload.usage === 'object') {
|
|
55
|
-
const usage = payload.usage;
|
|
56
|
-
const promptTokens = usage.prompt_tokens;
|
|
57
|
-
const completionTokens = usage.completion_tokens;
|
|
58
|
-
const totalTokens = usage.total_tokens;
|
|
59
|
-
if (!isNonNegativeNumber(promptTokens) ||
|
|
60
|
-
!isNonNegativeNumber(completionTokens) ||
|
|
61
|
-
!isNonNegativeNumber(totalTokens)) {
|
|
62
|
-
errors.push('usage字段的token必须是非负数');
|
|
63
|
-
}
|
|
64
|
-
else if (promptTokens + completionTokens !== totalTokens) {
|
|
65
|
-
errors.push('usage.total_tokens 应等于 prompt_tokens 与 completion_tokens 之和');
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (errors.length) {
|
|
69
|
-
const error = new Error(`GLM响应校验失败:\n${errors.join('\n')}`);
|
|
70
|
-
error.code = 'compat_response_validation_failed';
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function isNonNegativeNumber(value) {
|
|
75
|
-
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
3
|
+
validateResponsePayloadWithNative(payload);
|
|
76
4
|
}
|
|
@@ -1,152 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
3
|
-
}
|
|
4
|
-
const ORPHAN_TAG_RE = /^\s*(?:[•*+-]\s*)?(?:<\/?\s*function_calls\s*\/?\s*>|<\/\s*(?:parameter|function|tool_call)\s*>)\s*$/i;
|
|
5
|
-
function stripOrphanTagLines(text) {
|
|
6
|
-
const raw = String(text ?? '');
|
|
7
|
-
if (!raw)
|
|
8
|
-
return { text: raw, changed: false };
|
|
9
|
-
const lines = raw.split(/\r?\n/);
|
|
10
|
-
const kept = [];
|
|
11
|
-
let changed = false;
|
|
12
|
-
for (const line of lines) {
|
|
13
|
-
if (ORPHAN_TAG_RE.test(line)) {
|
|
14
|
-
changed = true;
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
kept.push(line);
|
|
18
|
-
}
|
|
19
|
-
return { text: kept.join('\n'), changed };
|
|
20
|
-
}
|
|
21
|
-
function stripInMessageInPlace(message) {
|
|
22
|
-
let changed = false;
|
|
23
|
-
const content = message.content;
|
|
24
|
-
if (typeof content === 'string') {
|
|
25
|
-
const res = stripOrphanTagLines(content);
|
|
26
|
-
if (res.changed) {
|
|
27
|
-
message.content = res.text;
|
|
28
|
-
changed = true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
else if (Array.isArray(content)) {
|
|
32
|
-
const next = content.map((part) => {
|
|
33
|
-
if (typeof part === 'string') {
|
|
34
|
-
const res = stripOrphanTagLines(part);
|
|
35
|
-
if (res.changed)
|
|
36
|
-
changed = true;
|
|
37
|
-
return res.text;
|
|
38
|
-
}
|
|
39
|
-
if (!isRecord(part))
|
|
40
|
-
return part;
|
|
41
|
-
const p = { ...part };
|
|
42
|
-
if (typeof p.text === 'string') {
|
|
43
|
-
const res = stripOrphanTagLines(p.text);
|
|
44
|
-
if (res.changed) {
|
|
45
|
-
p.text = res.text;
|
|
46
|
-
changed = true;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (typeof p.content === 'string') {
|
|
50
|
-
const res = stripOrphanTagLines(p.content);
|
|
51
|
-
if (res.changed) {
|
|
52
|
-
p.content = res.text;
|
|
53
|
-
changed = true;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return p;
|
|
57
|
-
});
|
|
58
|
-
if (changed) {
|
|
59
|
-
message.content = next;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
for (const key of ['reasoning', 'thinking', 'reasoning_content']) {
|
|
63
|
-
const raw = message[key];
|
|
64
|
-
if (typeof raw === 'string' && raw.trim().length) {
|
|
65
|
-
const res = stripOrphanTagLines(raw);
|
|
66
|
-
if (res.changed) {
|
|
67
|
-
message[key] = res.text;
|
|
68
|
-
changed = true;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return changed;
|
|
73
|
-
}
|
|
74
|
-
function stripInChatPayloadInPlace(root) {
|
|
75
|
-
const choices = Array.isArray(root.choices) ? root.choices : [];
|
|
76
|
-
if (!choices.length)
|
|
77
|
-
return false;
|
|
78
|
-
let changed = false;
|
|
79
|
-
for (const choice of choices) {
|
|
80
|
-
if (!isRecord(choice))
|
|
81
|
-
continue;
|
|
82
|
-
const message = choice.message;
|
|
83
|
-
if (!isRecord(message))
|
|
84
|
-
continue;
|
|
85
|
-
const role = typeof message.role === 'string' ? String(message.role).trim().toLowerCase() : 'assistant';
|
|
86
|
-
if (role !== 'assistant')
|
|
87
|
-
continue;
|
|
88
|
-
if (stripInMessageInPlace(message)) {
|
|
89
|
-
changed = true;
|
|
90
|
-
choice.message = message;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return changed;
|
|
94
|
-
}
|
|
95
|
-
function stripInResponsesPayloadInPlace(root) {
|
|
96
|
-
const output = Array.isArray(root.output) ? root.output : [];
|
|
97
|
-
if (!output.length)
|
|
98
|
-
return false;
|
|
99
|
-
let changed = false;
|
|
100
|
-
for (const item of output) {
|
|
101
|
-
if (!isRecord(item))
|
|
102
|
-
continue;
|
|
103
|
-
const type = typeof item.type === 'string' ? String(item.type).trim().toLowerCase() : '';
|
|
104
|
-
if (type !== 'message')
|
|
105
|
-
continue;
|
|
106
|
-
const role = typeof item.role === 'string' ? String(item.role).trim().toLowerCase() : 'assistant';
|
|
107
|
-
if (role !== 'assistant')
|
|
108
|
-
continue;
|
|
109
|
-
const content = Array.isArray(item.content) ? item.content : [];
|
|
110
|
-
if (content.length) {
|
|
111
|
-
const next = content.map((part) => {
|
|
112
|
-
if (!isRecord(part))
|
|
113
|
-
return part;
|
|
114
|
-
const p = { ...part };
|
|
115
|
-
for (const key of ['text', 'content', 'value']) {
|
|
116
|
-
if (typeof p[key] === 'string' && String(p[key]).trim().length) {
|
|
117
|
-
const res = stripOrphanTagLines(String(p[key]));
|
|
118
|
-
if (res.changed) {
|
|
119
|
-
p[key] = res.text;
|
|
120
|
-
changed = true;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return p;
|
|
125
|
-
});
|
|
126
|
-
if (changed) {
|
|
127
|
-
item.content = next;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (typeof item.output_text === 'string' && String(item.output_text).trim().length) {
|
|
131
|
-
const res = stripOrphanTagLines(String(item.output_text));
|
|
132
|
-
if (res.changed) {
|
|
133
|
-
item.output_text = res.text;
|
|
134
|
-
changed = true;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return changed;
|
|
139
|
-
}
|
|
1
|
+
import { stripOrphanFunctionCallsTagWithNative } from '../../../router/virtual-router/engine-selection/native-chat-process-governance-semantics.js';
|
|
140
2
|
export function stripOrphanFunctionCallsTag(payload) {
|
|
141
|
-
|
|
142
|
-
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
143
|
-
return payload;
|
|
144
|
-
}
|
|
145
|
-
const root = structuredClone(payload);
|
|
146
|
-
const changed = stripInChatPayloadInPlace(root) || stripInResponsesPayloadInPlace(root);
|
|
147
|
-
return (changed ? root : payload);
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return payload;
|
|
151
|
-
}
|
|
3
|
+
return stripOrphanFunctionCallsTagWithNative(payload);
|
|
152
4
|
}
|
|
@@ -179,6 +179,20 @@ function buildAnthropicThinkingFromReasoning(reasoning) {
|
|
|
179
179
|
}
|
|
180
180
|
return { type: 'enabled', budget_tokens: 4096 };
|
|
181
181
|
}
|
|
182
|
+
function normalizeContextToken(value) {
|
|
183
|
+
return typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
184
|
+
}
|
|
185
|
+
function isArkCodingPlanContext(ctx) {
|
|
186
|
+
if (!ctx || typeof ctx !== 'object') {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
const candidates = [
|
|
190
|
+
normalizeContextToken(ctx.providerId),
|
|
191
|
+
normalizeContextToken(ctx.providerKey),
|
|
192
|
+
normalizeContextToken(ctx.runtimeKey)
|
|
193
|
+
];
|
|
194
|
+
return candidates.some((candidate) => candidate === 'ark-coding-plan' || candidate.startsWith('ark-coding-plan.'));
|
|
195
|
+
}
|
|
182
196
|
function cloneAnthropicSystemBlocks(value) {
|
|
183
197
|
if (value === undefined || value === null) {
|
|
184
198
|
return undefined;
|
|
@@ -395,10 +409,19 @@ export class AnthropicSemanticMapper {
|
|
|
395
409
|
if (baseRequest.max_output_tokens && !baseRequest.max_tokens) {
|
|
396
410
|
baseRequest.max_tokens = baseRequest.max_output_tokens;
|
|
397
411
|
}
|
|
398
|
-
const
|
|
412
|
+
const rawReasoning = trimmedParameters?.reasoning;
|
|
413
|
+
const mappedThinking = buildAnthropicThinkingFromReasoning(rawReasoning);
|
|
399
414
|
if (mappedThinking && baseRequest.thinking === undefined) {
|
|
400
415
|
baseRequest.thinking = mappedThinking;
|
|
401
416
|
}
|
|
417
|
+
if (baseRequest.thinking === undefined &&
|
|
418
|
+
rawReasoning === undefined &&
|
|
419
|
+
isArkCodingPlanContext(ctx)) {
|
|
420
|
+
baseRequest.thinking = {
|
|
421
|
+
type: 'enabled',
|
|
422
|
+
budget_tokens: mapReasoningEffortToAnthropicBudget('high')
|
|
423
|
+
};
|
|
424
|
+
}
|
|
402
425
|
if (responsesOrigin && trimmedParameters && Object.prototype.hasOwnProperty.call(trimmedParameters, 'reasoning')) {
|
|
403
426
|
appendLossyFieldAudit(chat, {
|
|
404
427
|
field: 'reasoning',
|
|
@@ -69,6 +69,95 @@ function propagateAdapterContextMetadataFields(adapterContext, metadata, keys) {
|
|
|
69
69
|
function resolveStopMessageRouterMetadata(metadata) {
|
|
70
70
|
return resolveStopMessageRouterMetadataWithNative(metadata);
|
|
71
71
|
}
|
|
72
|
+
function isSearchRouteId(routeId) {
|
|
73
|
+
const normalized = typeof routeId === 'string' ? routeId.trim().toLowerCase() : '';
|
|
74
|
+
return normalized.startsWith('web_search') || normalized.startsWith('search');
|
|
75
|
+
}
|
|
76
|
+
function isCanonicalWebSearchToolDefinition(tool) {
|
|
77
|
+
if (!tool || typeof tool !== 'object' || Array.isArray(tool)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const row = tool;
|
|
81
|
+
const rawType = typeof row.type === 'string' ? row.type.trim().toLowerCase() : '';
|
|
82
|
+
if (rawType === 'web_search_20250305' || rawType === 'web_search') {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
const fnNode = row.function && typeof row.function === 'object' && !Array.isArray(row.function)
|
|
86
|
+
? row.function
|
|
87
|
+
: undefined;
|
|
88
|
+
const name = typeof fnNode?.name === 'string'
|
|
89
|
+
? fnNode.name.trim().toLowerCase()
|
|
90
|
+
: typeof row.name === 'string'
|
|
91
|
+
? row.name.trim().toLowerCase()
|
|
92
|
+
: '';
|
|
93
|
+
return name === 'web_search' || name === 'websearch' || name === 'web-search';
|
|
94
|
+
}
|
|
95
|
+
function maybeApplyDirectBuiltinWebSearchTool(providerPayload, adapterContext, providerProtocol) {
|
|
96
|
+
if (providerProtocol !== 'anthropic-messages') {
|
|
97
|
+
return providerPayload;
|
|
98
|
+
}
|
|
99
|
+
if (!isSearchRouteId(adapterContext.routeId)) {
|
|
100
|
+
return providerPayload;
|
|
101
|
+
}
|
|
102
|
+
const modelId = typeof providerPayload.model === 'string' ? providerPayload.model.trim() : '';
|
|
103
|
+
if (!modelId) {
|
|
104
|
+
return providerPayload;
|
|
105
|
+
}
|
|
106
|
+
const rt = readRuntimeMetadata(adapterContext);
|
|
107
|
+
const webSearch = rt && typeof rt.webSearch === 'object' && rt.webSearch && !Array.isArray(rt.webSearch)
|
|
108
|
+
? rt.webSearch
|
|
109
|
+
: undefined;
|
|
110
|
+
const enginesRaw = Array.isArray(webSearch?.engines) ? webSearch?.engines : [];
|
|
111
|
+
const matchedEngine = enginesRaw.find((entry) => {
|
|
112
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const row = entry;
|
|
116
|
+
const executionMode = typeof row.executionMode === 'string' ? row.executionMode.trim().toLowerCase() : '';
|
|
117
|
+
if (executionMode !== 'direct') {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const directActivation = typeof row.directActivation === 'string' ? row.directActivation.trim().toLowerCase() : 'route';
|
|
121
|
+
if (directActivation !== 'builtin') {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const configuredModelId = typeof row.modelId === 'string' ? row.modelId.trim() : '';
|
|
125
|
+
if (configuredModelId && configuredModelId === modelId) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
const providerKey = typeof row.providerKey === 'string' ? row.providerKey.trim() : '';
|
|
129
|
+
return providerKey.endsWith(`.${modelId}`);
|
|
130
|
+
});
|
|
131
|
+
if (!matchedEngine) {
|
|
132
|
+
return providerPayload;
|
|
133
|
+
}
|
|
134
|
+
const rawMaxUses = typeof matchedEngine.maxUses === 'number' ? matchedEngine.maxUses : Number(matchedEngine.maxUses);
|
|
135
|
+
const maxUses = Number.isFinite(rawMaxUses) && rawMaxUses > 0 ? Math.floor(rawMaxUses) : 2;
|
|
136
|
+
const builtinTool = {
|
|
137
|
+
type: 'web_search_20250305',
|
|
138
|
+
name: 'web_search',
|
|
139
|
+
max_uses: maxUses
|
|
140
|
+
};
|
|
141
|
+
const tools = Array.isArray(providerPayload.tools) ? providerPayload.tools : [];
|
|
142
|
+
let replaced = false;
|
|
143
|
+
const nextTools = [];
|
|
144
|
+
for (const tool of tools) {
|
|
145
|
+
if (!replaced && isCanonicalWebSearchToolDefinition(tool)) {
|
|
146
|
+
nextTools.push(builtinTool);
|
|
147
|
+
replaced = true;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (isCanonicalWebSearchToolDefinition(tool)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
nextTools.push(tool);
|
|
154
|
+
}
|
|
155
|
+
if (!replaced) {
|
|
156
|
+
nextTools.unshift(builtinTool);
|
|
157
|
+
}
|
|
158
|
+
providerPayload.tools = nextTools;
|
|
159
|
+
return providerPayload;
|
|
160
|
+
}
|
|
72
161
|
function extractHubShadowCompareConfig(metadata) {
|
|
73
162
|
const parsed = resolveHubShadowCompareConfigWithNative(metadata);
|
|
74
163
|
if (!parsed) {
|
|
@@ -558,6 +647,7 @@ export class HubPipeline {
|
|
|
558
647
|
stageRecorder: outboundRecorder,
|
|
559
648
|
requestId: normalized.id
|
|
560
649
|
});
|
|
650
|
+
providerPayload = maybeApplyDirectBuiltinWebSearchTool(providerPayload, outboundAdapterContext, outboundProtocol);
|
|
561
651
|
recordHubPolicyObservation({
|
|
562
652
|
policy: effectivePolicy,
|
|
563
653
|
providerProtocol: outboundProtocol,
|
|
@@ -1003,6 +1093,7 @@ export class HubPipeline {
|
|
|
1003
1093
|
stageRecorder: outboundRecorder,
|
|
1004
1094
|
requestId: normalized.id
|
|
1005
1095
|
});
|
|
1096
|
+
providerPayload = maybeApplyDirectBuiltinWebSearchTool(providerPayload, outboundAdapterContext, outboundProtocol);
|
|
1006
1097
|
recordHubPolicyObservation({
|
|
1007
1098
|
policy: effectivePolicy,
|
|
1008
1099
|
providerProtocol: outboundProtocol,
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { extractToolCallsFromReasoningTextWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
|
|
2
|
-
|
|
3
|
-
if (typeof
|
|
4
|
-
|
|
2
|
+
function assertReasoningToolParserNativeAvailable() {
|
|
3
|
+
if (typeof extractToolCallsFromReasoningTextWithNative !== 'function') {
|
|
4
|
+
throw new Error('[reasoning-tool-parser] native bindings unavailable');
|
|
5
5
|
}
|
|
6
|
+
}
|
|
7
|
+
export function extractToolCallsFromReasoningText(text, options) {
|
|
8
|
+
assertReasoningToolParserNativeAvailable();
|
|
6
9
|
const idPrefix = options?.idPrefix ?? 'reasoning';
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
cleanedText: output.cleanedText,
|
|
10
|
-
toolCalls: output.toolCalls
|
|
11
|
-
};
|
|
10
|
+
return extractToolCallsFromReasoningTextWithNative(String(text ?? ''), idPrefix);
|
|
12
11
|
}
|
|
@@ -2,7 +2,7 @@ import { extractOutputSegments } from './output-content-normalizer.js';
|
|
|
2
2
|
import { createBridgeActionState, runBridgeActionPipeline } from '../bridge-actions.js';
|
|
3
3
|
import { resolveBridgePolicy, resolvePolicyActions } from '../bridge-policies.js';
|
|
4
4
|
import { registerResponsesPayloadSnapshot, registerResponsesPassthrough } from './responses-reasoning-registry.js';
|
|
5
|
-
import { normalizeFunctionCallIdWithNative, sanitizeReasoningTaggedTextWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
|
|
5
|
+
import { collectToolCallsFromResponsesWithNative, normalizeFunctionCallIdWithNative, resolveFinishReasonWithNative, sanitizeReasoningTaggedTextWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
|
|
6
6
|
import { sanitizeResponsesFunctionNameWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js';
|
|
7
7
|
function selectCallId(entry) {
|
|
8
8
|
const candidates = [
|
|
@@ -53,55 +53,10 @@ function normalizeToolCall(entry, fallbackPrefix) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
export function collectToolCallsFromResponses(response) {
|
|
56
|
-
|
|
57
|
-
const seenIds = new Set();
|
|
58
|
-
const pushCall = (call, source) => {
|
|
59
|
-
if (!call)
|
|
60
|
-
return;
|
|
61
|
-
const key = typeof call.id === 'string' ? call.id : `${source}_${collected.length}`;
|
|
62
|
-
if (key && seenIds.has(key))
|
|
63
|
-
return;
|
|
64
|
-
if (key)
|
|
65
|
-
seenIds.add(key);
|
|
66
|
-
collected.push(call);
|
|
67
|
-
};
|
|
68
|
-
const required = response?.required_action?.submit_tool_outputs?.tool_calls;
|
|
69
|
-
if (Array.isArray(required)) {
|
|
70
|
-
for (const call of required) {
|
|
71
|
-
pushCall(normalizeToolCall(call, 'req_call'), 'req_call');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const outputItems = Array.isArray(response.output) ? response.output : [];
|
|
75
|
-
for (const item of outputItems) {
|
|
76
|
-
if (!item || typeof item !== 'object')
|
|
77
|
-
continue;
|
|
78
|
-
const type = typeof item.type === 'string' ? String(item.type).toLowerCase() : '';
|
|
79
|
-
if (type !== 'function_call')
|
|
80
|
-
continue;
|
|
81
|
-
pushCall(normalizeToolCall(item, 'output_call'), 'output_call');
|
|
82
|
-
}
|
|
83
|
-
return collected;
|
|
56
|
+
return collectToolCallsFromResponsesWithNative(response);
|
|
84
57
|
}
|
|
85
58
|
export function resolveFinishReason(response, toolCalls) {
|
|
86
|
-
|
|
87
|
-
? response.metadata
|
|
88
|
-
: undefined;
|
|
89
|
-
if (meta && typeof meta.finish_reason === 'string') {
|
|
90
|
-
return meta.finish_reason;
|
|
91
|
-
}
|
|
92
|
-
if (toolCalls.length > 0) {
|
|
93
|
-
return 'tool_calls';
|
|
94
|
-
}
|
|
95
|
-
const status = typeof response.status === 'string' ? response.status.toLowerCase() : '';
|
|
96
|
-
if (status === 'requires_action')
|
|
97
|
-
return 'tool_calls';
|
|
98
|
-
if (status === 'in_progress' || status === 'streaming')
|
|
99
|
-
return 'length';
|
|
100
|
-
if (status === 'cancelled')
|
|
101
|
-
return 'cancelled';
|
|
102
|
-
if (status === 'failed')
|
|
103
|
-
return 'error';
|
|
104
|
-
return 'stop';
|
|
59
|
+
return resolveFinishReasonWithNative(response, toolCalls);
|
|
105
60
|
}
|
|
106
61
|
function unwrapResponsesResponse(payload) {
|
|
107
62
|
if (!payload || typeof payload !== 'object')
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import { createToolCallIdTransformerWithNative, normalizeFunctionCallIdWithNative, normalizeFunctionCallOutputIdWithNative, normalizeResponsesCallIdWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
|
|
1
|
+
import { createToolCallIdTransformerWithNative, normalizeResponsesToolCallIdsWithNative, normalizeFunctionCallIdWithNative, normalizeFunctionCallOutputIdWithNative, normalizeResponsesCallIdWithNative, resolveToolCallIdStyleWithNative, stripInternalToolingMetadataWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
|
|
2
2
|
import { sanitizeResponsesFunctionNameWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js';
|
|
3
3
|
function assertResponsesToolUtilsNativeAvailable() {
|
|
4
4
|
if (typeof createToolCallIdTransformerWithNative !== 'function' ||
|
|
5
|
+
typeof normalizeResponsesToolCallIdsWithNative !== 'function' ||
|
|
5
6
|
typeof normalizeFunctionCallIdWithNative !== 'function' ||
|
|
6
7
|
typeof normalizeFunctionCallOutputIdWithNative !== 'function' ||
|
|
7
8
|
typeof normalizeResponsesCallIdWithNative !== 'function' ||
|
|
9
|
+
typeof resolveToolCallIdStyleWithNative !== 'function' ||
|
|
10
|
+
typeof stripInternalToolingMetadataWithNative !== 'function' ||
|
|
8
11
|
typeof sanitizeResponsesFunctionNameWithNative !== 'function') {
|
|
9
12
|
throw new Error('[responses-tool-utils] native bindings unavailable');
|
|
10
13
|
}
|
|
11
14
|
}
|
|
15
|
+
function replaceMutableRecord(target, next) {
|
|
16
|
+
for (const key of Object.keys(target)) {
|
|
17
|
+
delete target[key];
|
|
18
|
+
}
|
|
19
|
+
Object.assign(target, next);
|
|
20
|
+
}
|
|
12
21
|
export function createToolCallIdTransformer(style) {
|
|
13
22
|
assertResponsesToolUtilsNativeAvailable();
|
|
14
23
|
if (style !== 'fc') {
|
|
@@ -42,141 +51,28 @@ function transformCounter(state, prefix) {
|
|
|
42
51
|
state.__counter = next;
|
|
43
52
|
return `${prefix}_${next}`;
|
|
44
53
|
}
|
|
45
|
-
function isStableToolCallId(raw) {
|
|
46
|
-
return /^((fc|call)_[A-Za-z0-9_-]+)$/i.test(raw);
|
|
47
|
-
}
|
|
48
54
|
export function normalizeResponsesToolCallIds(payload) {
|
|
49
55
|
assertResponsesToolUtilsNativeAvailable();
|
|
50
|
-
if (!payload || typeof payload !== 'object') {
|
|
56
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
51
57
|
return;
|
|
52
58
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const normalizeCallId = (raw, fallbackPrefix) => {
|
|
57
|
-
const trimmed = typeof raw === 'string' ? raw.trim() : '';
|
|
58
|
-
if (trimmed) {
|
|
59
|
-
const cached = aliasMap.get(trimmed);
|
|
60
|
-
if (cached)
|
|
61
|
-
return cached;
|
|
62
|
-
}
|
|
63
|
-
const normalized = trimmed && isStableToolCallId(trimmed)
|
|
64
|
-
? trimmed
|
|
65
|
-
: normalizeFunctionCallIdWithNative({
|
|
66
|
-
callId: trimmed || undefined,
|
|
67
|
-
fallback: nextFallback(fallbackPrefix)
|
|
68
|
-
});
|
|
69
|
-
if (trimmed) {
|
|
70
|
-
aliasMap.set(trimmed, normalized);
|
|
71
|
-
}
|
|
72
|
-
return normalized;
|
|
73
|
-
};
|
|
74
|
-
const output = Array.isArray(payload.output) ? payload.output : [];
|
|
75
|
-
for (const item of output) {
|
|
76
|
-
if (!item || typeof item !== 'object')
|
|
77
|
-
continue;
|
|
78
|
-
const type = typeof item.type === 'string' ? String(item.type).toLowerCase() : '';
|
|
79
|
-
if (type === 'function_call') {
|
|
80
|
-
const normalizedCallId = normalizeCallId(item.call_id ?? item.tool_call_id ?? item.id, 'fc_call');
|
|
81
|
-
item.call_id = normalizedCallId;
|
|
82
|
-
if (item.tool_call_id !== undefined) {
|
|
83
|
-
item.tool_call_id = normalizedCallId;
|
|
84
|
-
}
|
|
85
|
-
const rawOutputId = typeof item.id === 'string' ? item.id : undefined;
|
|
86
|
-
item.id = normalizeFunctionCallOutputIdWithNative({
|
|
87
|
-
callId: normalizedCallId,
|
|
88
|
-
fallback: rawOutputId ?? nextFallback('fc')
|
|
89
|
-
});
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (type === 'function_call_output' || type === 'tool_result' || type === 'tool_message') {
|
|
93
|
-
const normalizedCallId = normalizeCallId(item.call_id ?? item.tool_call_id ?? item.id, 'fc_call');
|
|
94
|
-
item.call_id = normalizedCallId;
|
|
95
|
-
if (item.tool_call_id !== undefined) {
|
|
96
|
-
item.tool_call_id = normalizedCallId;
|
|
97
|
-
}
|
|
98
|
-
const rawOutputId = typeof item.id === 'string' ? item.id : undefined;
|
|
99
|
-
item.id = normalizeFunctionCallOutputIdWithNative({
|
|
100
|
-
callId: normalizedCallId,
|
|
101
|
-
fallback: rawOutputId ?? nextFallback('fc')
|
|
102
|
-
});
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
if (Array.isArray(item.tool_calls)) {
|
|
106
|
-
for (const call of item.tool_calls) {
|
|
107
|
-
if (!call || typeof call !== 'object')
|
|
108
|
-
continue;
|
|
109
|
-
const normalizedCallId = normalizeCallId(call.id ?? call.tool_call_id ?? call.call_id, 'fc_call');
|
|
110
|
-
call.id = normalizedCallId;
|
|
111
|
-
if (call.tool_call_id !== undefined) {
|
|
112
|
-
call.tool_call_id = normalizedCallId;
|
|
113
|
-
}
|
|
114
|
-
if (call.call_id !== undefined) {
|
|
115
|
-
call.call_id = normalizedCallId;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const submitCalls = payload?.required_action?.submit_tool_outputs?.tool_calls;
|
|
121
|
-
if (Array.isArray(submitCalls)) {
|
|
122
|
-
for (const call of submitCalls) {
|
|
123
|
-
if (!call || typeof call !== 'object')
|
|
124
|
-
continue;
|
|
125
|
-
const normalizedCallId = normalizeCallId(call.tool_call_id ?? call.id ?? call.call_id, 'fc_call');
|
|
126
|
-
call.tool_call_id = normalizedCallId;
|
|
127
|
-
call.id = normalizedCallId;
|
|
128
|
-
if (call.call_id !== undefined) {
|
|
129
|
-
call.call_id = normalizedCallId;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
59
|
+
const normalized = normalizeResponsesToolCallIdsWithNative(payload);
|
|
60
|
+
if (normalized && typeof normalized === 'object' && !Array.isArray(normalized)) {
|
|
61
|
+
replaceMutableRecord(payload, normalized);
|
|
132
62
|
}
|
|
133
63
|
}
|
|
134
64
|
export function resolveToolCallIdStyle(metadata) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (typeof raw === 'string') {
|
|
139
|
-
const lowered = raw.trim().toLowerCase();
|
|
140
|
-
if (lowered === 'fc') {
|
|
141
|
-
return 'fc';
|
|
142
|
-
}
|
|
143
|
-
if (lowered === 'preserve') {
|
|
144
|
-
return 'preserve';
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return 'fc';
|
|
65
|
+
assertResponsesToolUtilsNativeAvailable();
|
|
66
|
+
const style = resolveToolCallIdStyleWithNative(metadata ?? null);
|
|
67
|
+
return style === 'preserve' ? 'preserve' : 'fc';
|
|
148
68
|
}
|
|
149
|
-
const RAW_SYSTEM_SENTINEL = '__rcc_raw_system';
|
|
150
69
|
export function stripInternalToolingMetadata(metadata) {
|
|
151
|
-
|
|
70
|
+
assertResponsesToolUtilsNativeAvailable();
|
|
71
|
+
if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata))
|
|
152
72
|
return;
|
|
153
|
-
const
|
|
154
|
-
if ('
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
if (RAW_SYSTEM_SENTINEL in record) {
|
|
158
|
-
delete record[RAW_SYSTEM_SENTINEL];
|
|
159
|
-
}
|
|
160
|
-
if (record.extraFields && typeof record.extraFields === 'object') {
|
|
161
|
-
prunePrivateExtraFields(record.extraFields);
|
|
162
|
-
if (!Object.keys(record.extraFields).length) {
|
|
163
|
-
delete record.extraFields;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
function prunePrivateExtraFields(target) {
|
|
168
|
-
for (const key of Object.keys(target)) {
|
|
169
|
-
const value = target[key];
|
|
170
|
-
if (typeof key === 'string' && key.startsWith('__rcc_')) {
|
|
171
|
-
delete target[key];
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
if (value && typeof value === 'object') {
|
|
175
|
-
prunePrivateExtraFields(value);
|
|
176
|
-
if (!Object.keys(value).length) {
|
|
177
|
-
delete target[key];
|
|
178
|
-
}
|
|
179
|
-
}
|
|
73
|
+
const normalized = stripInternalToolingMetadataWithNative(metadata);
|
|
74
|
+
if (normalized && typeof normalized === 'object' && !Array.isArray(normalized)) {
|
|
75
|
+
replaceMutableRecord(metadata, normalized);
|
|
180
76
|
}
|
|
181
77
|
}
|
|
182
78
|
export function sanitizeResponsesFunctionName(rawName) {
|