@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,281 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
14
|
-
const readReasoningEffort = (value) => {
|
|
15
|
-
if (typeof value === 'string') {
|
|
16
|
-
return value;
|
|
17
|
-
}
|
|
18
|
-
if (isRecord(value) && typeof value.effort === 'string') {
|
|
19
|
-
return value.effort;
|
|
20
|
-
}
|
|
21
|
-
return undefined;
|
|
22
|
-
};
|
|
23
|
-
export function applyQwenRequestTransform(payload) {
|
|
24
|
-
const cloned = structuredClone(payload);
|
|
25
|
-
const transformed = convertToQwenRequest(cloned);
|
|
26
|
-
return transformed;
|
|
27
|
-
}
|
|
28
|
-
export function applyQwenResponseTransform(payload) {
|
|
29
|
-
const cloned = structuredClone(payload);
|
|
30
|
-
const transformed = transformQwenResponseToOpenAI(cloned);
|
|
31
|
-
return transformed;
|
|
32
|
-
}
|
|
33
|
-
function convertToQwenRequest(request) {
|
|
34
|
-
const qwenRequest = {};
|
|
35
|
-
const mappedModel = mapModelName(typeof request.model === 'string' ? request.model : undefined);
|
|
36
|
-
if (mappedModel) {
|
|
37
|
-
qwenRequest.model = mappedModel;
|
|
38
|
-
}
|
|
39
|
-
if (Array.isArray(request.messages)) {
|
|
40
|
-
qwenRequest.messages = structuredClone(request.messages);
|
|
41
|
-
const normalizedMessages = request.messages
|
|
42
|
-
.map(message => normalizeMessage(message))
|
|
43
|
-
.filter((entry) => entry !== null);
|
|
44
|
-
if (normalizedMessages.length > 0) {
|
|
45
|
-
qwenRequest.input = normalizedMessages;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const parameters = extractParameters(request);
|
|
49
|
-
if (Object.keys(parameters).length > 0) {
|
|
50
|
-
qwenRequest.parameters = parameters;
|
|
51
|
-
}
|
|
52
|
-
if (typeof request.stream === 'boolean') {
|
|
53
|
-
qwenRequest.stream = request.stream;
|
|
54
|
-
}
|
|
55
|
-
if (isRecord(request.response_format)) {
|
|
56
|
-
qwenRequest.response_format = structuredClone(request.response_format);
|
|
57
|
-
}
|
|
58
|
-
if (typeof request.user === 'string') {
|
|
59
|
-
qwenRequest.user = request.user;
|
|
60
|
-
}
|
|
61
|
-
if (Array.isArray(request.tools)) {
|
|
62
|
-
qwenRequest.tools = sanitizeTools(request.tools);
|
|
63
|
-
}
|
|
64
|
-
if (isRecord(request.metadata)) {
|
|
65
|
-
qwenRequest.metadata = structuredClone(request.metadata);
|
|
66
|
-
}
|
|
67
|
-
return qwenRequest;
|
|
68
|
-
}
|
|
69
|
-
function sanitizeTools(tools) {
|
|
70
|
-
return tools.map(tool => {
|
|
71
|
-
if (!isRecord(tool)) {
|
|
72
|
-
return tool;
|
|
73
|
-
}
|
|
74
|
-
const normalized = {};
|
|
75
|
-
if (typeof tool.type === 'string') {
|
|
76
|
-
normalized.type = tool.type;
|
|
77
|
-
}
|
|
78
|
-
if (isRecord(tool.function)) {
|
|
79
|
-
normalized.function = structuredClone(tool.function);
|
|
80
|
-
}
|
|
81
|
-
return Object.keys(normalized).length > 0 ? normalized : tool;
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
function normalizeMessage(message) {
|
|
85
|
-
if (!isRecord(message)) {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
const role = typeof message.role === 'string' ? message.role : 'user';
|
|
89
|
-
const content = normalizeMessageContent(message.content);
|
|
90
|
-
return { role, content };
|
|
91
|
-
}
|
|
92
|
-
function normalizeMessageContent(content) {
|
|
93
|
-
if (content === undefined || content === null) {
|
|
94
|
-
return [{ text: '' }];
|
|
95
|
-
}
|
|
96
|
-
if (typeof content === 'string') {
|
|
97
|
-
return [{ text: content }];
|
|
98
|
-
}
|
|
99
|
-
if (Array.isArray(content)) {
|
|
100
|
-
return content.map(chunk => normalizeContentChunk(chunk));
|
|
101
|
-
}
|
|
102
|
-
if (isRecord(content) && typeof content.text === 'string') {
|
|
103
|
-
return [{ text: content.text }];
|
|
104
|
-
}
|
|
105
|
-
return [{ text: JSON.stringify(content) }];
|
|
106
|
-
}
|
|
107
|
-
function normalizeContentChunk(chunk) {
|
|
108
|
-
if (typeof chunk === 'string') {
|
|
109
|
-
return { text: chunk };
|
|
110
|
-
}
|
|
111
|
-
if (isRecord(chunk)) {
|
|
112
|
-
if (typeof chunk.text === 'string') {
|
|
113
|
-
return { text: chunk.text };
|
|
114
|
-
}
|
|
115
|
-
return structuredClone(chunk);
|
|
116
|
-
}
|
|
117
|
-
return { text: String(chunk) };
|
|
118
|
-
}
|
|
119
|
-
function extractParameters(request) {
|
|
120
|
-
const parameters = {};
|
|
121
|
-
const numericFields = [
|
|
122
|
-
{ key: 'temperature', target: 'temperature' },
|
|
123
|
-
{ key: 'top_p', target: 'top_p' },
|
|
124
|
-
{ key: 'frequency_penalty', target: 'frequency_penalty' },
|
|
125
|
-
{ key: 'presence_penalty', target: 'presence_penalty' },
|
|
126
|
-
{ key: 'max_tokens', target: 'max_output_tokens' }
|
|
127
|
-
];
|
|
128
|
-
for (const field of numericFields) {
|
|
129
|
-
const value = request[field.key];
|
|
130
|
-
if (typeof value === 'number') {
|
|
131
|
-
parameters[field.target] = value;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (request.stop !== undefined) {
|
|
135
|
-
const stops = Array.isArray(request.stop) ? request.stop : [request.stop];
|
|
136
|
-
const sequences = stops.filter(item => typeof item === 'string');
|
|
137
|
-
if (sequences.length > 0) {
|
|
138
|
-
parameters.stop_sequences = sequences;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (typeof request.debug === 'boolean') {
|
|
142
|
-
parameters.debug = request.debug;
|
|
143
|
-
}
|
|
144
|
-
const reasoningEffort = readReasoningEffort(request.reasoning)?.toLowerCase();
|
|
145
|
-
if (reasoningEffort !== 'low') {
|
|
146
|
-
parameters.reasoning = true;
|
|
147
|
-
}
|
|
148
|
-
return parameters;
|
|
149
|
-
}
|
|
150
|
-
function transformQwenResponseToOpenAI(response) {
|
|
151
|
-
const data = isRecord(response.data) ? response.data : response;
|
|
152
|
-
const usage = isRecord(data.usage)
|
|
153
|
-
? structuredClone(data.usage)
|
|
154
|
-
: {
|
|
155
|
-
prompt_tokens: 0,
|
|
156
|
-
completion_tokens: 0,
|
|
157
|
-
total_tokens: 0
|
|
158
|
-
};
|
|
159
|
-
const transformed = {
|
|
160
|
-
id: typeof data.id === 'string' ? data.id : `chatcmpl-${Date.now()}`,
|
|
161
|
-
object: 'chat.completion',
|
|
162
|
-
created: typeof data.created === 'number' ? data.created : Math.floor(Date.now() / 1000),
|
|
163
|
-
model: typeof data.model === 'string' ? data.model : 'qwen-turbo',
|
|
164
|
-
choices: transformChoices(data.choices),
|
|
165
|
-
usage,
|
|
166
|
-
_transformed: true,
|
|
167
|
-
_originalFormat: 'qwen',
|
|
168
|
-
_targetFormat: 'openai'
|
|
1
|
+
import { buildNativeReqOutboundCompatAdapterContext } from '../../hub/pipeline/compat/native-adapter-context.js';
|
|
2
|
+
import { runReqOutboundStage3CompatWithNative, runRespInboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
3
|
+
const PROFILE = 'chat:qwen';
|
|
4
|
+
const DEFAULT_PROVIDER_PROTOCOL = 'openai-chat';
|
|
5
|
+
const DEFAULT_ENTRY_ENDPOINT = '/v1/chat/completions';
|
|
6
|
+
function buildQwenCompatContext(adapterContext) {
|
|
7
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
8
|
+
return {
|
|
9
|
+
...nativeContext,
|
|
10
|
+
compatibilityProfile: PROFILE,
|
|
11
|
+
providerProtocol: nativeContext.providerProtocol ?? adapterContext?.providerProtocol ?? DEFAULT_PROVIDER_PROTOCOL,
|
|
12
|
+
entryEndpoint: nativeContext.entryEndpoint ?? adapterContext?.entryEndpoint ?? DEFAULT_ENTRY_ENDPOINT
|
|
169
13
|
};
|
|
170
|
-
const firstMessage = (() => {
|
|
171
|
-
const choices = Array.isArray(transformed.choices) ? transformed.choices : [];
|
|
172
|
-
const primary = choices[0] && isRecord(choices[0]) ? choices[0] : undefined;
|
|
173
|
-
const message = primary && isRecord(primary.message) ? primary.message : undefined;
|
|
174
|
-
return message;
|
|
175
|
-
})();
|
|
176
|
-
if (firstMessage && typeof firstMessage.reasoning_content === 'string' && firstMessage.reasoning_content.trim()) {
|
|
177
|
-
transformed.__responses_reasoning = {
|
|
178
|
-
content: [{ type: 'reasoning_text', text: String(firstMessage.reasoning_content).trim() }]
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
return transformed;
|
|
182
14
|
}
|
|
183
|
-
function
|
|
184
|
-
if (!Array.isArray(rawChoices)) {
|
|
185
|
-
return [];
|
|
186
|
-
}
|
|
187
|
-
return rawChoices.map((choice, index) => {
|
|
188
|
-
const choiceObj = isRecord(choice) ? choice : {};
|
|
189
|
-
const messageObj = isRecord(choiceObj.message) ? choiceObj.message : {};
|
|
190
|
-
const contentDetails = extractContentAndReasoning(messageObj.content);
|
|
191
|
-
const reasoningText = mergeReasoningText(contentDetails?.reasoningText, readString(messageObj.reasoning_content) ?? readString(messageObj.reasoning));
|
|
192
|
-
return {
|
|
193
|
-
index: typeof choiceObj.index === 'number' ? choiceObj.index : index,
|
|
194
|
-
message: {
|
|
195
|
-
role: typeof messageObj.role === 'string' ? messageObj.role : 'assistant',
|
|
196
|
-
content: contentDetails?.contentText ?? (typeof messageObj.content === 'string' ? messageObj.content : ''),
|
|
197
|
-
tool_calls: transformToolCalls(messageObj.tool_calls),
|
|
198
|
-
...(reasoningText ? { reasoning_content: reasoningText } : {})
|
|
199
|
-
},
|
|
200
|
-
finish_reason: transformFinishReason(typeof choiceObj.finish_reason === 'string' ? choiceObj.finish_reason : undefined)
|
|
201
|
-
};
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
function readString(value) {
|
|
205
|
-
if (typeof value !== 'string') {
|
|
206
|
-
return undefined;
|
|
207
|
-
}
|
|
208
|
-
const trimmed = value.trim();
|
|
209
|
-
return trimmed.length ? trimmed : undefined;
|
|
210
|
-
}
|
|
211
|
-
function mergeReasoningText(primary, secondary) {
|
|
212
|
-
const parts = [primary, secondary].filter((item) => typeof item === 'string' && item.trim().length > 0);
|
|
213
|
-
if (parts.length === 0) {
|
|
214
|
-
return undefined;
|
|
215
|
-
}
|
|
216
|
-
return parts.join('\n');
|
|
217
|
-
}
|
|
218
|
-
function extractContentAndReasoning(content) {
|
|
219
|
-
if (!Array.isArray(content)) {
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
const contentParts = [];
|
|
223
|
-
const reasoningParts = [];
|
|
224
|
-
for (const chunk of content) {
|
|
225
|
-
if (typeof chunk === 'string') {
|
|
226
|
-
contentParts.push(chunk);
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (!isRecord(chunk)) {
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
const text = readString(chunk.text);
|
|
233
|
-
const type = typeof chunk.type === 'string' ? chunk.type.toLowerCase() : '';
|
|
234
|
-
const isThinking = Boolean(chunk.thought) || type === 'thinking' || type === 'reasoning';
|
|
235
|
-
if (text) {
|
|
236
|
-
if (isThinking) {
|
|
237
|
-
reasoningParts.push(text);
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
contentParts.push(text);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
15
|
+
function buildQwenRequestCompatInput(payload, adapterContext) {
|
|
244
16
|
return {
|
|
245
|
-
|
|
246
|
-
|
|
17
|
+
payload,
|
|
18
|
+
adapterContext: buildQwenCompatContext(adapterContext),
|
|
19
|
+
explicitProfile: PROFILE
|
|
247
20
|
};
|
|
248
21
|
}
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const fnObj = isRecord(toolCallObj.function) ? toolCallObj.function : {};
|
|
256
|
-
const id = typeof toolCallObj.id === 'string'
|
|
257
|
-
? toolCallObj.id
|
|
258
|
-
: `call_${Date.now()}_${index}`;
|
|
259
|
-
const name = typeof fnObj.name === 'string' ? fnObj.name : '';
|
|
260
|
-
const args = typeof fnObj.arguments === 'string'
|
|
261
|
-
? fnObj.arguments
|
|
262
|
-
: JSON.stringify(fnObj.arguments ?? {});
|
|
263
|
-
return {
|
|
264
|
-
id,
|
|
265
|
-
type: 'function',
|
|
266
|
-
function: { name, arguments: args }
|
|
267
|
-
};
|
|
268
|
-
});
|
|
22
|
+
function buildQwenResponseCompatInput(payload, adapterContext) {
|
|
23
|
+
return {
|
|
24
|
+
payload,
|
|
25
|
+
adapterContext: buildQwenCompatContext(adapterContext),
|
|
26
|
+
explicitProfile: PROFILE
|
|
27
|
+
};
|
|
269
28
|
}
|
|
270
|
-
function
|
|
271
|
-
if (!
|
|
272
|
-
return
|
|
29
|
+
export function applyQwenRequestTransform(payload, adapterContext) {
|
|
30
|
+
if (!payload || typeof payload !== 'object') {
|
|
31
|
+
return payload;
|
|
273
32
|
}
|
|
274
|
-
return
|
|
33
|
+
return runReqOutboundStage3CompatWithNative(buildQwenRequestCompatInput(payload, adapterContext)).payload;
|
|
275
34
|
}
|
|
276
|
-
function
|
|
277
|
-
if (!
|
|
278
|
-
return
|
|
35
|
+
export function applyQwenResponseTransform(payload, adapterContext) {
|
|
36
|
+
if (!payload || typeof payload !== 'object') {
|
|
37
|
+
return payload;
|
|
279
38
|
}
|
|
280
|
-
return
|
|
39
|
+
return runRespInboundStage3CompatWithNative(buildQwenResponseCompatInput(payload, adapterContext)).payload;
|
|
281
40
|
}
|
|
@@ -1,177 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
function readString(value) {
|
|
3
|
-
return typeof value === 'string' ? value.trim() : '';
|
|
4
|
-
}
|
|
5
|
-
function collectToolNames(toolsRaw) {
|
|
6
|
-
const out = [];
|
|
7
|
-
if (!Array.isArray(toolsRaw)) {
|
|
8
|
-
return out;
|
|
9
|
-
}
|
|
10
|
-
for (const item of toolsRaw) {
|
|
11
|
-
if (!isRecord(item))
|
|
12
|
-
continue;
|
|
13
|
-
const fn = isRecord(item.function) ? item.function : item;
|
|
14
|
-
const name = readString(fn.name);
|
|
15
|
-
if (!name)
|
|
16
|
-
continue;
|
|
17
|
-
if (!out.includes(name)) {
|
|
18
|
-
out.push(name);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return out;
|
|
22
|
-
}
|
|
23
|
-
function isToolChoiceRequired(root) {
|
|
24
|
-
const toolChoice = root.tool_choice;
|
|
25
|
-
if (typeof toolChoice === 'string') {
|
|
26
|
-
const normalized = toolChoice.trim().toLowerCase();
|
|
27
|
-
if (normalized === 'required')
|
|
28
|
-
return true;
|
|
29
|
-
if (normalized === 'auto' || normalized === 'none')
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
if (isRecord(toolChoice)) {
|
|
33
|
-
return String(toolChoice.type || '').trim().toLowerCase() === 'function';
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
function buildDefaultInstruction(root, config) {
|
|
38
|
-
const toolNames = collectToolNames(root.tools);
|
|
39
|
-
const includeToolNames = config?.includeToolNames !== false;
|
|
40
|
-
const required = isToolChoiceRequired(root);
|
|
41
|
-
const marker = readString(config?.marker) || 'Tool-call output contract (STRICT)';
|
|
42
|
-
const lines = [
|
|
43
|
-
`${marker}:`,
|
|
44
|
-
'1) If calling tools, output exactly one JSON object: {"tool_calls":[{"name":"tool_name","input":{...}}]}',
|
|
45
|
-
'2) Use only keys: `tool_calls` + each call `name` and `input`.',
|
|
46
|
-
'3) Do not output markdown fences, prose, or tool transcripts around JSON.',
|
|
47
|
-
'4) Do NOT output pseudo tool results in text (forbidden examples: {"exec_command":...}, <function_results>...</function_results>).'
|
|
48
|
-
];
|
|
49
|
-
if (includeToolNames && toolNames.length) {
|
|
50
|
-
lines.push(`5) Allowed tool names this turn: ${toolNames.join(', ')}`);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
lines.push('5) Tool name must match provided schema exactly.');
|
|
54
|
-
}
|
|
55
|
-
lines.push(required
|
|
56
|
-
? '6) tool_choice is required for this turn: return at least one tool call.'
|
|
57
|
-
: '6) If no tool is needed, plain text is allowed.');
|
|
58
|
-
return lines.join('\n');
|
|
59
|
-
}
|
|
60
|
-
function ensureSystemMessage(messages) {
|
|
61
|
-
const first = messages[0];
|
|
62
|
-
if (isRecord(first) && readString(first.role).toLowerCase() === 'system') {
|
|
63
|
-
return first;
|
|
64
|
-
}
|
|
65
|
-
const created = { role: 'system', content: '' };
|
|
66
|
-
messages.unshift(created);
|
|
67
|
-
return created;
|
|
68
|
-
}
|
|
69
|
-
function contentToText(content) {
|
|
70
|
-
if (typeof content === 'string') {
|
|
71
|
-
return content;
|
|
72
|
-
}
|
|
73
|
-
if (Array.isArray(content)) {
|
|
74
|
-
const parts = [];
|
|
75
|
-
for (const part of content) {
|
|
76
|
-
if (typeof part === 'string') {
|
|
77
|
-
parts.push(part);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (!isRecord(part))
|
|
81
|
-
continue;
|
|
82
|
-
const text = readString(part.text) || readString(part.content);
|
|
83
|
-
if (text)
|
|
84
|
-
parts.push(text);
|
|
85
|
-
}
|
|
86
|
-
return parts.join('\n').trim();
|
|
87
|
-
}
|
|
88
|
-
if (isRecord(content)) {
|
|
89
|
-
const text = readString(content.text) || readString(content.content);
|
|
90
|
-
if (text) {
|
|
91
|
-
return text;
|
|
92
|
-
}
|
|
93
|
-
if (Array.isArray(content.content)) {
|
|
94
|
-
return contentToText(content.content);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return '';
|
|
98
|
-
}
|
|
99
|
-
function contentHasMarker(content, marker) {
|
|
100
|
-
if (!marker) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
const text = contentToText(content);
|
|
104
|
-
return text.includes(marker);
|
|
105
|
-
}
|
|
106
|
-
function appendInstructionToContent(content, instruction) {
|
|
107
|
-
if (typeof content === 'string') {
|
|
108
|
-
return content.trim() ? `${content}\n\n${instruction}` : instruction;
|
|
109
|
-
}
|
|
110
|
-
if (Array.isArray(content)) {
|
|
111
|
-
return [
|
|
112
|
-
...content,
|
|
113
|
-
{
|
|
114
|
-
type: 'text',
|
|
115
|
-
text: content.length ? `\n\n${instruction}` : instruction
|
|
116
|
-
}
|
|
117
|
-
];
|
|
118
|
-
}
|
|
119
|
-
if (isRecord(content)) {
|
|
120
|
-
if (typeof content.text === 'string') {
|
|
121
|
-
const previous = content.text;
|
|
122
|
-
return {
|
|
123
|
-
...content,
|
|
124
|
-
text: previous.trim() ? `${previous}\n\n${instruction}` : instruction
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
if (typeof content.content === 'string') {
|
|
128
|
-
const previous = content.content;
|
|
129
|
-
return {
|
|
130
|
-
...content,
|
|
131
|
-
content: previous.trim() ? `${previous}\n\n${instruction}` : instruction
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
if (Array.isArray(content.content)) {
|
|
135
|
-
return {
|
|
136
|
-
...content,
|
|
137
|
-
content: appendInstructionToContent(content.content, instruction)
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
return [content, { type: 'text', text: instruction }];
|
|
141
|
-
}
|
|
142
|
-
return instruction;
|
|
143
|
-
}
|
|
1
|
+
import { applyToolTextRequestGuidanceWithNative } from '../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
|
|
144
2
|
export function applyToolTextRequestGuidance(payload, config) {
|
|
145
|
-
|
|
146
|
-
if (!enabled) {
|
|
3
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
147
4
|
return payload;
|
|
148
5
|
}
|
|
149
|
-
|
|
150
|
-
const requireTools = config?.requireTools !== false;
|
|
151
|
-
if (requireTools) {
|
|
152
|
-
const tools = Array.isArray(root.tools) ? root.tools : [];
|
|
153
|
-
if (!tools.length) {
|
|
154
|
-
return root;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (!Array.isArray(root.messages)) {
|
|
158
|
-
return root;
|
|
159
|
-
}
|
|
160
|
-
const messages = root.messages.filter((item) => isRecord(item));
|
|
161
|
-
if (!messages.length) {
|
|
162
|
-
return root;
|
|
163
|
-
}
|
|
164
|
-
const instruction = readString(config?.instruction) || buildDefaultInstruction(root, config);
|
|
165
|
-
if (!instruction) {
|
|
166
|
-
return root;
|
|
167
|
-
}
|
|
168
|
-
const marker = readString(config?.marker) || 'Tool-call output contract (STRICT)';
|
|
169
|
-
const system = ensureSystemMessage(messages);
|
|
170
|
-
if (contentHasMarker(system.content, marker)) {
|
|
171
|
-
root.messages = messages;
|
|
172
|
-
return root;
|
|
173
|
-
}
|
|
174
|
-
system.content = appendInstructionToContent(system.content, instruction);
|
|
175
|
-
root.messages = messages;
|
|
176
|
-
return root;
|
|
6
|
+
return applyToolTextRequestGuidanceWithNative(payload, (config ?? {}));
|
|
177
7
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { AdapterContext } from
|
|
2
|
-
import type { JsonObject } from
|
|
1
|
+
import type { AdapterContext } from "../../hub/types/chat-envelope.js";
|
|
2
|
+
import type { JsonObject } from "../../hub/types/json.js";
|
|
3
3
|
type RequestMessagesRule = {
|
|
4
4
|
when?: {
|
|
5
|
-
role?:
|
|
5
|
+
role?: "system" | "user" | "assistant" | "tool";
|
|
6
6
|
hasToolCalls?: boolean;
|
|
7
7
|
};
|
|
8
|
-
action:
|
|
8
|
+
action: "drop" | "keep" | "set";
|
|
9
9
|
set?: Record<string, unknown>;
|
|
10
10
|
};
|
|
11
11
|
export interface FilterConfig {
|
|
@@ -22,7 +22,7 @@ export interface FilterConfig {
|
|
|
22
22
|
forceToolChoiceAuto?: boolean;
|
|
23
23
|
};
|
|
24
24
|
assistantToolCalls?: {
|
|
25
|
-
functionArgumentsType?:
|
|
25
|
+
functionArgumentsType?: "object" | "string";
|
|
26
26
|
};
|
|
27
27
|
messagesRules?: RequestMessagesRule[];
|
|
28
28
|
};
|
|
@@ -37,7 +37,7 @@ export interface FilterConfig {
|
|
|
37
37
|
tool_calls?: {
|
|
38
38
|
function?: {
|
|
39
39
|
nameRequired?: boolean;
|
|
40
|
-
argumentsType?:
|
|
40
|
+
argumentsType?: "object" | "string";
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
43
|
};
|
|
@@ -53,22 +53,5 @@ export declare class UniversalShapeFilter {
|
|
|
53
53
|
constructor(config: FilterConfig);
|
|
54
54
|
applyRequestFilter(payload: JsonObject): JsonObject;
|
|
55
55
|
applyResponseFilter(payload: JsonObject, ctx?: AdapterContext): JsonObject;
|
|
56
|
-
private shallowPick;
|
|
57
|
-
private toObjectArgs;
|
|
58
|
-
private toStringArgs;
|
|
59
|
-
private normalizeToolContent;
|
|
60
|
-
private normalizeRequestMessages;
|
|
61
|
-
private normalizeSingleMessage;
|
|
62
|
-
private normalizeAssistantToolCalls;
|
|
63
|
-
private applyMessageRules;
|
|
64
|
-
private pairToolResults;
|
|
65
|
-
private normalizeTools;
|
|
66
|
-
private normalizeSingleTool;
|
|
67
|
-
private normalizeToolParameters;
|
|
68
|
-
private enforceShellSchema;
|
|
69
|
-
private cleanupToolChoice;
|
|
70
|
-
private normalizeResponseChoice;
|
|
71
|
-
private normalizeResponseMessage;
|
|
72
|
-
private normalizeResponseToolCalls;
|
|
73
56
|
}
|
|
74
57
|
export {};
|