@jsonstudio/llms 0.6.3409 → 0.6.3541
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 +117 -855
- 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/quota/quota-state.js +29 -7
- package/dist/quota/types.d.ts +1 -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/servertool/handlers/followup-request-builder.js +12 -2
- 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,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
+
import { providerErrorCenter } from '../../../router/virtual-router/error-center.js';
|
|
5
|
+
const CAPABILITY = 'runRespInboundStage3CompatJson';
|
|
6
|
+
const PROFILE = 'chat:deepseek-web';
|
|
7
|
+
const DEFAULT_PROVIDER_PROTOCOL = 'openai-chat';
|
|
8
|
+
const DEFAULT_ENTRY_ENDPOINT = '/v1/chat/completions';
|
|
26
9
|
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)) {
|
|
10
|
+
const readBoolean = (value) => {
|
|
11
|
+
if (typeof value === 'boolean') {
|
|
82
12
|
return value;
|
|
83
13
|
}
|
|
84
14
|
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();
|
|
15
|
+
const normalized = value.trim().toLowerCase();
|
|
117
16
|
if (['true', '1', 'yes', 'on'].includes(normalized)) {
|
|
118
17
|
return true;
|
|
119
18
|
}
|
|
@@ -122,776 +21,139 @@ function readOptionalBoolean(input) {
|
|
|
122
21
|
}
|
|
123
22
|
}
|
|
124
23
|
return undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (typeof
|
|
24
|
+
};
|
|
25
|
+
const readToolProtocol = (value) => {
|
|
26
|
+
if (typeof value !== 'string') {
|
|
128
27
|
return undefined;
|
|
129
28
|
}
|
|
130
|
-
const normalized =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return cached;
|
|
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
|
|
29
|
+
const normalized = value.trim().toLowerCase();
|
|
30
|
+
return normalized === 'native' || normalized === 'text' ? normalized : undefined;
|
|
31
|
+
};
|
|
32
|
+
function buildRuntimeMetadata(adapterContext, payload, details) {
|
|
33
|
+
const contextRecord = adapterContext && typeof adapterContext === 'object'
|
|
34
|
+
? adapterContext
|
|
35
|
+
: undefined;
|
|
36
|
+
const runtime = {};
|
|
37
|
+
const assignString = (key, value) => {
|
|
38
|
+
if (typeof value === 'string' && value.trim()) {
|
|
39
|
+
runtime[key] = value.trim();
|
|
150
40
|
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
41
|
+
};
|
|
42
|
+
assignString('requestId', contextRecord?.requestId);
|
|
43
|
+
assignString('providerProtocol', contextRecord?.providerProtocol);
|
|
44
|
+
assignString('providerId', contextRecord?.providerId);
|
|
45
|
+
assignString('providerKey', contextRecord?.providerKey);
|
|
46
|
+
assignString('runtimeKey', contextRecord?.runtimeKey);
|
|
47
|
+
assignString('routeName', contextRecord?.routeId);
|
|
48
|
+
assignString('pipelineId', PROFILE);
|
|
49
|
+
if (payload && typeof payload === 'object') {
|
|
50
|
+
assignString('target', payload.model);
|
|
51
|
+
}
|
|
52
|
+
if (details && Object.keys(details).length > 0) {
|
|
53
|
+
runtime.details = details;
|
|
54
|
+
}
|
|
55
|
+
return runtime;
|
|
56
|
+
}
|
|
57
|
+
function emitCompatError(error, adapterContext, payload, details) {
|
|
58
|
+
providerErrorCenter.emit({
|
|
59
|
+
code: 'DEEPSEEK_WEB_COMPAT_ERROR',
|
|
60
|
+
message: error.message,
|
|
61
|
+
stage: 'compat:deepseek-web-response',
|
|
62
|
+
runtime: buildRuntimeMetadata(adapterContext, payload, details),
|
|
63
|
+
details: {
|
|
64
|
+
compatibilityProfile: PROFILE,
|
|
65
|
+
...(details ?? {})
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
throw error;
|
|
168
69
|
}
|
|
169
|
-
function
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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);
|
|
70
|
+
function resolveDeepseekNode(adapterContext, config) {
|
|
71
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
72
|
+
const baseNode = isRecord(nativeContext.deepseek) ? nativeContext.deepseek : {};
|
|
73
|
+
const configProtocol = readToolProtocol(config?.toolProtocol);
|
|
74
|
+
const baseProtocol = readToolProtocol(baseNode.toolProtocol);
|
|
75
|
+
const baseFallback = readBoolean(baseNode.textToolFallback);
|
|
76
|
+
const protocol = configProtocol ??
|
|
77
|
+
baseProtocol ??
|
|
78
|
+
(baseFallback === undefined ? undefined : baseFallback ? 'text' : 'native');
|
|
191
79
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
: undefined,
|
|
197
|
-
toolProtocol
|
|
80
|
+
...baseNode,
|
|
81
|
+
strictToolRequired: config?.strictToolRequired ?? readBoolean(baseNode.strictToolRequired) ?? true,
|
|
82
|
+
textToolFallback: protocol ? protocol === 'text' : baseFallback ?? true,
|
|
83
|
+
...(protocol ? { toolProtocol: protocol } : {})
|
|
198
84
|
};
|
|
199
85
|
}
|
|
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'
|
|
86
|
+
function buildCompatInput(payload, adapterContext, config) {
|
|
87
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
88
|
+
const normalizedContext = {
|
|
89
|
+
...nativeContext,
|
|
90
|
+
compatibilityProfile: PROFILE,
|
|
91
|
+
providerProtocol: nativeContext.providerProtocol ?? adapterContext?.providerProtocol ?? DEFAULT_PROVIDER_PROTOCOL,
|
|
92
|
+
entryEndpoint: nativeContext.entryEndpoint ?? adapterContext?.entryEndpoint ?? DEFAULT_ENTRY_ENDPOINT,
|
|
93
|
+
deepseek: resolveDeepseekNode(adapterContext, config)
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
payload,
|
|
97
|
+
adapterContext: normalizedContext,
|
|
98
|
+
explicitProfile: PROFILE
|
|
286
99
|
};
|
|
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
100
|
}
|
|
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
|
-
}
|
|
101
|
+
function parseCompatOutput(raw) {
|
|
102
|
+
let parsed;
|
|
465
103
|
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;
|
|
104
|
+
parsed = JSON.parse(raw);
|
|
475
105
|
}
|
|
476
106
|
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
|
-
}
|
|
107
|
+
throw makeNativeRequiredError(CAPABILITY, 'invalid payload');
|
|
522
108
|
}
|
|
523
|
-
if (!
|
|
524
|
-
|
|
109
|
+
if (!isRecord(parsed) || !isRecord(parsed.payload) || typeof parsed.nativeApplied !== 'boolean') {
|
|
110
|
+
throw makeNativeRequiredError(CAPABILITY, 'invalid payload');
|
|
525
111
|
}
|
|
526
|
-
|
|
527
|
-
return alias;
|
|
528
|
-
}
|
|
529
|
-
const allowedAlias = findAllowedToolNameCaseInsensitive(allowedToolNames, alias);
|
|
530
|
-
if (allowedAlias) {
|
|
531
|
-
return allowedAlias;
|
|
532
|
-
}
|
|
533
|
-
const allowedRaw = findAllowedToolNameCaseInsensitive(allowedToolNames, rawName);
|
|
534
|
-
if (allowedRaw) {
|
|
535
|
-
return allowedRaw;
|
|
536
|
-
}
|
|
537
|
-
return alias;
|
|
112
|
+
return parsed;
|
|
538
113
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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;
|
|
114
|
+
function callDeepSeekWebResponseCompat(input, adapterContext) {
|
|
115
|
+
if (isNativeDisabledByEnv()) {
|
|
116
|
+
emitCompatError(makeNativeRequiredError(CAPABILITY, 'native disabled'), adapterContext, input.payload, {
|
|
117
|
+
reason: 'native disabled'
|
|
118
|
+
});
|
|
553
119
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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;
|
|
120
|
+
const binding = loadNativeRouterHotpathBindingForInternalUse();
|
|
121
|
+
const fn = binding?.[CAPABILITY];
|
|
122
|
+
if (typeof fn !== 'function') {
|
|
123
|
+
emitCompatError(makeNativeRequiredError(CAPABILITY), adapterContext, input.payload, {
|
|
124
|
+
reason: 'missing native export'
|
|
125
|
+
});
|
|
594
126
|
}
|
|
127
|
+
let inputJson;
|
|
595
128
|
try {
|
|
596
|
-
|
|
129
|
+
inputJson = JSON.stringify(input);
|
|
597
130
|
}
|
|
598
131
|
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;
|
|
132
|
+
emitCompatError(makeNativeRequiredError(CAPABILITY, 'json stringify failed'), adapterContext, input.payload, {
|
|
133
|
+
reason: 'json stringify failed'
|
|
134
|
+
});
|
|
633
135
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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;
|
|
749
|
-
}
|
|
750
|
-
root.usage = usage;
|
|
751
|
-
}
|
|
752
|
-
function normalizeChoice(choice, options, allowedToolNames, callIdPrefix) {
|
|
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 };
|
|
764
|
-
}
|
|
765
|
-
if (options.toolProtocol === 'text') {
|
|
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
|
|
136
|
+
try {
|
|
137
|
+
const raw = fn(inputJson);
|
|
138
|
+
if (typeof raw !== 'string' || !raw) {
|
|
139
|
+
emitCompatError(makeNativeRequiredError(CAPABILITY, 'empty result'), adapterContext, input.payload, {
|
|
140
|
+
reason: 'empty result'
|
|
832
141
|
});
|
|
833
142
|
}
|
|
143
|
+
return parseCompatOutput(raw);
|
|
834
144
|
}
|
|
835
|
-
|
|
836
|
-
|
|
145
|
+
catch (error) {
|
|
146
|
+
const compatError = error instanceof Error ? error : new Error(String(error));
|
|
147
|
+
emitCompatError(compatError, adapterContext, input.payload, {
|
|
148
|
+
reason: 'native compat execution failed'
|
|
149
|
+
});
|
|
837
150
|
}
|
|
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;
|
|
849
151
|
}
|
|
850
152
|
export function applyDeepSeekWebResponseTransform(payload, adapterContext, config) {
|
|
851
153
|
if (!payload || typeof payload !== 'object') {
|
|
852
|
-
|
|
853
|
-
|
|
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;
|
|
154
|
+
emitCompatError(new Error('[deepseek-web] invalid compat payload: expected object'), adapterContext, payload, {
|
|
155
|
+
reason: 'payload is not an object'
|
|
156
|
+
});
|
|
895
157
|
}
|
|
896
|
-
return
|
|
158
|
+
return callDeepSeekWebResponseCompat(buildCompatInput(payload, adapterContext, config), adapterContext).payload;
|
|
897
159
|
}
|