@jsonstudio/llms 0.6.3379 → 0.6.3405
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/native/router_hotpath_napi.node +0 -0
- 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/types.d.ts +23 -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
|
@@ -37,6 +37,27 @@ export function normalizeWebSearch(input, routingSource) {
|
|
|
37
37
|
const resolvedProviderKey = resolveWebSearchEngineProviderKey(providerKey, webSearchRouteTargets) ?? providerKey;
|
|
38
38
|
const description = typeof node.description === 'string' && node.description.trim() ? node.description.trim() : undefined;
|
|
39
39
|
const isDefault = node.default === true || (typeof node.default === 'string' && node.default.trim().toLowerCase() === 'true');
|
|
40
|
+
const rawExecutionMode = typeof node.executionMode === 'string'
|
|
41
|
+
? node.executionMode.trim().toLowerCase()
|
|
42
|
+
: typeof node.mode === 'string'
|
|
43
|
+
? node.mode.trim().toLowerCase()
|
|
44
|
+
: '';
|
|
45
|
+
const executionMode = rawExecutionMode === 'direct' ? 'direct' : 'servertool';
|
|
46
|
+
const rawDirectActivation = typeof node.directActivation === 'string'
|
|
47
|
+
? node.directActivation.trim().toLowerCase()
|
|
48
|
+
: typeof node.activation === 'string'
|
|
49
|
+
? node.activation.trim().toLowerCase()
|
|
50
|
+
: '';
|
|
51
|
+
const directActivation = rawDirectActivation === 'builtin'
|
|
52
|
+
? 'builtin'
|
|
53
|
+
: rawDirectActivation === 'route'
|
|
54
|
+
? 'route'
|
|
55
|
+
: executionMode === 'direct'
|
|
56
|
+
? 'route'
|
|
57
|
+
: undefined;
|
|
58
|
+
const modelId = typeof node.modelId === 'string' && node.modelId.trim() ? node.modelId.trim() : undefined;
|
|
59
|
+
const maxUsesRaw = typeof node.maxUses === 'number' ? node.maxUses : Number(node.maxUses);
|
|
60
|
+
const maxUses = Number.isFinite(maxUsesRaw) && maxUsesRaw > 0 ? Math.floor(maxUsesRaw) : undefined;
|
|
40
61
|
const serverToolsDisabled = node.serverToolsDisabled === true ||
|
|
41
62
|
(typeof node.serverToolsDisabled === 'string' &&
|
|
42
63
|
node.serverToolsDisabled.trim().toLowerCase() === 'true') ||
|
|
@@ -51,6 +72,10 @@ export function normalizeWebSearch(input, routingSource) {
|
|
|
51
72
|
providerKey: resolvedProviderKey,
|
|
52
73
|
description,
|
|
53
74
|
default: isDefault,
|
|
75
|
+
executionMode,
|
|
76
|
+
...(directActivation ? { directActivation } : {}),
|
|
77
|
+
...(modelId ? { modelId } : {}),
|
|
78
|
+
...(maxUses ? { maxUses } : {}),
|
|
54
79
|
...(serverToolsDisabled ? { serverToolsDisabled: true } : {})
|
|
55
80
|
});
|
|
56
81
|
}
|
|
@@ -140,26 +140,31 @@ function buildProviderRuntimeEntries(providers) {
|
|
|
140
140
|
}
|
|
141
141
|
return { runtimeEntries, aliasIndex, modelIndex };
|
|
142
142
|
}
|
|
143
|
-
function collectProviderModels(providerRaw,
|
|
143
|
+
function collectProviderModels(providerRaw, _normalizedProvider) {
|
|
144
144
|
const rawModelsNode = providerRaw.models;
|
|
145
145
|
const modelsDeclared = rawModelsNode !== undefined;
|
|
146
146
|
const modelsNode = asRecord(rawModelsNode);
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
147
|
+
const collected = new Set();
|
|
148
|
+
for (const [modelName, modelConfigRaw] of Object.entries(modelsNode)) {
|
|
149
|
+
const normalizedModelName = typeof modelName === 'string' ? modelName.trim() : '';
|
|
150
|
+
if (normalizedModelName) {
|
|
151
|
+
collected.add(normalizedModelName);
|
|
152
|
+
}
|
|
153
|
+
const modelConfig = asRecord(modelConfigRaw);
|
|
154
|
+
const aliasesNode = Array.isArray(modelConfig.aliases)
|
|
155
|
+
? modelConfig.aliases
|
|
156
|
+
: [];
|
|
157
|
+
for (const alias of aliasesNode) {
|
|
158
|
+
if (typeof alias !== 'string') {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const normalizedAlias = alias.trim();
|
|
162
|
+
if (normalizedAlias) {
|
|
163
|
+
collected.add(normalizedAlias);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
161
166
|
}
|
|
162
|
-
return { declared: modelsDeclared, models: Array.from(
|
|
167
|
+
return { declared: modelsDeclared, models: Array.from(collected) };
|
|
163
168
|
}
|
|
164
169
|
function normalizeClassifier(input) {
|
|
165
170
|
const normalized = asRecord(input);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function normalizeResponsePayloadWithNative(payload: Record<string, unknown>, config?: Record<string, unknown>): Record<string, unknown>;
|
|
2
|
+
export declare function validateResponsePayloadWithNative(payload: Record<string, unknown>): void;
|
|
3
|
+
export declare function applyRequestRulesWithNative(payload: Record<string, unknown>, config?: Record<string, unknown>): Record<string, unknown>;
|
|
4
|
+
export declare function applyResponseBlacklistWithNative(payload: Record<string, unknown>, config?: Record<string, unknown>): Record<string, unknown>;
|
|
5
|
+
export declare function normalizeToolCallIdsWithNative(payload: Record<string, unknown>): Record<string, unknown>;
|
|
6
|
+
export declare function enforceLmstudioResponsesFcToolCallIdsWithNative(payload: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { failNativeRequired, isNativeDisabledByEnv } from './native-router-hotpath-policy.js';
|
|
2
|
+
import { loadNativeRouterHotpathBindingForInternalUse } from './native-router-hotpath.js';
|
|
3
|
+
function readNativeFunction(name) {
|
|
4
|
+
const binding = loadNativeRouterHotpathBindingForInternalUse();
|
|
5
|
+
const fn = binding?.[name];
|
|
6
|
+
return typeof fn === 'function' ? fn : null;
|
|
7
|
+
}
|
|
8
|
+
function safeStringify(value) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.stringify(value);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function parseRecord(raw) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(raw);
|
|
19
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function normalizeResponsePayloadWithNative(payload, config) {
|
|
29
|
+
const capability = 'normalizeResponsePayloadJson';
|
|
30
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
31
|
+
if (isNativeDisabledByEnv()) {
|
|
32
|
+
return fail('native disabled');
|
|
33
|
+
}
|
|
34
|
+
const fn = readNativeFunction(capability);
|
|
35
|
+
if (!fn) {
|
|
36
|
+
return fail();
|
|
37
|
+
}
|
|
38
|
+
const payloadJson = safeStringify(payload);
|
|
39
|
+
const configJson = config ? safeStringify(config) : '{}';
|
|
40
|
+
if (!payloadJson || !configJson) {
|
|
41
|
+
return fail('json stringify failed');
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const raw = fn(payloadJson, configJson);
|
|
45
|
+
if (typeof raw !== 'string' || !raw) {
|
|
46
|
+
return fail('empty result');
|
|
47
|
+
}
|
|
48
|
+
const parsed = parseRecord(raw);
|
|
49
|
+
return parsed ?? fail('invalid payload');
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
53
|
+
return fail(reason);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function validateResponsePayloadWithNative(payload) {
|
|
57
|
+
const capability = 'validateResponsePayloadJson';
|
|
58
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
59
|
+
if (isNativeDisabledByEnv()) {
|
|
60
|
+
return fail('native disabled');
|
|
61
|
+
}
|
|
62
|
+
const fn = readNativeFunction(capability);
|
|
63
|
+
if (!fn) {
|
|
64
|
+
return fail();
|
|
65
|
+
}
|
|
66
|
+
const payloadJson = safeStringify(payload);
|
|
67
|
+
if (!payloadJson) {
|
|
68
|
+
return fail('json stringify failed');
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
fn(payloadJson);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
75
|
+
throw new Error(reason);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function applyRequestRulesWithNative(payload, config) {
|
|
79
|
+
const capability = 'applyRequestRulesJson';
|
|
80
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
81
|
+
if (isNativeDisabledByEnv())
|
|
82
|
+
return fail('native disabled');
|
|
83
|
+
const fn = readNativeFunction(capability);
|
|
84
|
+
if (!fn)
|
|
85
|
+
return fail();
|
|
86
|
+
const payloadJson = safeStringify(payload);
|
|
87
|
+
const configJson = config ? safeStringify(config) : '{}';
|
|
88
|
+
if (!payloadJson || !configJson)
|
|
89
|
+
return fail('json stringify failed');
|
|
90
|
+
try {
|
|
91
|
+
const raw = fn(payloadJson, configJson);
|
|
92
|
+
if (typeof raw !== 'string' || !raw)
|
|
93
|
+
return fail('empty result');
|
|
94
|
+
const parsed = parseRecord(raw);
|
|
95
|
+
return parsed ?? fail('invalid payload');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
99
|
+
return fail(reason);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function applyResponseBlacklistWithNative(payload, config) {
|
|
103
|
+
const capability = 'applyResponseBlacklistJson';
|
|
104
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
105
|
+
if (isNativeDisabledByEnv())
|
|
106
|
+
return fail('native disabled');
|
|
107
|
+
const fn = readNativeFunction(capability);
|
|
108
|
+
if (!fn)
|
|
109
|
+
return fail();
|
|
110
|
+
const payloadJson = safeStringify(payload);
|
|
111
|
+
const configJson = config ? safeStringify(config) : '{}';
|
|
112
|
+
if (!payloadJson || !configJson)
|
|
113
|
+
return fail('json stringify failed');
|
|
114
|
+
try {
|
|
115
|
+
const raw = fn(payloadJson, configJson);
|
|
116
|
+
if (typeof raw !== 'string' || !raw)
|
|
117
|
+
return fail('empty result');
|
|
118
|
+
const parsed = parseRecord(raw);
|
|
119
|
+
return parsed ?? fail('invalid payload');
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
123
|
+
return fail(reason);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export function normalizeToolCallIdsWithNative(payload) {
|
|
127
|
+
const capability = 'normalizeToolCallIdsJson';
|
|
128
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
129
|
+
if (isNativeDisabledByEnv())
|
|
130
|
+
return fail('native disabled');
|
|
131
|
+
const fn = readNativeFunction(capability);
|
|
132
|
+
if (!fn)
|
|
133
|
+
return fail();
|
|
134
|
+
const payloadJson = safeStringify(payload);
|
|
135
|
+
if (!payloadJson)
|
|
136
|
+
return fail('json stringify failed');
|
|
137
|
+
try {
|
|
138
|
+
const raw = fn(payloadJson);
|
|
139
|
+
if (typeof raw !== 'string' || !raw)
|
|
140
|
+
return fail('empty result');
|
|
141
|
+
const parsed = parseRecord(raw);
|
|
142
|
+
return parsed ?? fail('invalid payload');
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
146
|
+
return fail(reason);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function enforceLmstudioResponsesFcToolCallIdsWithNative(payload) {
|
|
150
|
+
const capability = 'enforceLmstudioResponsesFcToolCallIdsJson';
|
|
151
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
152
|
+
if (isNativeDisabledByEnv())
|
|
153
|
+
return fail('native disabled');
|
|
154
|
+
const fn = readNativeFunction(capability);
|
|
155
|
+
if (!fn)
|
|
156
|
+
return fail();
|
|
157
|
+
const payloadJson = safeStringify(payload);
|
|
158
|
+
if (!payloadJson)
|
|
159
|
+
return fail('json stringify failed');
|
|
160
|
+
try {
|
|
161
|
+
const raw = fn(payloadJson);
|
|
162
|
+
if (typeof raw !== 'string' || !raw)
|
|
163
|
+
return fail('empty result');
|
|
164
|
+
const parsed = parseRecord(raw);
|
|
165
|
+
return parsed ?? fail('invalid payload');
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
169
|
+
return fail(reason);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -98,6 +98,8 @@ const REQUIRED_NATIVE_EXPORTS = [
|
|
|
98
98
|
'chatToolToBridgeDefinitionJson',
|
|
99
99
|
'mapBridgeToolsToChatWithOptionsJson',
|
|
100
100
|
'mapChatToolsToBridgeWithOptionsJson',
|
|
101
|
+
'collectToolCallsFromResponsesJson',
|
|
102
|
+
'resolveFinishReasonJson',
|
|
101
103
|
'captureReqInboundResponsesContextSnapshotJson',
|
|
102
104
|
'computeQuotaBucketsJson',
|
|
103
105
|
'extractReasoningSegmentsJson',
|
|
@@ -167,6 +169,15 @@ const REQUIRED_NATIVE_EXPORTS = [
|
|
|
167
169
|
'normalizeChatResponseReasoningToolsJson',
|
|
168
170
|
'normalizeBridgeToolCallIdsJson',
|
|
169
171
|
'normalizeChatMessageContentJson',
|
|
172
|
+
'validateResponsePayloadJson',
|
|
173
|
+
'normalizeResponsePayloadJson',
|
|
174
|
+
'applyResponseBlacklistJson',
|
|
175
|
+
'normalizeToolCallIdsJson',
|
|
176
|
+
'normalizeResponsesToolCallIdsJson',
|
|
177
|
+
'resolveToolCallIdStyleJson',
|
|
178
|
+
'stripInternalToolingMetadataJson',
|
|
179
|
+
'enforceLmstudioResponsesFcToolCallIdsJson',
|
|
180
|
+
'applyRequestRulesJson',
|
|
170
181
|
'normalizeArgsBySchemaJson',
|
|
171
182
|
'normalizeToolsJson',
|
|
172
183
|
'normalizeFunctionCallIdJson',
|
|
@@ -26,6 +26,8 @@ export declare function mapBridgeToolsToChatWithNative(rawTools: unknown, option
|
|
|
26
26
|
export declare function mapChatToolsToBridgeWithNative(rawTools: unknown, options?: {
|
|
27
27
|
sanitizeMode?: string;
|
|
28
28
|
}): Array<Record<string, unknown>>;
|
|
29
|
+
export declare function collectToolCallsFromResponsesWithNative(response: Record<string, unknown>): Array<Record<string, unknown>>;
|
|
30
|
+
export declare function resolveFinishReasonWithNative(response: Record<string, unknown>, toolCalls: Array<Record<string, unknown>>): string;
|
|
29
31
|
export declare function hasValidThoughtSignatureWithNative(block: unknown, options?: Record<string, unknown>): boolean;
|
|
30
32
|
export declare function sanitizeThinkingBlockWithNative(block: unknown): Record<string, unknown>;
|
|
31
33
|
export declare function filterInvalidThinkingBlocksWithNative(messages: unknown, options?: Record<string, unknown>): unknown[];
|
|
@@ -49,6 +51,9 @@ export declare function enforceToolCallIdStyleWithNative(messages: unknown[], st
|
|
|
49
51
|
messages: unknown[];
|
|
50
52
|
state: Record<string, unknown>;
|
|
51
53
|
};
|
|
54
|
+
export declare function normalizeResponsesToolCallIdsWithNative(payload: unknown): Record<string, unknown> | null;
|
|
55
|
+
export declare function resolveToolCallIdStyleWithNative(metadata: unknown): string;
|
|
56
|
+
export declare function stripInternalToolingMetadataWithNative(metadata: unknown): Record<string, unknown> | null;
|
|
52
57
|
export declare function buildProviderProtocolErrorWithNative(input: {
|
|
53
58
|
message: string;
|
|
54
59
|
code: string;
|
|
@@ -123,6 +123,10 @@ function parseToolDefinitionArray(raw) {
|
|
|
123
123
|
}
|
|
124
124
|
return output;
|
|
125
125
|
}
|
|
126
|
+
function parseString(raw) {
|
|
127
|
+
const parsed = parseJson(raw);
|
|
128
|
+
return typeof parsed === 'string' ? parsed : null;
|
|
129
|
+
}
|
|
126
130
|
function parseStringArray(raw) {
|
|
127
131
|
const parsed = parseArray(raw);
|
|
128
132
|
if (!parsed)
|
|
@@ -510,6 +514,60 @@ export function mapChatToolsToBridgeWithNative(rawTools, options) {
|
|
|
510
514
|
return fail(reason);
|
|
511
515
|
}
|
|
512
516
|
}
|
|
517
|
+
export function collectToolCallsFromResponsesWithNative(response) {
|
|
518
|
+
const capability = 'collectToolCallsFromResponsesJson';
|
|
519
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
520
|
+
if (isNativeDisabledByEnv()) {
|
|
521
|
+
return fail('native disabled');
|
|
522
|
+
}
|
|
523
|
+
const fn = readNativeFunction(capability);
|
|
524
|
+
if (!fn) {
|
|
525
|
+
return fail();
|
|
526
|
+
}
|
|
527
|
+
const payloadJson = safeStringify(response ?? {});
|
|
528
|
+
if (!payloadJson) {
|
|
529
|
+
return fail('json stringify failed');
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const raw = fn(payloadJson);
|
|
533
|
+
if (typeof raw !== 'string' || !raw) {
|
|
534
|
+
return fail('empty result');
|
|
535
|
+
}
|
|
536
|
+
const parsed = parseToolDefinitionArray(raw);
|
|
537
|
+
return parsed ?? fail('invalid payload');
|
|
538
|
+
}
|
|
539
|
+
catch (error) {
|
|
540
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
541
|
+
return fail(reason);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
export function resolveFinishReasonWithNative(response, toolCalls) {
|
|
545
|
+
const capability = 'resolveFinishReasonJson';
|
|
546
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
547
|
+
if (isNativeDisabledByEnv()) {
|
|
548
|
+
return fail('native disabled');
|
|
549
|
+
}
|
|
550
|
+
const fn = readNativeFunction(capability);
|
|
551
|
+
if (!fn) {
|
|
552
|
+
return fail();
|
|
553
|
+
}
|
|
554
|
+
const responseJson = safeStringify(response ?? {});
|
|
555
|
+
const toolCallsJson = safeStringify(Array.isArray(toolCalls) ? toolCalls : []);
|
|
556
|
+
if (!responseJson || !toolCallsJson) {
|
|
557
|
+
return fail('json stringify failed');
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
const raw = fn(responseJson, toolCallsJson);
|
|
561
|
+
if (typeof raw !== 'string' || !raw) {
|
|
562
|
+
return fail('empty result');
|
|
563
|
+
}
|
|
564
|
+
return parseString(raw) ?? fail('invalid payload');
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
568
|
+
return fail(reason);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
513
571
|
export function hasValidThoughtSignatureWithNative(block, options) {
|
|
514
572
|
const capability = 'hasValidThoughtSignatureJson';
|
|
515
573
|
const fail = (reason) => failNativeRequired(capability, reason);
|
|
@@ -973,6 +1031,85 @@ export function enforceToolCallIdStyleWithNative(messages, state) {
|
|
|
973
1031
|
return fail(reason);
|
|
974
1032
|
}
|
|
975
1033
|
}
|
|
1034
|
+
export function normalizeResponsesToolCallIdsWithNative(payload) {
|
|
1035
|
+
const capability = 'normalizeResponsesToolCallIdsJson';
|
|
1036
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
1037
|
+
if (isNativeDisabledByEnv()) {
|
|
1038
|
+
return fail('native disabled');
|
|
1039
|
+
}
|
|
1040
|
+
const fn = readNativeFunction(capability);
|
|
1041
|
+
if (!fn) {
|
|
1042
|
+
return fail();
|
|
1043
|
+
}
|
|
1044
|
+
const payloadJson = safeStringify(payload ?? null);
|
|
1045
|
+
if (!payloadJson) {
|
|
1046
|
+
return fail('json stringify failed');
|
|
1047
|
+
}
|
|
1048
|
+
try {
|
|
1049
|
+
const raw = fn(payloadJson);
|
|
1050
|
+
if (typeof raw !== 'string' || !raw) {
|
|
1051
|
+
return fail('empty result');
|
|
1052
|
+
}
|
|
1053
|
+
return parseRecord(raw) ?? fail('invalid payload');
|
|
1054
|
+
}
|
|
1055
|
+
catch (error) {
|
|
1056
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
1057
|
+
return fail(reason);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
export function resolveToolCallIdStyleWithNative(metadata) {
|
|
1061
|
+
const capability = 'resolveToolCallIdStyleJson';
|
|
1062
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
1063
|
+
if (isNativeDisabledByEnv()) {
|
|
1064
|
+
return fail('native disabled');
|
|
1065
|
+
}
|
|
1066
|
+
const fn = readNativeFunction(capability);
|
|
1067
|
+
if (!fn) {
|
|
1068
|
+
return fail();
|
|
1069
|
+
}
|
|
1070
|
+
const metadataJson = safeStringify(metadata ?? null);
|
|
1071
|
+
if (!metadataJson) {
|
|
1072
|
+
return fail('json stringify failed');
|
|
1073
|
+
}
|
|
1074
|
+
try {
|
|
1075
|
+
const raw = fn(metadataJson);
|
|
1076
|
+
if (typeof raw !== 'string' || !raw) {
|
|
1077
|
+
return fail('empty result');
|
|
1078
|
+
}
|
|
1079
|
+
const parsed = parseJson(raw);
|
|
1080
|
+
return typeof parsed === 'string' ? parsed : fail('invalid payload');
|
|
1081
|
+
}
|
|
1082
|
+
catch (error) {
|
|
1083
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
1084
|
+
return fail(reason);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
export function stripInternalToolingMetadataWithNative(metadata) {
|
|
1088
|
+
const capability = 'stripInternalToolingMetadataJson';
|
|
1089
|
+
const fail = (reason) => failNativeRequired(capability, reason);
|
|
1090
|
+
if (isNativeDisabledByEnv()) {
|
|
1091
|
+
return fail('native disabled');
|
|
1092
|
+
}
|
|
1093
|
+
const fn = readNativeFunction(capability);
|
|
1094
|
+
if (!fn) {
|
|
1095
|
+
return fail();
|
|
1096
|
+
}
|
|
1097
|
+
const metadataJson = safeStringify(metadata ?? null);
|
|
1098
|
+
if (!metadataJson) {
|
|
1099
|
+
return fail('json stringify failed');
|
|
1100
|
+
}
|
|
1101
|
+
try {
|
|
1102
|
+
const raw = fn(metadataJson);
|
|
1103
|
+
if (typeof raw !== 'string' || !raw) {
|
|
1104
|
+
return fail('empty result');
|
|
1105
|
+
}
|
|
1106
|
+
return parseRecord(raw) ?? fail('invalid payload');
|
|
1107
|
+
}
|
|
1108
|
+
catch (error) {
|
|
1109
|
+
const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
|
|
1110
|
+
return fail(reason);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
976
1113
|
export function buildProviderProtocolErrorWithNative(input) {
|
|
977
1114
|
const capability = 'buildProviderProtocolErrorJson';
|
|
978
1115
|
const fail = (reason) => failNativeRequired(capability, reason);
|
|
@@ -215,11 +215,34 @@ export interface ProviderHealthConfig {
|
|
|
215
215
|
cooldownMs: number;
|
|
216
216
|
fatalCooldownMs?: number;
|
|
217
217
|
}
|
|
218
|
+
export type VirtualRouterWebSearchExecutionMode = 'servertool' | 'direct';
|
|
219
|
+
export type VirtualRouterWebSearchDirectActivation = 'route' | 'builtin';
|
|
218
220
|
export interface VirtualRouterWebSearchEngineConfig {
|
|
219
221
|
id: string;
|
|
220
222
|
providerKey: string;
|
|
221
223
|
description?: string;
|
|
222
224
|
default?: boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Search execution mode:
|
|
227
|
+
* - servertool: expose canonical web_search tool and execute through servertool engine.
|
|
228
|
+
* - direct: route to a search-capable model/provider directly; servertool injection must skip it.
|
|
229
|
+
*/
|
|
230
|
+
executionMode?: VirtualRouterWebSearchExecutionMode;
|
|
231
|
+
/**
|
|
232
|
+
* When executionMode=direct, controls how the upstream search capability is activated.
|
|
233
|
+
* - route: route selection itself enables native search behavior (e.g. deepseek-web search route).
|
|
234
|
+
* - builtin: upstream requires a provider-native builtin search tool/schema.
|
|
235
|
+
*/
|
|
236
|
+
directActivation?: VirtualRouterWebSearchDirectActivation;
|
|
237
|
+
/**
|
|
238
|
+
* Optional target model id for direct-mode matching when request/compat layers need to detect
|
|
239
|
+
* which routed provider payload should receive native web search activation.
|
|
240
|
+
*/
|
|
241
|
+
modelId?: string;
|
|
242
|
+
/**
|
|
243
|
+
* Optional builtin max-uses hint for providers that support builtin web search tools.
|
|
244
|
+
*/
|
|
245
|
+
maxUses?: number;
|
|
223
246
|
/**
|
|
224
247
|
* When true, this engine will never be used by server-side tools
|
|
225
248
|
* (e.g. web_search). It will also be omitted from injected tool
|
|
@@ -118,6 +118,27 @@ function getWebSearchConfig(ctx) {
|
|
|
118
118
|
: undefined;
|
|
119
119
|
if (!id || !providerKey)
|
|
120
120
|
continue;
|
|
121
|
+
const rawExecutionMode = typeof obj.executionMode === 'string'
|
|
122
|
+
? obj.executionMode.trim().toLowerCase()
|
|
123
|
+
: typeof obj.mode === 'string'
|
|
124
|
+
? obj.mode.trim().toLowerCase()
|
|
125
|
+
: '';
|
|
126
|
+
const executionMode = rawExecutionMode === 'direct' ? 'direct' : 'servertool';
|
|
127
|
+
const rawDirectActivation = typeof obj.directActivation === 'string'
|
|
128
|
+
? obj.directActivation.trim().toLowerCase()
|
|
129
|
+
: typeof obj.activation === 'string'
|
|
130
|
+
? obj.activation.trim().toLowerCase()
|
|
131
|
+
: '';
|
|
132
|
+
const directActivation = rawDirectActivation === 'builtin'
|
|
133
|
+
? 'builtin'
|
|
134
|
+
: rawDirectActivation === 'route'
|
|
135
|
+
? 'route'
|
|
136
|
+
: executionMode === 'direct'
|
|
137
|
+
? 'route'
|
|
138
|
+
: undefined;
|
|
139
|
+
const modelId = typeof obj.modelId === 'string' && obj.modelId.trim() ? obj.modelId.trim() : undefined;
|
|
140
|
+
const rawMaxUses = typeof obj.maxUses === 'number' ? obj.maxUses : Number(obj.maxUses);
|
|
141
|
+
const maxUses = Number.isFinite(rawMaxUses) && rawMaxUses > 0 ? Math.floor(rawMaxUses) : undefined;
|
|
121
142
|
const serverToolsDisabled = obj.serverToolsDisabled === true ||
|
|
122
143
|
(typeof obj.serverToolsDisabled === 'string' &&
|
|
123
144
|
obj.serverToolsDisabled.trim().toLowerCase() === 'true') ||
|
|
@@ -142,6 +163,10 @@ function getWebSearchConfig(ctx) {
|
|
|
142
163
|
providerKey,
|
|
143
164
|
description: typeof obj.description === 'string' && obj.description.trim() ? obj.description.trim() : undefined,
|
|
144
165
|
default: obj.default === true,
|
|
166
|
+
executionMode,
|
|
167
|
+
...(directActivation ? { directActivation } : {}),
|
|
168
|
+
...(modelId ? { modelId } : {}),
|
|
169
|
+
...(maxUses ? { maxUses } : {}),
|
|
145
170
|
...(serverToolsDisabled ? { serverToolsDisabled: true } : {}),
|
|
146
171
|
...(searchEngineList ? { searchEngineList } : {})
|
|
147
172
|
});
|
|
@@ -181,7 +206,7 @@ function resolveWebSearchEngine(config, engineId) {
|
|
|
181
206
|
return undefined;
|
|
182
207
|
}
|
|
183
208
|
function buildEnginePriorityList(config, engineId) {
|
|
184
|
-
const engines = (Array.isArray(config.engines) ? config.engines : []).filter((engine) => !engine.serverToolsDisabled);
|
|
209
|
+
const engines = (Array.isArray(config.engines) ? config.engines : []).filter((engine) => !engine.serverToolsDisabled && (engine.executionMode ?? 'servertool') === 'servertool');
|
|
185
210
|
if (!engines.length) {
|
|
186
211
|
return [];
|
|
187
212
|
}
|
|
@@ -100,7 +100,7 @@ function normalizeFilterTokenSet(values) {
|
|
|
100
100
|
return normalized.size > 0 ? normalized : null;
|
|
101
101
|
}
|
|
102
102
|
function isNameIncluded(name, includeSet, excludeSet) {
|
|
103
|
-
const normalized = name
|
|
103
|
+
const normalized = normalizeServerToolCallName(name);
|
|
104
104
|
if (includeSet && !includeSet.has(normalized)) {
|
|
105
105
|
return false;
|
|
106
106
|
}
|
|
@@ -109,6 +109,13 @@ function isNameIncluded(name, includeSet, excludeSet) {
|
|
|
109
109
|
}
|
|
110
110
|
return true;
|
|
111
111
|
}
|
|
112
|
+
function normalizeServerToolCallName(name) {
|
|
113
|
+
const normalized = name.trim().toLowerCase();
|
|
114
|
+
if (normalized === 'websearch' || normalized === 'web-search') {
|
|
115
|
+
return 'web_search';
|
|
116
|
+
}
|
|
117
|
+
return normalized;
|
|
118
|
+
}
|
|
112
119
|
function extractToolCallsFromMessage(message) {
|
|
113
120
|
const toolCalls = getArray(message.tool_calls);
|
|
114
121
|
const out = [];
|
|
@@ -120,7 +127,9 @@ function extractToolCallsFromMessage(message) {
|
|
|
120
127
|
const fn = asObject(tc.function) ??
|
|
121
128
|
asObject(tc.functionCall) ??
|
|
122
129
|
asObject(tc.function_call);
|
|
123
|
-
const name = fn && typeof fn.name === 'string' && String(fn.name).trim()
|
|
130
|
+
const name = fn && typeof fn.name === 'string' && String(fn.name).trim()
|
|
131
|
+
? normalizeServerToolCallName(String(fn.name))
|
|
132
|
+
: '';
|
|
124
133
|
const rawArgs = (fn ? fn.arguments : undefined) ??
|
|
125
134
|
(fn ? fn.args : undefined) ??
|
|
126
135
|
(fn ? fn.input : undefined) ??
|
|
@@ -137,6 +137,10 @@ export type ServerToolBackendPlan = {
|
|
|
137
137
|
providerKey: string;
|
|
138
138
|
description?: string;
|
|
139
139
|
default?: boolean;
|
|
140
|
+
executionMode?: 'servertool' | 'direct';
|
|
141
|
+
directActivation?: 'route' | 'builtin';
|
|
142
|
+
modelId?: string;
|
|
143
|
+
maxUses?: number;
|
|
140
144
|
serverToolsDisabled?: boolean;
|
|
141
145
|
searchEngineList?: string[];
|
|
142
146
|
}[];
|