@jsonstudio/llms 0.6.3409 → 0.6.3539
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/codecs/anthropic-openai-codec.d.ts +12 -3
- package/dist/conversion/codecs/anthropic-openai-codec.js +32 -92
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +6 -5
- package/dist/conversion/codecs/gemini-openai-codec.js +48 -685
- package/dist/conversion/codecs/openai-openai-codec.d.ts +1 -1
- package/dist/conversion/codecs/openai-openai-codec.js +34 -100
- package/dist/conversion/codecs/responses-openai-codec.d.ts +1 -1
- package/dist/conversion/codecs/responses-openai-codec.js +47 -159
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.d.ts +2 -6
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.js +29 -245
- package/dist/conversion/compat/actions/anthropic-claude-code-user-id.d.ts +3 -0
- package/dist/conversion/compat/actions/anthropic-claude-code-user-id.js +30 -0
- package/dist/conversion/compat/actions/antigravity-thought-signature-prepare.js +21 -232
- package/dist/conversion/compat/actions/deepseek-web-request.js +41 -276
- package/dist/conversion/compat/actions/deepseek-web-response.js +64 -859
- package/dist/conversion/compat/actions/gemini-cli-request.d.ts +1 -1
- package/dist/conversion/compat/actions/gemini-cli-request.js +20 -613
- package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -15
- package/dist/conversion/compat/actions/gemini-web-search.js +22 -69
- package/dist/conversion/compat/actions/glm-tool-extraction.d.ts +3 -2
- package/dist/conversion/compat/actions/glm-tool-extraction.js +28 -257
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +0 -8
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +24 -206
- package/dist/conversion/compat/actions/qwen-transform.d.ts +3 -2
- package/dist/conversion/compat/actions/qwen-transform.js +30 -271
- package/dist/conversion/compat/actions/tool-text-request-guidance.js +3 -173
- package/dist/conversion/compat/actions/universal-shape-filter.d.ts +6 -23
- package/dist/conversion/compat/actions/universal-shape-filter.js +4 -383
- package/dist/conversion/hub/pipeline/compat/native-adapter-context.js +1 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +1 -2
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +50 -104
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +12 -10
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +0 -2
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +46 -67
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +15 -40
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +47 -348
- package/dist/conversion/responses/responses-openai-bridge.js +129 -611
- package/dist/conversion/shared/chat-output-normalizer.js +6 -0
- package/dist/conversion/shared/chat-request-filters.js +1 -1
- package/dist/conversion/shared/output-content-normalizer.js +10 -0
- package/dist/conversion/shared/responses-conversation-store.js +22 -135
- package/dist/conversion/shared/responses-output-builder.d.ts +0 -2
- package/dist/conversion/shared/responses-output-builder.js +28 -318
- package/dist/conversion/shared/responses-response-utils.js +35 -86
- package/dist/conversion/shared/streaming-text-extractor.d.ts +1 -2
- package/dist/conversion/shared/streaming-text-extractor.js +13 -14
- package/dist/native/router_hotpath_napi.node +0 -0
- package/dist/router/virtual-router/bootstrap/routing-config.js +11 -3
- package/dist/router/virtual-router/engine-legacy.d.ts +3 -3
- package/dist/router/virtual-router/engine-legacy.js +15 -7
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +16 -0
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +434 -46
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +83 -0
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +295 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.d.ts +7 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js +8 -1
- package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +383 -298
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +20 -0
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +201 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.js +37 -0
- package/dist/router/virtual-router/engine.js +0 -38
- package/dist/router/virtual-router/features.js +44 -3
- package/dist/router/virtual-router/routing-instructions/parse.d.ts +0 -12
- package/dist/router/virtual-router/routing-instructions/parse.js +9 -389
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +3 -6
- package/dist/router/virtual-router/stop-message-state-sync.js +50 -21
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +26 -0
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +12 -2
- package/package.json +1 -1
- package/dist/router/virtual-router/engine-legacy/route-finalize.d.ts +0 -9
- package/dist/router/virtual-router/engine-legacy/route-finalize.js +0 -84
- package/dist/router/virtual-router/engine-legacy/route-selection.d.ts +0 -17
- package/dist/router/virtual-router/engine-legacy/route-selection.js +0 -205
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.d.ts +0 -3
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.js +0 -36
- package/dist/router/virtual-router/engine-legacy/route-state.d.ts +0 -12
- package/dist/router/virtual-router/engine-legacy/route-state.js +0 -386
- package/dist/router/virtual-router/engine-legacy/routing.d.ts +0 -8
- package/dist/router/virtual-router/engine-legacy/routing.js +0 -8
|
@@ -1,119 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
const SHELL_LIKE_TOOL_NAMES = new Set(['exec_command', 'shell_command', 'shell', 'bash', 'terminal']);
|
|
10
|
-
const SHELL_TOOL_NAME_ALIASES = new Map([
|
|
11
|
-
['shell_command', 'exec_command'],
|
|
12
|
-
['shell', 'exec_command'],
|
|
13
|
-
['bash', 'exec_command'],
|
|
14
|
-
['terminal', 'exec_command']
|
|
15
|
-
]);
|
|
16
|
-
const SHELL_ALLOWED_TOOL_CANDIDATES = [
|
|
17
|
-
'exec_command',
|
|
18
|
-
'shell_command',
|
|
19
|
-
'bash',
|
|
20
|
-
'shell',
|
|
21
|
-
'terminal'
|
|
22
|
-
];
|
|
23
|
-
const DEFAULT_TOKEN_ENCODING = 'cl100k_base';
|
|
24
|
-
const tokenEncoderCache = new Map();
|
|
25
|
-
let defaultTokenEncoder = null;
|
|
1
|
+
import { buildNativeReqOutboundCompatAdapterContext } from '../../hub/pipeline/compat/native-adapter-context.js';
|
|
2
|
+
import { loadNativeRouterHotpathBindingForInternalUse } from '../../../router/virtual-router/engine-selection/native-router-hotpath.js';
|
|
3
|
+
import { isNativeDisabledByEnv, makeNativeRequiredError } from '../../../router/virtual-router/engine-selection/native-router-hotpath-policy.js';
|
|
4
|
+
const CAPABILITY = 'runRespInboundStage3CompatJson';
|
|
5
|
+
const PROFILE = 'chat:deepseek-web';
|
|
6
|
+
const DEFAULT_PROVIDER_PROTOCOL = 'openai-chat';
|
|
7
|
+
const DEFAULT_ENTRY_ENDPOINT = '/v1/chat/completions';
|
|
26
8
|
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
-
const
|
|
28
|
-
if (typeof value
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
const trimmed = value.trim();
|
|
32
|
-
return trimmed.length ? trimmed : undefined;
|
|
33
|
-
};
|
|
34
|
-
function flattenMessageContent(content) {
|
|
35
|
-
if (typeof content === 'string') {
|
|
36
|
-
return content.trim();
|
|
37
|
-
}
|
|
38
|
-
if (Array.isArray(content)) {
|
|
39
|
-
const parts = [];
|
|
40
|
-
for (const part of content) {
|
|
41
|
-
if (!isRecord(part)) {
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
const text = readString(part.text) ?? readString(part.content) ?? readString(part.value);
|
|
45
|
-
if (text) {
|
|
46
|
-
parts.push(text);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return parts.join('\n').trim();
|
|
50
|
-
}
|
|
51
|
-
if (isRecord(content)) {
|
|
52
|
-
const direct = readString(content.text) ?? readString(content.content);
|
|
53
|
-
if (direct) {
|
|
54
|
-
return direct;
|
|
55
|
-
}
|
|
56
|
-
if (Array.isArray(content.content)) {
|
|
57
|
-
return flattenMessageContent(content.content);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return '';
|
|
61
|
-
}
|
|
62
|
-
function extractResponsesMessageText(item) {
|
|
63
|
-
const parts = [];
|
|
64
|
-
const content = Array.isArray(item.content) ? item.content : [];
|
|
65
|
-
for (const part of content) {
|
|
66
|
-
if (!isRecord(part)) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const text = readString(part.text) ?? readString(part.content) ?? readString(part.value);
|
|
70
|
-
if (text) {
|
|
71
|
-
parts.push(text);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const outputText = readString(item.output_text);
|
|
75
|
-
if (outputText) {
|
|
76
|
-
parts.push(outputText);
|
|
77
|
-
}
|
|
78
|
-
return parts.join('\n').trim();
|
|
79
|
-
}
|
|
80
|
-
function readNumber(value) {
|
|
81
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
9
|
+
const readBoolean = (value) => {
|
|
10
|
+
if (typeof value === 'boolean') {
|
|
82
11
|
return value;
|
|
83
12
|
}
|
|
84
13
|
if (typeof value === 'string') {
|
|
85
|
-
const
|
|
86
|
-
if (!trimmed.length) {
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
const parsed = Number(trimmed);
|
|
90
|
-
if (Number.isFinite(parsed)) {
|
|
91
|
-
return parsed;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
function readBoolean(input, fallback) {
|
|
97
|
-
if (typeof input === 'boolean') {
|
|
98
|
-
return input;
|
|
99
|
-
}
|
|
100
|
-
if (typeof input === 'string') {
|
|
101
|
-
const normalized = input.trim().toLowerCase();
|
|
102
|
-
if (['true', '1', 'yes', 'on'].includes(normalized)) {
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
if (['false', '0', 'no', 'off'].includes(normalized)) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return fallback;
|
|
110
|
-
}
|
|
111
|
-
function readOptionalBoolean(input) {
|
|
112
|
-
if (typeof input === 'boolean') {
|
|
113
|
-
return input;
|
|
114
|
-
}
|
|
115
|
-
if (typeof input === 'string') {
|
|
116
|
-
const normalized = input.trim().toLowerCase();
|
|
14
|
+
const normalized = value.trim().toLowerCase();
|
|
117
15
|
if (['true', '1', 'yes', 'on'].includes(normalized)) {
|
|
118
16
|
return true;
|
|
119
17
|
}
|
|
@@ -122,776 +20,83 @@ function readOptionalBoolean(input) {
|
|
|
122
20
|
}
|
|
123
21
|
}
|
|
124
22
|
return undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (typeof
|
|
23
|
+
};
|
|
24
|
+
const readToolProtocol = (value) => {
|
|
25
|
+
if (typeof value !== 'string') {
|
|
128
26
|
return undefined;
|
|
129
27
|
}
|
|
130
|
-
const normalized =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
const encoder = encoding_for_model(normalized);
|
|
145
|
-
tokenEncoderCache.set(normalized, encoder);
|
|
146
|
-
return encoder;
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
// fall through to default encoder
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (!defaultTokenEncoder) {
|
|
153
|
-
defaultTokenEncoder = get_encoding(DEFAULT_TOKEN_ENCODING);
|
|
154
|
-
}
|
|
155
|
-
return defaultTokenEncoder;
|
|
156
|
-
}
|
|
157
|
-
function countTextTokens(text, modelHint) {
|
|
158
|
-
const trimmed = text.trim();
|
|
159
|
-
if (!trimmed.length) {
|
|
160
|
-
return 0;
|
|
161
|
-
}
|
|
162
|
-
try {
|
|
163
|
-
return getTokenEncoder(modelHint).encode(trimmed).length;
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
return 0;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
function resolveOptions(adapterContext, config) {
|
|
170
|
-
const context = (adapterContext ?? {});
|
|
171
|
-
const deepseekNode = isRecord(context.deepseek)
|
|
172
|
-
? context.deepseek
|
|
173
|
-
: isRecord(context.__rt) && isRecord(context.__rt.deepseek)
|
|
174
|
-
? context.__rt.deepseek
|
|
175
|
-
: undefined;
|
|
176
|
-
if (!deepseekNode) {
|
|
177
|
-
const toolProtocolOverride = readToolProtocol(config?.toolProtocol);
|
|
178
|
-
return {
|
|
179
|
-
...DEFAULT_OPTIONS,
|
|
180
|
-
strictToolRequired: readOptionalBoolean(config?.strictToolRequired) ?? DEFAULT_OPTIONS.strictToolRequired,
|
|
181
|
-
textNormalizer: config?.textNormalizer,
|
|
182
|
-
toolProtocol: toolProtocolOverride ?? DEFAULT_OPTIONS.toolProtocol
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
const strictOverride = readOptionalBoolean(config?.strictToolRequired);
|
|
186
|
-
const toolProtocolOverride = readToolProtocol(config?.toolProtocol);
|
|
187
|
-
const legacyFallback = readOptionalBoolean(deepseekNode.textToolFallback);
|
|
188
|
-
const toolProtocol = toolProtocolOverride ??
|
|
189
|
-
readToolProtocol(deepseekNode.toolProtocol) ??
|
|
190
|
-
(legacyFallback !== undefined ? (legacyFallback ? 'text' : 'native') : undefined);
|
|
28
|
+
const normalized = value.trim().toLowerCase();
|
|
29
|
+
return normalized === 'native' || normalized === 'text' ? normalized : undefined;
|
|
30
|
+
};
|
|
31
|
+
function resolveDeepseekNode(adapterContext, config) {
|
|
32
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
33
|
+
const baseNode = isRecord(nativeContext.deepseek) ? nativeContext.deepseek : {};
|
|
34
|
+
const configProtocol = readToolProtocol(config?.toolProtocol);
|
|
35
|
+
const baseProtocol = readToolProtocol(baseNode.toolProtocol);
|
|
36
|
+
const baseFallback = readBoolean(baseNode.textToolFallback);
|
|
37
|
+
const protocol = configProtocol ??
|
|
38
|
+
baseProtocol ??
|
|
39
|
+
(baseFallback === undefined ? undefined : baseFallback ? 'text' : 'native');
|
|
191
40
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
: undefined,
|
|
197
|
-
toolProtocol
|
|
41
|
+
...baseNode,
|
|
42
|
+
strictToolRequired: config?.strictToolRequired ?? readBoolean(baseNode.strictToolRequired) ?? true,
|
|
43
|
+
textToolFallback: protocol ? protocol === 'text' : baseFallback ?? true,
|
|
44
|
+
...(protocol ? { toolProtocol: protocol } : {})
|
|
198
45
|
};
|
|
199
46
|
}
|
|
200
|
-
function
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
];
|
|
214
|
-
for (const candidate of candidates) {
|
|
215
|
-
const value = readNumber(candidate);
|
|
216
|
-
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
217
|
-
return Math.max(1, Math.round(value));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return undefined;
|
|
221
|
-
}
|
|
222
|
-
function resolveModelHint(root, captured, adapterContext) {
|
|
223
|
-
return (readString(root.model) ??
|
|
224
|
-
readString(captured?.model) ??
|
|
225
|
-
readString(adapterContext?.modelId) ??
|
|
226
|
-
readString(adapterContext?.clientModelId) ??
|
|
227
|
-
readString(adapterContext?.originalModelId));
|
|
228
|
-
}
|
|
229
|
-
function looksLikeKnownProviderResponseShape(value) {
|
|
230
|
-
if (!isRecord(value))
|
|
231
|
-
return false;
|
|
232
|
-
if (Array.isArray(value.choices))
|
|
233
|
-
return true;
|
|
234
|
-
if (Array.isArray(value.output) || value.object === 'response')
|
|
235
|
-
return true;
|
|
236
|
-
if (typeof value.type === 'string' && String(value.type).toLowerCase() === 'message' && Array.isArray(value.content)) {
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
if (Array.isArray(value.candidates))
|
|
240
|
-
return true;
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
function tryUnwrapKnownShape(value, depth) {
|
|
244
|
-
if (depth < 0) {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
if (looksLikeKnownProviderResponseShape(value)) {
|
|
248
|
-
return value;
|
|
249
|
-
}
|
|
250
|
-
if (!isRecord(value)) {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
for (const key of ['data', 'body', 'response', 'payload', 'result', 'biz_data']) {
|
|
254
|
-
const nested = value[key];
|
|
255
|
-
const unwrapped = tryUnwrapKnownShape(nested, depth - 1);
|
|
256
|
-
if (unwrapped) {
|
|
257
|
-
return unwrapped;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return null;
|
|
261
|
-
}
|
|
262
|
-
function isLikelyRateLimitMessage(message) {
|
|
263
|
-
return /(rate.?limit|quota|too many|exceed|exceeded|limit reached|message count)/i.test(message);
|
|
264
|
-
}
|
|
265
|
-
function throwDeepSeekBusinessEnvelopeError(root, adapterContext) {
|
|
266
|
-
const dataNode = isRecord(root.data) ? root.data : undefined;
|
|
267
|
-
const upstreamCode = readNumber(root.code);
|
|
268
|
-
const bizCode = readNumber(dataNode?.biz_code);
|
|
269
|
-
const bizMsg = readString(dataNode?.biz_msg);
|
|
270
|
-
const topMsg = readString(root.msg);
|
|
271
|
-
const message = bizMsg ?? topMsg ?? 'DeepSeek returned a non-chat business envelope';
|
|
272
|
-
const error = new Error(`[deepseek-web] upstream business error: ${message}`);
|
|
273
|
-
error.code = 'DEEPSEEK_BIZ_ERROR';
|
|
274
|
-
if ((typeof bizCode === 'number' && bizCode === 3) || isLikelyRateLimitMessage(message)) {
|
|
275
|
-
error.statusCode = 429;
|
|
276
|
-
}
|
|
277
|
-
error.details = {
|
|
278
|
-
requestId: readString(root.request_id) ??
|
|
279
|
-
readString(adapterContext?.requestId),
|
|
280
|
-
upstreamCode,
|
|
281
|
-
bizCode,
|
|
282
|
-
bizMsg,
|
|
283
|
-
upstreamMsg: topMsg,
|
|
284
|
-
payloadKeys: Object.keys(root).slice(0, 20),
|
|
285
|
-
phase: 'chat_process.resp.stage3.compat'
|
|
47
|
+
function buildCompatInput(payload, adapterContext, config) {
|
|
48
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
49
|
+
const normalizedContext = {
|
|
50
|
+
...nativeContext,
|
|
51
|
+
compatibilityProfile: PROFILE,
|
|
52
|
+
providerProtocol: nativeContext.providerProtocol ?? adapterContext?.providerProtocol ?? DEFAULT_PROVIDER_PROTOCOL,
|
|
53
|
+
entryEndpoint: nativeContext.entryEndpoint ?? adapterContext?.entryEndpoint ?? DEFAULT_ENTRY_ENDPOINT,
|
|
54
|
+
deepseek: resolveDeepseekNode(adapterContext, config)
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
payload,
|
|
58
|
+
adapterContext: normalizedContext,
|
|
59
|
+
explicitProfile: PROFILE
|
|
286
60
|
};
|
|
287
|
-
throw error;
|
|
288
|
-
}
|
|
289
|
-
function normalizeDeepSeekBusinessEnvelope(root, adapterContext) {
|
|
290
|
-
if (looksLikeKnownProviderResponseShape(root)) {
|
|
291
|
-
return root;
|
|
292
|
-
}
|
|
293
|
-
const hasEnvelopeKeys = Object.prototype.hasOwnProperty.call(root, 'code')
|
|
294
|
-
&& Object.prototype.hasOwnProperty.call(root, 'data');
|
|
295
|
-
if (!hasEnvelopeKeys) {
|
|
296
|
-
return root;
|
|
297
|
-
}
|
|
298
|
-
const unwrapped = tryUnwrapKnownShape(root.data, 6);
|
|
299
|
-
if (unwrapped) {
|
|
300
|
-
return unwrapped;
|
|
301
|
-
}
|
|
302
|
-
const dataNode = isRecord(root.data) ? root.data : undefined;
|
|
303
|
-
const upstreamCode = readNumber(root.code);
|
|
304
|
-
const bizCode = readNumber(dataNode?.biz_code);
|
|
305
|
-
const bizMsg = readString(dataNode?.biz_msg);
|
|
306
|
-
const topMsg = readString(root.msg);
|
|
307
|
-
if ((typeof upstreamCode === 'number' && upstreamCode !== 0) ||
|
|
308
|
-
(typeof bizCode === 'number' && bizCode !== 0) ||
|
|
309
|
-
Boolean(bizMsg) ||
|
|
310
|
-
Boolean(topMsg)) {
|
|
311
|
-
throwDeepSeekBusinessEnvelopeError(root, adapterContext);
|
|
312
|
-
}
|
|
313
|
-
return root;
|
|
314
|
-
}
|
|
315
|
-
function resolveRequestedToolNames(captured) {
|
|
316
|
-
const names = new Set();
|
|
317
|
-
if (!captured) {
|
|
318
|
-
return names;
|
|
319
|
-
}
|
|
320
|
-
const toolsRaw = Array.isArray(captured.tools) ? captured.tools : [];
|
|
321
|
-
for (const tool of toolsRaw) {
|
|
322
|
-
if (!isRecord(tool)) {
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
const fnNode = isRecord(tool.function) ? tool.function : undefined;
|
|
326
|
-
const name = readString(fnNode?.name) ?? readString(tool.name);
|
|
327
|
-
if (name) {
|
|
328
|
-
names.add(name);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
const toolChoice = captured.tool_choice;
|
|
332
|
-
if (isRecord(toolChoice)) {
|
|
333
|
-
const type = readString(toolChoice.type)?.toLowerCase();
|
|
334
|
-
if (type === 'function') {
|
|
335
|
-
const fnNode = isRecord(toolChoice.function)
|
|
336
|
-
? toolChoice.function
|
|
337
|
-
: undefined;
|
|
338
|
-
const forcedName = readString(fnNode?.name) ?? readString(toolChoice.name);
|
|
339
|
-
if (forcedName) {
|
|
340
|
-
names.add(forcedName);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return names;
|
|
345
|
-
}
|
|
346
|
-
function isToolChoiceRequired(captured) {
|
|
347
|
-
if (!captured) {
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
const toolChoice = captured.tool_choice;
|
|
351
|
-
if (typeof toolChoice === 'string') {
|
|
352
|
-
const normalized = toolChoice.trim().toLowerCase();
|
|
353
|
-
if (normalized === 'required') {
|
|
354
|
-
return true;
|
|
355
|
-
}
|
|
356
|
-
if (normalized === 'none' || normalized === 'auto') {
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
if (isRecord(toolChoice)) {
|
|
361
|
-
const type = readString(toolChoice.type)?.toLowerCase();
|
|
362
|
-
if (type === 'function') {
|
|
363
|
-
return true;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
function tryParseJsonText(value) {
|
|
369
|
-
try {
|
|
370
|
-
return JSON.parse(value);
|
|
371
|
-
}
|
|
372
|
-
catch {
|
|
373
|
-
// Best-effort repair: some upstream payloads place raw newlines/tabs inside JSON strings.
|
|
374
|
-
let out = '';
|
|
375
|
-
let changed = false;
|
|
376
|
-
let inString = false;
|
|
377
|
-
let escaped = false;
|
|
378
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
379
|
-
const ch = value[i] ?? '';
|
|
380
|
-
if (inString) {
|
|
381
|
-
if (escaped) {
|
|
382
|
-
out += ch;
|
|
383
|
-
escaped = false;
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (ch === '\\') {
|
|
387
|
-
out += ch;
|
|
388
|
-
escaped = true;
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
if (ch === '"') {
|
|
392
|
-
out += ch;
|
|
393
|
-
inString = false;
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
if (ch === '\n') {
|
|
397
|
-
out += '\\n';
|
|
398
|
-
changed = true;
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
if (ch === '\r') {
|
|
402
|
-
out += '\\n';
|
|
403
|
-
changed = true;
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
|
-
if (ch === '\t') {
|
|
407
|
-
out += '\\t';
|
|
408
|
-
changed = true;
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
const code = ch.charCodeAt(0);
|
|
412
|
-
if (code >= 0 && code < 0x20) {
|
|
413
|
-
out += `\\u${code.toString(16).padStart(4, '0')}`;
|
|
414
|
-
changed = true;
|
|
415
|
-
continue;
|
|
416
|
-
}
|
|
417
|
-
out += ch;
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
if (ch === '"') {
|
|
421
|
-
inString = true;
|
|
422
|
-
}
|
|
423
|
-
out += ch;
|
|
424
|
-
}
|
|
425
|
-
if (!changed) {
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
try {
|
|
429
|
-
return JSON.parse(out);
|
|
430
|
-
}
|
|
431
|
-
catch {
|
|
432
|
-
return null;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
61
|
}
|
|
436
|
-
function
|
|
437
|
-
|
|
438
|
-
const trimmed = value.trim();
|
|
439
|
-
if (!trimmed.length) {
|
|
440
|
-
return JSON.stringify({});
|
|
441
|
-
}
|
|
442
|
-
const parsed = tryParseJsonText(trimmed);
|
|
443
|
-
if (parsed !== null) {
|
|
444
|
-
return JSON.stringify(parsed);
|
|
445
|
-
}
|
|
446
|
-
if (toolName) {
|
|
447
|
-
const validation = validateToolCall(toolName, trimmed);
|
|
448
|
-
if (validation.ok && typeof validation.normalizedArgs === 'string' && validation.normalizedArgs.trim().length > 0) {
|
|
449
|
-
return validation.normalizedArgs;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return null;
|
|
453
|
-
}
|
|
454
|
-
if (value === undefined || value === null) {
|
|
455
|
-
const empty = JSON.stringify({});
|
|
456
|
-
if (!toolName) {
|
|
457
|
-
return empty;
|
|
458
|
-
}
|
|
459
|
-
const validation = validateToolCall(toolName, empty);
|
|
460
|
-
if (validation.ok && typeof validation.normalizedArgs === 'string' && validation.normalizedArgs.trim().length > 0) {
|
|
461
|
-
return validation.normalizedArgs;
|
|
462
|
-
}
|
|
463
|
-
return empty;
|
|
464
|
-
}
|
|
62
|
+
function parseCompatOutput(raw) {
|
|
63
|
+
let parsed;
|
|
465
64
|
try {
|
|
466
|
-
|
|
467
|
-
if (!toolName) {
|
|
468
|
-
return serialized;
|
|
469
|
-
}
|
|
470
|
-
const validation = validateToolCall(toolName, serialized);
|
|
471
|
-
if (validation.ok && typeof validation.normalizedArgs === 'string' && validation.normalizedArgs.trim().length > 0) {
|
|
472
|
-
return validation.normalizedArgs;
|
|
473
|
-
}
|
|
474
|
-
return serialized;
|
|
65
|
+
parsed = JSON.parse(raw);
|
|
475
66
|
}
|
|
476
67
|
catch {
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
function hasAllowedToolName(allowed, candidate) {
|
|
481
|
-
if (allowed.has(candidate)) {
|
|
482
|
-
return true;
|
|
483
|
-
}
|
|
484
|
-
const lowered = candidate.toLowerCase();
|
|
485
|
-
for (const name of allowed) {
|
|
486
|
-
if (name.toLowerCase() === lowered) {
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
function findAllowedToolNameCaseInsensitive(allowed, candidate) {
|
|
493
|
-
if (allowed.has(candidate)) {
|
|
494
|
-
return candidate;
|
|
495
|
-
}
|
|
496
|
-
const lowered = candidate.toLowerCase();
|
|
497
|
-
for (const name of allowed) {
|
|
498
|
-
if (name.toLowerCase() === lowered) {
|
|
499
|
-
return name;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return undefined;
|
|
503
|
-
}
|
|
504
|
-
function resolveAllowedShellToolName(allowedToolNames) {
|
|
505
|
-
for (const candidate of SHELL_ALLOWED_TOOL_CANDIDATES) {
|
|
506
|
-
const resolved = findAllowedToolNameCaseInsensitive(allowedToolNames, candidate);
|
|
507
|
-
if (resolved) {
|
|
508
|
-
return resolved;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
return undefined;
|
|
512
|
-
}
|
|
513
|
-
function resolveToolNameForCompat(rawName, allowedToolNames) {
|
|
514
|
-
const normalizedRawName = rawName.toLowerCase();
|
|
515
|
-
const alias = SHELL_TOOL_NAME_ALIASES.get(normalizedRawName);
|
|
516
|
-
const shellLike = SHELL_LIKE_TOOL_NAMES.has(normalizedRawName) || alias === 'exec_command';
|
|
517
|
-
if (allowedToolNames.size > 0 && shellLike) {
|
|
518
|
-
const allowedShell = resolveAllowedShellToolName(allowedToolNames);
|
|
519
|
-
if (allowedShell) {
|
|
520
|
-
return allowedShell;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
if (!alias) {
|
|
524
|
-
return findAllowedToolNameCaseInsensitive(allowedToolNames, rawName) ?? rawName;
|
|
68
|
+
throw makeNativeRequiredError(CAPABILITY, 'invalid payload');
|
|
525
69
|
}
|
|
526
|
-
if (
|
|
527
|
-
|
|
70
|
+
if (!isRecord(parsed) || !isRecord(parsed.payload) || typeof parsed.nativeApplied !== 'boolean') {
|
|
71
|
+
throw makeNativeRequiredError(CAPABILITY, 'invalid payload');
|
|
528
72
|
}
|
|
529
|
-
|
|
530
|
-
if (allowedAlias) {
|
|
531
|
-
return allowedAlias;
|
|
532
|
-
}
|
|
533
|
-
const allowedRaw = findAllowedToolNameCaseInsensitive(allowedToolNames, rawName);
|
|
534
|
-
if (allowedRaw) {
|
|
535
|
-
return allowedRaw;
|
|
536
|
-
}
|
|
537
|
-
return alias;
|
|
73
|
+
return parsed;
|
|
538
74
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
readString(args.command) ??
|
|
543
|
-
readString(args.script) ??
|
|
544
|
-
(() => {
|
|
545
|
-
const arr = Array.isArray(args.command) ? args.command : Array.isArray(args.cmd) ? args.cmd : undefined;
|
|
546
|
-
if (!arr)
|
|
547
|
-
return undefined;
|
|
548
|
-
const tokens = arr.map((item) => (item == null ? '' : String(item).trim())).filter((item) => item.length > 0);
|
|
549
|
-
return tokens.length ? tokens.join(' ') : undefined;
|
|
550
|
-
})();
|
|
551
|
-
if (direct) {
|
|
552
|
-
return direct;
|
|
553
|
-
}
|
|
554
|
-
if (!input) {
|
|
555
|
-
return undefined;
|
|
75
|
+
function callDeepSeekWebResponseCompat(input) {
|
|
76
|
+
if (isNativeDisabledByEnv()) {
|
|
77
|
+
throw makeNativeRequiredError(CAPABILITY, 'native disabled');
|
|
556
78
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
(
|
|
561
|
-
const arr = Array.isArray(input.command) ? input.command : Array.isArray(input.cmd) ? input.cmd : undefined;
|
|
562
|
-
if (!arr)
|
|
563
|
-
return undefined;
|
|
564
|
-
const tokens = arr.map((item) => (item == null ? '' : String(item).trim())).filter((item) => item.length > 0);
|
|
565
|
-
return tokens.length ? tokens.join(' ') : undefined;
|
|
566
|
-
})());
|
|
567
|
-
}
|
|
568
|
-
function readWorkdirFromShellArgs(args) {
|
|
569
|
-
const input = isRecord(args.input) ? args.input : undefined;
|
|
570
|
-
return (readString(args.workdir) ??
|
|
571
|
-
readString(args.cwd) ??
|
|
572
|
-
readString(args.workDir) ??
|
|
573
|
-
readString(input?.workdir) ??
|
|
574
|
-
readString(input?.cwd));
|
|
575
|
-
}
|
|
576
|
-
function normalizeShellLikeArguments(argsString, argsSource) {
|
|
577
|
-
const parsed = tryParseJsonText(argsString);
|
|
578
|
-
const args = isRecord(parsed) ? { ...parsed } : isRecord(argsSource) ? { ...argsSource } : {};
|
|
579
|
-
const cmd = readCommandFromShellArgs(args);
|
|
580
|
-
if (!cmd) {
|
|
581
|
-
return argsString;
|
|
582
|
-
}
|
|
583
|
-
const workdir = readWorkdirFromShellArgs(args);
|
|
584
|
-
const next = {
|
|
585
|
-
...args,
|
|
586
|
-
cmd,
|
|
587
|
-
command: cmd
|
|
588
|
-
};
|
|
589
|
-
if (workdir) {
|
|
590
|
-
next.workdir = workdir;
|
|
591
|
-
}
|
|
592
|
-
if (Object.prototype.hasOwnProperty.call(next, 'toon')) {
|
|
593
|
-
delete next.toon;
|
|
79
|
+
const binding = loadNativeRouterHotpathBindingForInternalUse();
|
|
80
|
+
const fn = binding?.[CAPABILITY];
|
|
81
|
+
if (typeof fn !== 'function') {
|
|
82
|
+
throw makeNativeRequiredError(CAPABILITY);
|
|
594
83
|
}
|
|
84
|
+
let inputJson;
|
|
595
85
|
try {
|
|
596
|
-
|
|
86
|
+
inputJson = JSON.stringify(input);
|
|
597
87
|
}
|
|
598
88
|
catch {
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
function coerceToolCallsValue(toolCallsRaw) {
|
|
603
|
-
if (Array.isArray(toolCallsRaw)) {
|
|
604
|
-
return toolCallsRaw;
|
|
605
|
-
}
|
|
606
|
-
if (typeof toolCallsRaw !== 'string') {
|
|
607
|
-
return [];
|
|
608
|
-
}
|
|
609
|
-
const parsed = tryParseJsonText(toolCallsRaw.trim());
|
|
610
|
-
if (Array.isArray(parsed)) {
|
|
611
|
-
return parsed;
|
|
612
|
-
}
|
|
613
|
-
if (isRecord(parsed) && Array.isArray(parsed.tool_calls)) {
|
|
614
|
-
return parsed.tool_calls;
|
|
615
|
-
}
|
|
616
|
-
return [];
|
|
617
|
-
}
|
|
618
|
-
function normalizeToolCallEntry(entry, allowedToolNames, callIdPrefix, index) {
|
|
619
|
-
const fn = isRecord(entry.function) ? entry.function : undefined;
|
|
620
|
-
const rawName = readString(entry.name) ?? readString(fn?.name);
|
|
621
|
-
if (!rawName) {
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
const name = resolveToolNameForCompat(rawName, allowedToolNames);
|
|
625
|
-
const argsSource = entry.input !== undefined
|
|
626
|
-
? entry.input
|
|
627
|
-
: entry.arguments !== undefined
|
|
628
|
-
? entry.arguments
|
|
629
|
-
: fn?.arguments;
|
|
630
|
-
const normalizedArgs = normalizeToolArguments(argsSource, name);
|
|
631
|
-
if (!normalizedArgs) {
|
|
632
|
-
return null;
|
|
633
|
-
}
|
|
634
|
-
const finalArgs = SHELL_LIKE_TOOL_NAMES.has(name.toLowerCase())
|
|
635
|
-
? normalizeShellLikeArguments(normalizedArgs, argsSource)
|
|
636
|
-
: normalizedArgs;
|
|
637
|
-
const id = readString(entry.id) ??
|
|
638
|
-
readString(entry.call_id) ??
|
|
639
|
-
`${callIdPrefix}_${index + 1}`;
|
|
640
|
-
return {
|
|
641
|
-
id,
|
|
642
|
-
type: 'function',
|
|
643
|
-
function: {
|
|
644
|
-
name,
|
|
645
|
-
arguments: finalArgs
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
function normalizeToolCalls(toolCallsRaw, allowedToolNames, callIdPrefix) {
|
|
650
|
-
const toolCalls = coerceToolCallsValue(toolCallsRaw);
|
|
651
|
-
const normalized = [];
|
|
652
|
-
for (const [index, entry] of toolCalls.entries()) {
|
|
653
|
-
if (!isRecord(entry)) {
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
const call = normalizeToolCallEntry(entry, allowedToolNames, callIdPrefix, index);
|
|
657
|
-
if (call) {
|
|
658
|
-
normalized.push(call);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
return normalized;
|
|
662
|
-
}
|
|
663
|
-
function normalizeMessageToolCalls(message, allowedToolNames, callIdPrefix) {
|
|
664
|
-
const nextCalls = normalizeToolCalls(message.tool_calls, allowedToolNames, callIdPrefix);
|
|
665
|
-
if (!nextCalls.length) {
|
|
666
|
-
delete message.tool_calls;
|
|
667
|
-
return false;
|
|
668
|
-
}
|
|
669
|
-
message.tool_calls = nextCalls;
|
|
670
|
-
message.content = null;
|
|
671
|
-
return true;
|
|
672
|
-
}
|
|
673
|
-
function normalizeUsageSnapshot(raw) {
|
|
674
|
-
if (!isRecord(raw)) {
|
|
675
|
-
return {};
|
|
676
|
-
}
|
|
677
|
-
const prompt = readNumber(raw.prompt_tokens) ?? readNumber(raw.input_tokens);
|
|
678
|
-
const completion = readNumber(raw.completion_tokens) ?? readNumber(raw.output_tokens);
|
|
679
|
-
const total = readNumber(raw.total_tokens);
|
|
680
|
-
return {
|
|
681
|
-
...(typeof prompt === 'number' ? { prompt: Math.max(0, Math.round(prompt)) } : {}),
|
|
682
|
-
...(typeof completion === 'number' ? { completion: Math.max(0, Math.round(completion)) } : {}),
|
|
683
|
-
...(typeof total === 'number' ? { total: Math.max(0, Math.round(total)) } : {})
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
function estimateCompletionTokensFromChoices(choices, modelHint) {
|
|
687
|
-
const segments = [];
|
|
688
|
-
for (const choice of choices) {
|
|
689
|
-
if (!isRecord(choice)) {
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
const message = isRecord(choice.message) ? choice.message : undefined;
|
|
693
|
-
if (!message) {
|
|
694
|
-
continue;
|
|
695
|
-
}
|
|
696
|
-
const content = message.content;
|
|
697
|
-
if (typeof content === 'string' && content.trim().length) {
|
|
698
|
-
segments.push(content);
|
|
699
|
-
}
|
|
700
|
-
else if (Array.isArray(content)) {
|
|
701
|
-
for (const part of content) {
|
|
702
|
-
if (!isRecord(part)) {
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
705
|
-
const text = readString(part.text);
|
|
706
|
-
if (text) {
|
|
707
|
-
segments.push(text);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
if (Array.isArray(message.tool_calls) && message.tool_calls.length) {
|
|
712
|
-
try {
|
|
713
|
-
segments.push(JSON.stringify(message.tool_calls));
|
|
714
|
-
}
|
|
715
|
-
catch {
|
|
716
|
-
// ignore serialization failure
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
if (!segments.length) {
|
|
721
|
-
return 0;
|
|
722
|
-
}
|
|
723
|
-
const joined = segments.join('\n');
|
|
724
|
-
return countTextTokens(joined, modelHint);
|
|
725
|
-
}
|
|
726
|
-
function applyUsageEstimate(root, choices, adapterContext, captured) {
|
|
727
|
-
const current = normalizeUsageSnapshot(root.usage);
|
|
728
|
-
const prompt = current.prompt ?? resolveEstimatedInputTokens(adapterContext);
|
|
729
|
-
const completion = current.completion ??
|
|
730
|
-
estimateCompletionTokensFromChoices(choices, resolveModelHint(root, captured, adapterContext));
|
|
731
|
-
const total = current.total ??
|
|
732
|
-
(typeof prompt === 'number' && typeof completion === 'number' ? prompt + completion : undefined);
|
|
733
|
-
if (typeof prompt !== 'number' &&
|
|
734
|
-
typeof completion !== 'number' &&
|
|
735
|
-
typeof total !== 'number') {
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
const usage = isRecord(root.usage) ? { ...root.usage } : {};
|
|
739
|
-
if (typeof prompt === 'number') {
|
|
740
|
-
usage.prompt_tokens = prompt;
|
|
741
|
-
usage.input_tokens = prompt;
|
|
742
|
-
}
|
|
743
|
-
if (typeof completion === 'number') {
|
|
744
|
-
usage.completion_tokens = completion;
|
|
745
|
-
usage.output_tokens = completion;
|
|
746
|
-
}
|
|
747
|
-
if (typeof total === 'number') {
|
|
748
|
-
usage.total_tokens = total;
|
|
89
|
+
throw makeNativeRequiredError(CAPABILITY, 'json stringify failed');
|
|
749
90
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const message = isRecord(choice.message) ? choice.message : undefined;
|
|
754
|
-
if (!message) {
|
|
755
|
-
return { hasNative: false, hasText: false, harvestedFunctionResults: false };
|
|
756
|
-
}
|
|
757
|
-
const native = normalizeMessageToolCalls(message, allowedToolNames, callIdPrefix);
|
|
758
|
-
if (native) {
|
|
759
|
-
const finish = readString(choice.finish_reason)?.toLowerCase();
|
|
760
|
-
if (!finish || finish === 'stop') {
|
|
761
|
-
choice.finish_reason = 'tool_calls';
|
|
762
|
-
}
|
|
763
|
-
return { hasNative: true, hasText: false, harvestedFunctionResults: false };
|
|
91
|
+
const raw = fn(inputJson);
|
|
92
|
+
if (typeof raw !== 'string' || !raw) {
|
|
93
|
+
throw makeNativeRequiredError(CAPABILITY, 'empty result');
|
|
764
94
|
}
|
|
765
|
-
|
|
766
|
-
const contentText = flattenMessageContent(message.content);
|
|
767
|
-
if (contentText && (contentText.startsWith('{') || contentText.startsWith('['))) {
|
|
768
|
-
message.tool_calls = contentText;
|
|
769
|
-
}
|
|
770
|
-
const text = normalizeMessageToolCalls(message, allowedToolNames, callIdPrefix);
|
|
771
|
-
if (text) {
|
|
772
|
-
const finish = readString(choice.finish_reason)?.toLowerCase();
|
|
773
|
-
if (!finish || finish === 'stop') {
|
|
774
|
-
choice.finish_reason = 'tool_calls';
|
|
775
|
-
}
|
|
776
|
-
return { hasNative: false, hasText: true, harvestedFunctionResults: false };
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
return {
|
|
780
|
-
hasNative: false,
|
|
781
|
-
hasText: false,
|
|
782
|
-
harvestedFunctionResults: false
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
function harvestResponsesTextToolCalls(root, options, allowedToolNames) {
|
|
786
|
-
if (options.toolProtocol !== 'text') {
|
|
787
|
-
return { harvested: false };
|
|
788
|
-
}
|
|
789
|
-
const output = Array.isArray(root.output) ? root.output : [];
|
|
790
|
-
if (!output.length) {
|
|
791
|
-
return { harvested: false };
|
|
792
|
-
}
|
|
793
|
-
let harvested = false;
|
|
794
|
-
const nextOutput = [];
|
|
795
|
-
let callIndex = 0;
|
|
796
|
-
for (const item of output) {
|
|
797
|
-
if (!isRecord(item)) {
|
|
798
|
-
nextOutput.push(item);
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
const type = readString(item.type)?.toLowerCase() ?? '';
|
|
802
|
-
if (type !== 'message') {
|
|
803
|
-
nextOutput.push(item);
|
|
804
|
-
continue;
|
|
805
|
-
}
|
|
806
|
-
const role = readString(item.role)?.toLowerCase() ?? 'assistant';
|
|
807
|
-
if (role !== 'assistant') {
|
|
808
|
-
nextOutput.push(item);
|
|
809
|
-
continue;
|
|
810
|
-
}
|
|
811
|
-
const text = extractResponsesMessageText(item);
|
|
812
|
-
if (!text || (!text.trim().startsWith('{') && !text.trim().startsWith('['))) {
|
|
813
|
-
nextOutput.push(item);
|
|
814
|
-
continue;
|
|
815
|
-
}
|
|
816
|
-
const normalizedCalls = normalizeToolCalls(text.trim(), allowedToolNames, 'deepseek_resp_call');
|
|
817
|
-
if (!normalizedCalls.length) {
|
|
818
|
-
nextOutput.push(item);
|
|
819
|
-
continue;
|
|
820
|
-
}
|
|
821
|
-
harvested = true;
|
|
822
|
-
for (const call of normalizedCalls) {
|
|
823
|
-
callIndex += 1;
|
|
824
|
-
const callId = readString(call.id) ?? `deepseek_resp_call_${callIndex}`;
|
|
825
|
-
const itemId = normalizeFunctionCallId({ callId, fallback: `fc_${callId}` });
|
|
826
|
-
nextOutput.push({
|
|
827
|
-
type: 'function_call',
|
|
828
|
-
id: itemId,
|
|
829
|
-
call_id: callId,
|
|
830
|
-
name: call.function.name,
|
|
831
|
-
arguments: call.function.arguments
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
if (harvested) {
|
|
836
|
-
root.output = nextOutput;
|
|
837
|
-
}
|
|
838
|
-
return { harvested };
|
|
839
|
-
}
|
|
840
|
-
function writeCompatState(root, state, source, harvestedFunctionResults) {
|
|
841
|
-
const metadata = isRecord(root.metadata) ? root.metadata : {};
|
|
842
|
-
metadata.deepseek = {
|
|
843
|
-
...(isRecord(metadata.deepseek) ? metadata.deepseek : {}),
|
|
844
|
-
toolCallState: state,
|
|
845
|
-
toolCallSource: source,
|
|
846
|
-
...(harvestedFunctionResults ? { functionResultsTextHarvested: true } : {})
|
|
847
|
-
};
|
|
848
|
-
root.metadata = metadata;
|
|
95
|
+
return parseCompatOutput(raw);
|
|
849
96
|
}
|
|
850
97
|
export function applyDeepSeekWebResponseTransform(payload, adapterContext, config) {
|
|
851
98
|
if (!payload || typeof payload !== 'object') {
|
|
852
99
|
return payload;
|
|
853
100
|
}
|
|
854
|
-
|
|
855
|
-
const root = normalizeDeepSeekBusinessEnvelope(cloned, adapterContext);
|
|
856
|
-
const options = resolveOptions(adapterContext, config);
|
|
857
|
-
const captured = resolveCapturedRequest(adapterContext);
|
|
858
|
-
const allowedToolNames = resolveRequestedToolNames(captured);
|
|
859
|
-
const toolChoiceRequired = isToolChoiceRequired(captured);
|
|
860
|
-
const choices = Array.isArray(root.choices) ? root.choices : [];
|
|
861
|
-
let hasNative = false;
|
|
862
|
-
let hasText = false;
|
|
863
|
-
let harvestedFunctionResults = false;
|
|
864
|
-
for (const [index, choice] of choices.entries()) {
|
|
865
|
-
if (!isRecord(choice)) {
|
|
866
|
-
continue;
|
|
867
|
-
}
|
|
868
|
-
const result = normalizeChoice(choice, options, allowedToolNames, `deepseek_call_${index + 1}`);
|
|
869
|
-
hasNative = hasNative || result.hasNative;
|
|
870
|
-
hasText = hasText || result.hasText;
|
|
871
|
-
harvestedFunctionResults = harvestedFunctionResults || result.harvestedFunctionResults;
|
|
872
|
-
}
|
|
873
|
-
if (!choices.length) {
|
|
874
|
-
const harvested = harvestResponsesTextToolCalls(root, options, allowedToolNames);
|
|
875
|
-
hasText = hasText || harvested.harvested;
|
|
876
|
-
}
|
|
877
|
-
const state = hasNative
|
|
878
|
-
? 'native_tool_calls'
|
|
879
|
-
: hasText
|
|
880
|
-
? 'text_tool_calls'
|
|
881
|
-
: 'no_tool_calls';
|
|
882
|
-
const source = hasNative ? 'native' : hasText ? 'text' : 'none';
|
|
883
|
-
writeCompatState(root, state, source, harvestedFunctionResults);
|
|
884
|
-
applyUsageEstimate(root, choices, adapterContext, captured);
|
|
885
|
-
if (toolChoiceRequired && options.strictToolRequired && !hasNative && !hasText) {
|
|
886
|
-
const error = new Error('DeepSeek tool_choice=required but no valid tool call was produced');
|
|
887
|
-
error.code = 'DEEPSEEK_TOOL_REQUIRED_MISSING';
|
|
888
|
-
error.details = {
|
|
889
|
-
requestId: readString(adapterContext?.requestId),
|
|
890
|
-
phase: 'chat_process.resp.stage3.compat',
|
|
891
|
-
state,
|
|
892
|
-
strictToolRequired: options.strictToolRequired
|
|
893
|
-
};
|
|
894
|
-
throw error;
|
|
895
|
-
}
|
|
896
|
-
return root;
|
|
101
|
+
return callDeepSeekWebResponseCompat(buildCompatInput(payload, adapterContext, config)).payload;
|
|
897
102
|
}
|