@jsonstudio/llms 0.6.199 → 0.6.215

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.
@@ -4,6 +4,7 @@ import { normalizeChatMessageContent } from '../shared/chat-output-normalizer.js
4
4
  import { mapBridgeToolsToChat } from '../shared/tool-mapping.js';
5
5
  import { prepareGeminiToolsForBridge } from '../shared/gemini-tool-utils.js';
6
6
  import { registerResponsesReasoning, consumeResponsesReasoning, registerResponsesOutputTextMeta, consumeResponsesOutputTextMeta, consumeResponsesPayloadSnapshot, registerResponsesPayloadSnapshot, consumeResponsesPassthrough, registerResponsesPassthrough } from '../shared/responses-reasoning-registry.js';
7
+ const DUMMY_THOUGHT_SIGNATURE = 'skip_thought_signature_validator';
7
8
  function isObject(v) {
8
9
  return !!v && typeof v === 'object' && !Array.isArray(v);
9
10
  }
@@ -54,6 +55,32 @@ function mapChatRoleToGemini(role) {
54
55
  return 'tool';
55
56
  return 'user';
56
57
  }
58
+ function coerceThoughtSignature(value) {
59
+ if (typeof value === 'string' && value.trim().length) {
60
+ return value.trim();
61
+ }
62
+ return undefined;
63
+ }
64
+ function extractThoughtSignatureFromToolCall(tc) {
65
+ if (!tc || typeof tc !== 'object') {
66
+ return undefined;
67
+ }
68
+ const direct = coerceThoughtSignature(tc.thought_signature ?? tc.thoughtSignature);
69
+ if (direct) {
70
+ return direct;
71
+ }
72
+ const extraContent = tc.extra_content ?? tc.extraContent;
73
+ if (extraContent && typeof extraContent === 'object') {
74
+ const googleNode = extraContent.google ?? extraContent.Google;
75
+ if (googleNode && typeof googleNode === 'object') {
76
+ const googleSig = coerceThoughtSignature(googleNode.thought_signature ?? googleNode.thoughtSignature);
77
+ if (googleSig) {
78
+ return googleSig;
79
+ }
80
+ }
81
+ }
82
+ return undefined;
83
+ }
57
84
  export function buildOpenAIChatFromGeminiRequest(payload) {
58
85
  const messages = [];
59
86
  // systemInstruction → Chat system 消息
@@ -195,11 +222,21 @@ export function buildOpenAIChatFromGeminiResponse(payload) {
195
222
  else {
196
223
  argsStr = safeJson(argsRaw);
197
224
  }
198
- toolCalls.push({
225
+ const thoughtSignature = coerceThoughtSignature(pObj.thoughtSignature);
226
+ const toolCall = {
199
227
  id,
200
228
  type: 'function',
201
229
  function: { name, arguments: argsStr }
202
- });
230
+ };
231
+ if (thoughtSignature) {
232
+ toolCall.thought_signature = thoughtSignature;
233
+ toolCall.extra_content = {
234
+ google: {
235
+ thought_signature: thoughtSignature
236
+ }
237
+ };
238
+ }
239
+ toolCalls.push(toolCall);
203
240
  continue;
204
241
  }
205
242
  }
@@ -398,7 +435,12 @@ export function buildGeminiFromOpenAIChat(chatResp) {
398
435
  const id = typeof tc.id === 'string' ? String(tc.id) : undefined;
399
436
  if (id)
400
437
  functionCall.id = id;
401
- parts.push({ functionCall });
438
+ const thoughtSignature = extractThoughtSignatureFromToolCall(tc) ?? DUMMY_THOUGHT_SIGNATURE;
439
+ const partEntry = { functionCall };
440
+ if (thoughtSignature) {
441
+ partEntry.thoughtSignature = thoughtSignature;
442
+ }
443
+ parts.push(partEntry);
402
444
  }
403
445
  const candidate = {
404
446
  content: {
@@ -0,0 +1,2 @@
1
+ import type { JsonObject } from '../../hub/types/json.js';
2
+ export declare function extractGlmToolMarkup(root: JsonObject): void;
@@ -0,0 +1,264 @@
1
+ function flattenContent(content, depth = 0) {
2
+ if (depth > 4 || content == null) {
3
+ return '';
4
+ }
5
+ if (typeof content === 'string') {
6
+ return content;
7
+ }
8
+ if (Array.isArray(content)) {
9
+ return content.map((entry) => flattenContent(entry, depth + 1)).join('');
10
+ }
11
+ if (typeof content === 'object') {
12
+ const record = content;
13
+ if (typeof record.text === 'string') {
14
+ return record.text;
15
+ }
16
+ if (record.content !== undefined) {
17
+ return flattenContent(record.content, depth + 1);
18
+ }
19
+ }
20
+ return '';
21
+ }
22
+ const GLM_CUSTOM_TAG = /<tool_call(?:\s+name="([^"]+)")?>([\s\S]*?)<\/tool_call>/gi;
23
+ const GLM_TAGGED_SEQUENCE = /<tool_call(?:\s+name="([^"]+)")?\s*>([\s\S]*?)(?:<\/tool_call>|(?=<tool_call)|$)/gi;
24
+ const GLM_TAGGED_BLOCK = /<arg_key>([\s\S]*?)<\/arg_key>\s*<arg_value>([\s\S]*?)<\/arg_value>/gi;
25
+ const GLM_INLINE_NAME = /^[\s\r\n]*([A-Za-z0-9_.:-]+)/;
26
+ const GENERIC_PATTERNS = [
27
+ [
28
+ /```(?:tool|function|tool_call|function_call)?\s*([\s\S]*?)\s*```/gi,
29
+ (match) => ({ body: match[1] ?? '' })
30
+ ],
31
+ [
32
+ /\[(tool_call|function_call)(?:\s+name="([^"]+)")?\]([\s\S]*?)\[\/\1\]/gi,
33
+ (match) => ({ body: match[3] ?? '', nameHint: match[2] })
34
+ ],
35
+ [
36
+ /(tool_call|function_call)\s*[:=]\s*({[\s\S]+?})/gi,
37
+ (match) => ({ body: match[2] ?? '' })
38
+ ]
39
+ ];
40
+ export function extractGlmToolMarkup(root) {
41
+ const choicesRaw = root?.choices;
42
+ const choices = Array.isArray(choicesRaw) ? choicesRaw : [];
43
+ choices.forEach((choice, index) => {
44
+ if (!choice || typeof choice !== 'object') {
45
+ return;
46
+ }
47
+ const message = choice.message;
48
+ if (!message || typeof message !== 'object') {
49
+ return;
50
+ }
51
+ const msgRecord = message;
52
+ const contentField = msgRecord.content;
53
+ const reasoningField = msgRecord.reasoning_content;
54
+ const text = contentField !== undefined
55
+ ? flattenContent(contentField)
56
+ : typeof reasoningField === 'string'
57
+ ? reasoningField
58
+ : '';
59
+ if (!text) {
60
+ return;
61
+ }
62
+ const extraction = extractToolCallsFromText(text, index + 1);
63
+ if (!extraction) {
64
+ return;
65
+ }
66
+ if (extraction.toolCalls.length) {
67
+ msgRecord.tool_calls = extraction.toolCalls;
68
+ if (contentField !== undefined) {
69
+ msgRecord.content = null;
70
+ }
71
+ }
72
+ if (extraction.reasoningText) {
73
+ msgRecord.reasoning_content = extraction.reasoningText;
74
+ }
75
+ else if ('reasoning_content' in msgRecord) {
76
+ delete msgRecord.reasoning_content;
77
+ }
78
+ });
79
+ }
80
+ function extractToolCallsFromText(text, choiceIndex) {
81
+ const matches = [];
82
+ const applyPattern = (pattern, factory) => {
83
+ pattern.lastIndex = 0;
84
+ let exec;
85
+ while ((exec = pattern.exec(text))) {
86
+ const payload = factory(exec);
87
+ if (!payload)
88
+ continue;
89
+ const parsed = parseToolCall(payload.body, payload.nameHint);
90
+ if (!parsed)
91
+ continue;
92
+ matches.push({
93
+ start: exec.index,
94
+ end: exec.index + exec[0].length,
95
+ call: parsed
96
+ });
97
+ }
98
+ };
99
+ applyPattern(GLM_CUSTOM_TAG, (match) => ({ body: match[2] ?? '', nameHint: match[1] }));
100
+ for (const [pattern, factory] of GENERIC_PATTERNS) {
101
+ applyPattern(pattern, factory);
102
+ }
103
+ applyTaggedArgPatterns(text, matches);
104
+ if (!matches.length && typeof text === 'string' && text.includes('<arg_key>')) {
105
+ GLM_INLINE_NAME.lastIndex = 0;
106
+ const inline = GLM_INLINE_NAME.exec(text);
107
+ GLM_INLINE_NAME.lastIndex = 0;
108
+ if (inline && inline[1]) {
109
+ const name = inline[1].trim();
110
+ const block = text.slice(inline[0].length);
111
+ const argsRecord = parseTaggedArgBlock(block);
112
+ if (name && argsRecord) {
113
+ matches.push({
114
+ start: 0,
115
+ end: text.length,
116
+ call: {
117
+ name,
118
+ args: safeStringify(argsRecord)
119
+ }
120
+ });
121
+ }
122
+ }
123
+ }
124
+ matches.sort((a, b) => a.start - b.start);
125
+ const toolCalls = matches.map((entry, idx) => ({
126
+ id: `glm_tool_${choiceIndex}_${idx + 1}`,
127
+ type: 'function',
128
+ function: {
129
+ name: entry.call.name,
130
+ arguments: entry.call.args
131
+ }
132
+ }));
133
+ matches.sort((a, b) => b.start - a.start);
134
+ let cleaned = text;
135
+ for (const entry of matches) {
136
+ cleaned = cleaned.slice(0, entry.start) + cleaned.slice(entry.end);
137
+ }
138
+ const reasoningText = cleaned.trim();
139
+ return {
140
+ toolCalls,
141
+ reasoningText: reasoningText.length ? reasoningText : undefined
142
+ };
143
+ }
144
+ function parseToolCall(body, nameHint) {
145
+ if (!body || typeof body !== 'string') {
146
+ return null;
147
+ }
148
+ const trimmed = body.trim();
149
+ if (!trimmed.length) {
150
+ return null;
151
+ }
152
+ try {
153
+ const parsed = JSON.parse(trimmed);
154
+ if (!parsed || typeof parsed !== 'object') {
155
+ return null;
156
+ }
157
+ const record = parsed;
158
+ const candidateName = (typeof record.name === 'string' && record.name.trim().length
159
+ ? record.name.trim()
160
+ : undefined) ??
161
+ (typeof record.tool_name === 'string' && record.tool_name.trim().length
162
+ ? record.tool_name.trim()
163
+ : undefined) ??
164
+ (typeof record.tool === 'string' && record.tool.trim().length
165
+ ? record.tool.trim()
166
+ : undefined) ??
167
+ (nameHint && nameHint.trim().length ? nameHint.trim() : undefined);
168
+ if (!candidateName) {
169
+ return null;
170
+ }
171
+ const argsSource = record.arguments ??
172
+ record.input ??
173
+ record.params ??
174
+ record.parameters ??
175
+ record.payload ??
176
+ {};
177
+ let args = '{}';
178
+ if (typeof argsSource === 'string' && argsSource.trim().length) {
179
+ args = argsSource.trim();
180
+ }
181
+ else {
182
+ try {
183
+ args = JSON.stringify(argsSource ?? {});
184
+ }
185
+ catch {
186
+ args = '{}';
187
+ }
188
+ }
189
+ return { name: candidateName, args };
190
+ }
191
+ catch {
192
+ return null;
193
+ }
194
+ }
195
+ function applyTaggedArgPatterns(text, matches) {
196
+ if (!text || typeof text !== 'string') {
197
+ return;
198
+ }
199
+ GLM_TAGGED_SEQUENCE.lastIndex = 0;
200
+ let exec;
201
+ while ((exec = GLM_TAGGED_SEQUENCE.exec(text))) {
202
+ let name = typeof exec[1] === 'string' ? exec[1].trim() : '';
203
+ let block = exec[2] ?? '';
204
+ if (!name) {
205
+ const inline = GLM_INLINE_NAME.exec(block);
206
+ if (inline && inline[1]) {
207
+ name = inline[1].trim();
208
+ block = block.slice(inline[0].length);
209
+ }
210
+ }
211
+ if (!name) {
212
+ continue;
213
+ }
214
+ const argsRecord = parseTaggedArgBlock(block);
215
+ if (!argsRecord) {
216
+ continue;
217
+ }
218
+ matches.push({
219
+ start: exec.index,
220
+ end: exec.index + exec[0].length,
221
+ call: {
222
+ name,
223
+ args: safeStringify(argsRecord)
224
+ }
225
+ });
226
+ }
227
+ }
228
+ function parseTaggedArgBlock(block) {
229
+ if (!block || typeof block !== 'string') {
230
+ return null;
231
+ }
232
+ const record = {};
233
+ GLM_TAGGED_BLOCK.lastIndex = 0;
234
+ let exec;
235
+ while ((exec = GLM_TAGGED_BLOCK.exec(block))) {
236
+ const key = typeof exec[1] === 'string' ? exec[1].trim() : '';
237
+ if (!key) {
238
+ continue;
239
+ }
240
+ const rawValue = typeof exec[2] === 'string' ? exec[2].trim() : '';
241
+ record[key] = coerceTaggedValue(rawValue);
242
+ }
243
+ return Object.keys(record).length ? record : null;
244
+ }
245
+ function coerceTaggedValue(raw) {
246
+ if (!raw) {
247
+ return '';
248
+ }
249
+ const trimmed = raw.trim();
250
+ try {
251
+ return JSON.parse(trimmed);
252
+ }
253
+ catch {
254
+ return trimmed;
255
+ }
256
+ }
257
+ function safeStringify(value) {
258
+ try {
259
+ return JSON.stringify(value ?? {});
260
+ }
261
+ catch {
262
+ return '{}';
263
+ }
264
+ }