@jsonstudio/llms 0.6.753 → 0.6.795
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversion/compat/actions/apply-patch-fixer.d.ts +1 -0
- package/dist/conversion/compat/actions/apply-patch-fixer.js +30 -0
- package/dist/conversion/compat/actions/apply-patch-format-fixer.d.ts +1 -0
- package/dist/conversion/compat/actions/apply-patch-format-fixer.js +233 -0
- package/dist/conversion/compat/actions/index.d.ts +2 -0
- package/dist/conversion/compat/actions/index.js +2 -0
- package/dist/conversion/compat/profiles/chat-gemini.json +15 -15
- package/dist/conversion/compat/profiles/chat-glm.json +194 -194
- package/dist/conversion/compat/profiles/chat-iflow.json +199 -199
- package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -43
- package/dist/conversion/compat/profiles/chat-qwen.json +20 -20
- package/dist/conversion/compat/profiles/responses-c4m.json +42 -42
- package/dist/conversion/compat/profiles/responses-output2choices-test.json +10 -9
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +6 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +35 -0
- package/dist/conversion/shared/bridge-message-utils.d.ts +1 -0
- package/dist/conversion/shared/bridge-message-utils.js +7 -0
- package/dist/conversion/shared/bridge-policies.js +8 -8
- package/dist/conversion/shared/tool-governor.js +18 -23
- package/dist/filters/special/response-tool-arguments-stringify.js +3 -22
- package/dist/router/virtual-router/engine.d.ts +5 -0
- package/dist/router/virtual-router/engine.js +21 -0
- package/dist/tools/apply-patch/regression-capturer.d.ts +12 -0
- package/dist/tools/apply-patch/regression-capturer.js +112 -0
- package/dist/tools/apply-patch/structured.d.ts +20 -0
- package/dist/tools/apply-patch/structured.js +441 -0
- package/dist/tools/apply-patch/validator.d.ts +8 -0
- package/dist/tools/apply-patch/validator.js +466 -0
- package/dist/tools/apply-patch-structured.d.ts +1 -20
- package/dist/tools/apply-patch-structured.js +1 -277
- package/dist/tools/args-json.d.ts +1 -0
- package/dist/tools/args-json.js +175 -0
- package/dist/tools/exec-command/normalize.d.ts +17 -0
- package/dist/tools/exec-command/normalize.js +112 -0
- package/dist/tools/exec-command/regression-capturer.d.ts +11 -0
- package/dist/tools/exec-command/regression-capturer.js +144 -0
- package/dist/tools/exec-command/validator.d.ts +6 -0
- package/dist/tools/exec-command/validator.js +22 -0
- package/dist/tools/patch-args-normalizer.d.ts +15 -0
- package/dist/tools/patch-args-normalizer.js +472 -0
- package/dist/tools/patch-regression-capturer.d.ts +1 -0
- package/dist/tools/patch-regression-capturer.js +1 -0
- package/dist/tools/tool-registry.js +36 -541
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { registerBridgeAction } from '../../shared/bridge-actions.js';
|
|
2
|
+
import { ensureMessagesArray } from '../../shared/bridge-message-utils.js';
|
|
3
|
+
import { validateToolCall } from '../../../tools/tool-registry.js';
|
|
4
|
+
const fixApplyPatchAction = (ctx) => {
|
|
5
|
+
const messages = ensureMessagesArray(ctx.state);
|
|
6
|
+
for (const message of messages) {
|
|
7
|
+
if (message.role !== 'assistant')
|
|
8
|
+
continue;
|
|
9
|
+
if (!Array.isArray(message.tool_calls))
|
|
10
|
+
continue;
|
|
11
|
+
for (const toolCall of message.tool_calls) {
|
|
12
|
+
if (toolCall.type !== 'function')
|
|
13
|
+
continue;
|
|
14
|
+
const fn = toolCall.function;
|
|
15
|
+
if (!fn || fn.name !== 'apply_patch')
|
|
16
|
+
continue;
|
|
17
|
+
const rawArgs = fn.arguments;
|
|
18
|
+
if (typeof rawArgs !== 'string')
|
|
19
|
+
continue;
|
|
20
|
+
const validation = validateToolCall('apply_patch', rawArgs);
|
|
21
|
+
if (validation?.ok && typeof validation.normalizedArgs === 'string') {
|
|
22
|
+
if (validation.normalizedArgs !== rawArgs) {
|
|
23
|
+
fn.arguments = validation.normalizedArgs;
|
|
24
|
+
toolCall._fixed_apply_patch = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
registerBridgeAction('compat.fix-apply-patch', fixApplyPatchAction);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { registerBridgeAction } from '../../shared/bridge-actions.js';
|
|
2
|
+
import { ensureMessagesArray } from '../../shared/bridge-message-utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* apply-patch-format-fixer.ts
|
|
5
|
+
*
|
|
6
|
+
* 专门处理 apply_patch 的格式错误修复,包括:
|
|
7
|
+
* 1. 修复缺失 '*** Begin Patch' 包装的 raw diff
|
|
8
|
+
* 2. 修复混用 git diff 头部的情况
|
|
9
|
+
* 3. 修复 hunk 中缺少前缀的代码行
|
|
10
|
+
*
|
|
11
|
+
* 注意:仅修复格式问题,不修复上下文匹配问题
|
|
12
|
+
*/
|
|
13
|
+
import { captureApplyPatchRegression } from '../../../tools/patch-regression-capturer.js';
|
|
14
|
+
/**
|
|
15
|
+
* 检测是否是裸 diff 格式(没有 Begin Patch 包装)
|
|
16
|
+
*/
|
|
17
|
+
function isBareDiff(text) {
|
|
18
|
+
const trimmed = text.trim();
|
|
19
|
+
// 检测典型的 git diff 标记
|
|
20
|
+
const hasGitDiffMarkers = trimmed.startsWith('diff --git') ||
|
|
21
|
+
/^---\s+a\//.test(trimmed) ||
|
|
22
|
+
/^\+\+\+\s+b\//.test(trimmed) ||
|
|
23
|
+
/^@@\s+-\d+,\d+\s+\+\d+,\d+\s+@@/.test(trimmed);
|
|
24
|
+
// 确保不是标准的 apply_patch 格式
|
|
25
|
+
const hasApplyPatchMarkers = trimmed.includes('*** Begin Patch') ||
|
|
26
|
+
trimmed.includes('*** Update File:') ||
|
|
27
|
+
trimmed.includes('*** Add File:');
|
|
28
|
+
return hasGitDiffMarkers && !hasApplyPatchMarkers;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 转换裸 diff 为 apply_patch 格式
|
|
32
|
+
*/
|
|
33
|
+
function convertBareDiffToApplyPatch(diffText) {
|
|
34
|
+
const lines = diffText.replace(/\r\n/g, '\n').split('\n');
|
|
35
|
+
const patches = new Map();
|
|
36
|
+
let currentFile = '';
|
|
37
|
+
let currentHunk = [];
|
|
38
|
+
let inHunk = false;
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
// 检测文件头
|
|
41
|
+
const diffMatch = line.match(/^diff --git\s+a\/(.+?)\s+b\/(.+)$/);
|
|
42
|
+
if (diffMatch) {
|
|
43
|
+
// 保存上一个文件的 hunk
|
|
44
|
+
if (currentFile && currentHunk.length > 0) {
|
|
45
|
+
patches.set(currentFile, {
|
|
46
|
+
header: `*** Update File: ${diffMatch[2]}`,
|
|
47
|
+
hunk: [...currentHunk]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
currentFile = diffMatch[2];
|
|
51
|
+
currentHunk = [];
|
|
52
|
+
inHunk = false;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// 检测 hunk 头
|
|
56
|
+
if (/^@@\s+-\d+/.test(line)) {
|
|
57
|
+
currentHunk.push(line);
|
|
58
|
+
inHunk = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// 在 hunk 中收集行
|
|
62
|
+
if (inHunk) {
|
|
63
|
+
// 确保每行都有正确的前缀
|
|
64
|
+
if (line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
|
|
65
|
+
currentHunk.push(line);
|
|
66
|
+
}
|
|
67
|
+
else if (line.trim() === '') {
|
|
68
|
+
currentHunk.push(' ');
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// 行缺少前缀,作为上下文行处理
|
|
72
|
+
currentHunk.push(' ' + line);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// 保存最后一个文件
|
|
77
|
+
if (currentFile && currentHunk.length > 0) {
|
|
78
|
+
patches.set(currentFile, {
|
|
79
|
+
header: `*** Update File: ${currentFile}`,
|
|
80
|
+
hunk: [...currentHunk]
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (patches.size === 0) {
|
|
84
|
+
// 如果无法解析,返回原始文本包装
|
|
85
|
+
return `*** Begin Patch\n${diffText}\n*** End Patch`;
|
|
86
|
+
}
|
|
87
|
+
// 构建 apply_patch 格式
|
|
88
|
+
const result = ['*** Begin Patch'];
|
|
89
|
+
for (const [file, { header, hunk }] of patches.entries()) {
|
|
90
|
+
result.push(header);
|
|
91
|
+
result.push(...hunk);
|
|
92
|
+
}
|
|
93
|
+
result.push('*** End Patch');
|
|
94
|
+
return result.join('\n');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 修复 hunk 中缺少前缀的行
|
|
98
|
+
*/
|
|
99
|
+
function fixHunkPrefixes(patchText) {
|
|
100
|
+
const lines = patchText.split('\n');
|
|
101
|
+
const result = [];
|
|
102
|
+
let inUpdateSection = false;
|
|
103
|
+
let afterHeader = false;
|
|
104
|
+
for (let i = 0; i < lines.length; i++) {
|
|
105
|
+
const line = lines[i];
|
|
106
|
+
if (line.startsWith('*** Begin Patch')) {
|
|
107
|
+
result.push(line);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (line.startsWith('*** End Patch')) {
|
|
111
|
+
result.push(line);
|
|
112
|
+
inUpdateSection = false;
|
|
113
|
+
afterHeader = false;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (line.startsWith('*** Update File:') || line.startsWith('*** Add File:') || line.startsWith('*** Delete File:')) {
|
|
117
|
+
result.push(line);
|
|
118
|
+
inUpdateSection = true;
|
|
119
|
+
afterHeader = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (inUpdateSection) {
|
|
123
|
+
// 跳过紧跟在 header 后的空行
|
|
124
|
+
if (afterHeader && line.trim() === '') {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
afterHeader = false;
|
|
128
|
+
// 检查是否是 hunk 行
|
|
129
|
+
if (/^@@\s+-\d+,\d+\s+\+\d+,\d+\s+@@/.test(line)) {
|
|
130
|
+
result.push(line);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
// 检查是否已经有正确前缀
|
|
134
|
+
if (/^[+-]\s/.test(line) || /^\s/.test(line)) {
|
|
135
|
+
result.push(line);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// 行缺少前缀,作为上下文行处理
|
|
139
|
+
if (line.trim() === '') {
|
|
140
|
+
result.push(' ');
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
result.push(' ' + line);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
result.push(line);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result.join('\n');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 主要的修复函数
|
|
154
|
+
*/
|
|
155
|
+
function fixApplyPatchFormat(argsStr) {
|
|
156
|
+
try {
|
|
157
|
+
let args;
|
|
158
|
+
try {
|
|
159
|
+
args = JSON.parse(argsStr);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { fixed: argsStr }; // 无法解析,返回原值
|
|
163
|
+
}
|
|
164
|
+
if (!args || typeof args !== 'object') {
|
|
165
|
+
return { fixed: argsStr };
|
|
166
|
+
}
|
|
167
|
+
let patch = args.patch || args.input || '';
|
|
168
|
+
if (typeof patch !== 'string') {
|
|
169
|
+
return { fixed: argsStr };
|
|
170
|
+
}
|
|
171
|
+
let modified = false;
|
|
172
|
+
let errorType;
|
|
173
|
+
// 修复 1: 裸 diff 缺少包装
|
|
174
|
+
if (isBareDiff(patch)) {
|
|
175
|
+
patch = convertBareDiffToApplyPatch(patch);
|
|
176
|
+
modified = true;
|
|
177
|
+
errorType = 'missing_begin_patch';
|
|
178
|
+
}
|
|
179
|
+
// 修复 2: hunk 中缺少前缀的行
|
|
180
|
+
const hasInvalidHunkLine = /"(.*?)".*Unexpected line found in update hunk/.test(patch);
|
|
181
|
+
if (hasInvalidHunkLine || !patch.match(/^@@/m)) {
|
|
182
|
+
const originalLength = patch.length;
|
|
183
|
+
patch = fixHunkPrefixes(patch);
|
|
184
|
+
if (patch.length !== originalLength) {
|
|
185
|
+
modified = true;
|
|
186
|
+
errorType = errorType || 'invalid_hunk_prefix';
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (modified) {
|
|
190
|
+
const newArgs = { ...args, patch, input: patch };
|
|
191
|
+
return { fixed: JSON.stringify(newArgs), errorType };
|
|
192
|
+
}
|
|
193
|
+
return { fixed: argsStr };
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return { fixed: argsStr };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Bridge action: 遍历消息,修复 apply_patch 格式问题
|
|
201
|
+
*/
|
|
202
|
+
const fixApplyPatchFormatAction = (ctx) => {
|
|
203
|
+
const messages = ensureMessagesArray(ctx.state);
|
|
204
|
+
for (const message of messages) {
|
|
205
|
+
if (message.role !== 'assistant')
|
|
206
|
+
continue;
|
|
207
|
+
if (!Array.isArray(message.tool_calls))
|
|
208
|
+
continue;
|
|
209
|
+
for (const toolCall of message.tool_calls) {
|
|
210
|
+
if (toolCall.type !== 'function')
|
|
211
|
+
continue;
|
|
212
|
+
const fn = toolCall.function;
|
|
213
|
+
if (!fn || fn.name !== 'apply_patch')
|
|
214
|
+
continue;
|
|
215
|
+
const rawArgs = fn.arguments;
|
|
216
|
+
if (typeof rawArgs !== 'string')
|
|
217
|
+
continue;
|
|
218
|
+
const { fixed, errorType } = fixApplyPatchFormat(rawArgs);
|
|
219
|
+
if (fixed !== rawArgs) {
|
|
220
|
+
// 保存可修复样本
|
|
221
|
+
captureApplyPatchRegression({
|
|
222
|
+
errorType: errorType || 'unknown_format_issue',
|
|
223
|
+
originalArgs: rawArgs,
|
|
224
|
+
fixerResult: fixed,
|
|
225
|
+
source: 'compat.fix-apply-patch-format'
|
|
226
|
+
});
|
|
227
|
+
fn.arguments = fixed;
|
|
228
|
+
toolCall._format_fixed = true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
registerBridgeAction('compat.fix-apply-patch-format', fixApplyPatchFormatAction);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
"id": "chat:gemini",
|
|
3
|
+
"protocol": "gemini-chat",
|
|
4
|
+
"request": {
|
|
5
|
+
"mappings": [
|
|
6
|
+
{ "action": "snapshot", "phase": "compat-pre" },
|
|
7
|
+
{ "action": "claude_thinking_tool_schema" },
|
|
8
|
+
{
|
|
9
|
+
"action": "gemini_web_search_request"
|
|
10
|
+
},
|
|
11
|
+
{ "action": "snapshot", "phase": "compat-post" }
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"response": {
|
|
15
|
+
"mappings": []
|
|
16
|
+
}
|
|
17
17
|
}
|