micro-models-agent 0.19.0 → 0.19.1
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/package.json +1 -1
- package/dist/main.js +0 -18225
- package/dist/modules/hallucination/factual.js +0 -561
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
import { t } from "../../i18n/index";
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
const FILE_EXTENSIONS = new Set([
|
|
5
|
-
"ts",
|
|
6
|
-
"tsx",
|
|
7
|
-
"js",
|
|
8
|
-
"jsx",
|
|
9
|
-
"mjs",
|
|
10
|
-
"cjs",
|
|
11
|
-
"mts",
|
|
12
|
-
"cts",
|
|
13
|
-
"json",
|
|
14
|
-
"md",
|
|
15
|
-
"yaml",
|
|
16
|
-
"yml",
|
|
17
|
-
"py",
|
|
18
|
-
"rs",
|
|
19
|
-
"go",
|
|
20
|
-
"java",
|
|
21
|
-
"c",
|
|
22
|
-
"cpp",
|
|
23
|
-
"h",
|
|
24
|
-
"hpp",
|
|
25
|
-
"html",
|
|
26
|
-
"css",
|
|
27
|
-
"scss",
|
|
28
|
-
"less",
|
|
29
|
-
"vue",
|
|
30
|
-
"svelte",
|
|
31
|
-
"sh",
|
|
32
|
-
"bash",
|
|
33
|
-
"zsh",
|
|
34
|
-
"ps1",
|
|
35
|
-
"bat",
|
|
36
|
-
"cmd",
|
|
37
|
-
"txt",
|
|
38
|
-
"env",
|
|
39
|
-
"env.local",
|
|
40
|
-
"env.production",
|
|
41
|
-
"gitignore",
|
|
42
|
-
"dockerignore",
|
|
43
|
-
"dockerfile",
|
|
44
|
-
"makefile",
|
|
45
|
-
"cmake",
|
|
46
|
-
"toml",
|
|
47
|
-
"lock",
|
|
48
|
-
"config",
|
|
49
|
-
"log",
|
|
50
|
-
"xml",
|
|
51
|
-
"sql",
|
|
52
|
-
"graphql",
|
|
53
|
-
"proto",
|
|
54
|
-
"wasm",
|
|
55
|
-
]);
|
|
56
|
-
const VERSION_PATTERN = /^\d+(\.\d+)*$/;
|
|
57
|
-
const COMMON_WORDS = new Set([
|
|
58
|
-
"node.js",
|
|
59
|
-
"Node.js",
|
|
60
|
-
"console.log",
|
|
61
|
-
"console.error",
|
|
62
|
-
"console.warn",
|
|
63
|
-
"console.info",
|
|
64
|
-
"Math.floor",
|
|
65
|
-
"Math.ceil",
|
|
66
|
-
"Math.round",
|
|
67
|
-
"Math.max",
|
|
68
|
-
"Math.min",
|
|
69
|
-
"JSON.parse",
|
|
70
|
-
"JSON.stringify",
|
|
71
|
-
"Object.keys",
|
|
72
|
-
"Object.values",
|
|
73
|
-
"Object.entries",
|
|
74
|
-
"Array.from",
|
|
75
|
-
"Array.isArray",
|
|
76
|
-
"Date.now",
|
|
77
|
-
"Date.parse",
|
|
78
|
-
"RegExp",
|
|
79
|
-
"Promise",
|
|
80
|
-
]);
|
|
81
|
-
/**
|
|
82
|
-
* Words that indicate the model is talking about a file or file operation.
|
|
83
|
-
* Used to filter out false positives where a dotted term is just a
|
|
84
|
-
* technology name (e.g. "React/Next.js component") rather than a file path.
|
|
85
|
-
*
|
|
86
|
-
* Includes base forms plus common English and Russian conjugations.
|
|
87
|
-
*/
|
|
88
|
-
const FILE_OPERATION_CONTEXT = new Set([
|
|
89
|
-
// English file operation verbs
|
|
90
|
-
"create",
|
|
91
|
-
"created",
|
|
92
|
-
"creating",
|
|
93
|
-
"update",
|
|
94
|
-
"updated",
|
|
95
|
-
"updating",
|
|
96
|
-
"edit",
|
|
97
|
-
"edited",
|
|
98
|
-
"editing",
|
|
99
|
-
"modify",
|
|
100
|
-
"modified",
|
|
101
|
-
"modifying",
|
|
102
|
-
"change",
|
|
103
|
-
"changed",
|
|
104
|
-
"changing",
|
|
105
|
-
"delete",
|
|
106
|
-
"deleted",
|
|
107
|
-
"deleting",
|
|
108
|
-
"remove",
|
|
109
|
-
"removed",
|
|
110
|
-
"removing",
|
|
111
|
-
"write",
|
|
112
|
-
"wrote",
|
|
113
|
-
"written",
|
|
114
|
-
"writing",
|
|
115
|
-
"read",
|
|
116
|
-
"reading",
|
|
117
|
-
"check",
|
|
118
|
-
"checked",
|
|
119
|
-
"checking",
|
|
120
|
-
"open",
|
|
121
|
-
"opened",
|
|
122
|
-
"opening",
|
|
123
|
-
"fix",
|
|
124
|
-
"fixed",
|
|
125
|
-
"fixing",
|
|
126
|
-
"rename",
|
|
127
|
-
"renamed",
|
|
128
|
-
"renaming",
|
|
129
|
-
"move",
|
|
130
|
-
"moved",
|
|
131
|
-
"moving",
|
|
132
|
-
"copy",
|
|
133
|
-
"copied",
|
|
134
|
-
"copying",
|
|
135
|
-
"make",
|
|
136
|
-
"made",
|
|
137
|
-
"making",
|
|
138
|
-
"build",
|
|
139
|
-
"built",
|
|
140
|
-
"building",
|
|
141
|
-
"run",
|
|
142
|
-
"ran",
|
|
143
|
-
"running",
|
|
144
|
-
"test",
|
|
145
|
-
"tested",
|
|
146
|
-
"testing",
|
|
147
|
-
"import",
|
|
148
|
-
"imported",
|
|
149
|
-
"importing",
|
|
150
|
-
"export",
|
|
151
|
-
"exported",
|
|
152
|
-
"exporting",
|
|
153
|
-
// English file nouns
|
|
154
|
-
"file",
|
|
155
|
-
"files",
|
|
156
|
-
"path",
|
|
157
|
-
"paths",
|
|
158
|
-
"directory",
|
|
159
|
-
"directories",
|
|
160
|
-
"folder",
|
|
161
|
-
"folders",
|
|
162
|
-
"module",
|
|
163
|
-
"modules",
|
|
164
|
-
"script",
|
|
165
|
-
"scripts",
|
|
166
|
-
"source",
|
|
167
|
-
"sources",
|
|
168
|
-
"code",
|
|
169
|
-
// Russian file operation verbs
|
|
170
|
-
"создать",
|
|
171
|
-
"создал",
|
|
172
|
-
"создала",
|
|
173
|
-
"создало",
|
|
174
|
-
"создаю",
|
|
175
|
-
"создаем",
|
|
176
|
-
"создает",
|
|
177
|
-
"создан",
|
|
178
|
-
"создание",
|
|
179
|
-
"создается",
|
|
180
|
-
"обновить",
|
|
181
|
-
"обновил",
|
|
182
|
-
"обновила",
|
|
183
|
-
"обновило",
|
|
184
|
-
"обновляю",
|
|
185
|
-
"обновляем",
|
|
186
|
-
"обновляет",
|
|
187
|
-
"обновлен",
|
|
188
|
-
"обновление",
|
|
189
|
-
"обновляется",
|
|
190
|
-
"изменить",
|
|
191
|
-
"изменил",
|
|
192
|
-
"изменила",
|
|
193
|
-
"изменило",
|
|
194
|
-
"изменяю",
|
|
195
|
-
"изменяем",
|
|
196
|
-
"изменяет",
|
|
197
|
-
"изменен",
|
|
198
|
-
"изменение",
|
|
199
|
-
"изменяется",
|
|
200
|
-
"редактировать",
|
|
201
|
-
"редактировал",
|
|
202
|
-
"редактировала",
|
|
203
|
-
"редактирую",
|
|
204
|
-
"редактируем",
|
|
205
|
-
"редактирует",
|
|
206
|
-
"редактирован",
|
|
207
|
-
"редактирование",
|
|
208
|
-
"редактируется",
|
|
209
|
-
"удалить",
|
|
210
|
-
"удалил",
|
|
211
|
-
"удалила",
|
|
212
|
-
"удалило",
|
|
213
|
-
"удаляю",
|
|
214
|
-
"удаляем",
|
|
215
|
-
"удаляет",
|
|
216
|
-
"удален",
|
|
217
|
-
"удаление",
|
|
218
|
-
"удаляется",
|
|
219
|
-
"добавить",
|
|
220
|
-
"добавил",
|
|
221
|
-
"добавила",
|
|
222
|
-
"добавило",
|
|
223
|
-
"добавляю",
|
|
224
|
-
"добавляем",
|
|
225
|
-
"добавляет",
|
|
226
|
-
"добавлен",
|
|
227
|
-
"добавление",
|
|
228
|
-
"добавляется",
|
|
229
|
-
"написать",
|
|
230
|
-
"написал",
|
|
231
|
-
"написала",
|
|
232
|
-
"написало",
|
|
233
|
-
"напишу",
|
|
234
|
-
"написано",
|
|
235
|
-
"писать",
|
|
236
|
-
"пишу",
|
|
237
|
-
"пишем",
|
|
238
|
-
"пишет",
|
|
239
|
-
"написание",
|
|
240
|
-
"пишется",
|
|
241
|
-
"записать",
|
|
242
|
-
"записал",
|
|
243
|
-
"записала",
|
|
244
|
-
"записываю",
|
|
245
|
-
"записываем",
|
|
246
|
-
"записывает",
|
|
247
|
-
"записан",
|
|
248
|
-
"запись",
|
|
249
|
-
"записывается",
|
|
250
|
-
"прочитать",
|
|
251
|
-
"прочитал",
|
|
252
|
-
"прочитала",
|
|
253
|
-
"прочитало",
|
|
254
|
-
"прочитаю",
|
|
255
|
-
"прочитано",
|
|
256
|
-
"читать",
|
|
257
|
-
"читаю",
|
|
258
|
-
"читаем",
|
|
259
|
-
"читает",
|
|
260
|
-
"чтение",
|
|
261
|
-
"читается",
|
|
262
|
-
"проверить",
|
|
263
|
-
"проверил",
|
|
264
|
-
"проверила",
|
|
265
|
-
"проверило",
|
|
266
|
-
"проверю",
|
|
267
|
-
"проверено",
|
|
268
|
-
"проверка",
|
|
269
|
-
"проверяю",
|
|
270
|
-
"проверяем",
|
|
271
|
-
"проверяет",
|
|
272
|
-
"проверяется",
|
|
273
|
-
"открыть",
|
|
274
|
-
"открыл",
|
|
275
|
-
"открыла",
|
|
276
|
-
"открыло",
|
|
277
|
-
"открою",
|
|
278
|
-
"открыто",
|
|
279
|
-
"открытие",
|
|
280
|
-
"открываю",
|
|
281
|
-
"открываем",
|
|
282
|
-
"открывает",
|
|
283
|
-
"открывается",
|
|
284
|
-
"найти",
|
|
285
|
-
"нашел",
|
|
286
|
-
"нашла",
|
|
287
|
-
"нашло",
|
|
288
|
-
"найду",
|
|
289
|
-
"найдено",
|
|
290
|
-
"поиск",
|
|
291
|
-
"ищу",
|
|
292
|
-
"ищем",
|
|
293
|
-
"ищет",
|
|
294
|
-
"ищется",
|
|
295
|
-
"исправить",
|
|
296
|
-
"исправил",
|
|
297
|
-
"исправила",
|
|
298
|
-
"исправило",
|
|
299
|
-
"исправлю",
|
|
300
|
-
"исправлено",
|
|
301
|
-
"исправление",
|
|
302
|
-
"исправляю",
|
|
303
|
-
"исправляем",
|
|
304
|
-
"исправляет",
|
|
305
|
-
"исправляется",
|
|
306
|
-
"переместить",
|
|
307
|
-
"переместил",
|
|
308
|
-
"переместила",
|
|
309
|
-
"переместило",
|
|
310
|
-
"перемещу",
|
|
311
|
-
"перемещен",
|
|
312
|
-
"перемещение",
|
|
313
|
-
"перемещаю",
|
|
314
|
-
"перемещаем",
|
|
315
|
-
"перемещает",
|
|
316
|
-
"перемещается",
|
|
317
|
-
"переименовать",
|
|
318
|
-
"переименовал",
|
|
319
|
-
"переименовала",
|
|
320
|
-
"переименую",
|
|
321
|
-
"переименован",
|
|
322
|
-
"переименование",
|
|
323
|
-
"переименовываю",
|
|
324
|
-
"переименовываем",
|
|
325
|
-
"переименовывает",
|
|
326
|
-
"переименовывается",
|
|
327
|
-
"скопировать",
|
|
328
|
-
"скопировал",
|
|
329
|
-
"скопировала",
|
|
330
|
-
"скопировало",
|
|
331
|
-
"скопирую",
|
|
332
|
-
"скопирован",
|
|
333
|
-
"копирование",
|
|
334
|
-
"копирую",
|
|
335
|
-
"копируем",
|
|
336
|
-
"копирует",
|
|
337
|
-
"копируется",
|
|
338
|
-
"собрать",
|
|
339
|
-
"собрал",
|
|
340
|
-
"собрала",
|
|
341
|
-
"собран",
|
|
342
|
-
"сборка",
|
|
343
|
-
"собираю",
|
|
344
|
-
"собираем",
|
|
345
|
-
"собирает",
|
|
346
|
-
"собирается",
|
|
347
|
-
"запустить",
|
|
348
|
-
"запустил",
|
|
349
|
-
"запустила",
|
|
350
|
-
"запустило",
|
|
351
|
-
"запущен",
|
|
352
|
-
"запуск",
|
|
353
|
-
"запускаю",
|
|
354
|
-
"запускаем",
|
|
355
|
-
"запускает",
|
|
356
|
-
"запускается",
|
|
357
|
-
"тестировать",
|
|
358
|
-
"тестировал",
|
|
359
|
-
"тестировала",
|
|
360
|
-
"тестирую",
|
|
361
|
-
"тестируем",
|
|
362
|
-
"тестирует",
|
|
363
|
-
"тестирован",
|
|
364
|
-
"тестирование",
|
|
365
|
-
"тестируется",
|
|
366
|
-
"импортировать",
|
|
367
|
-
"импортировал",
|
|
368
|
-
"импортировала",
|
|
369
|
-
"импортирую",
|
|
370
|
-
"импортируем",
|
|
371
|
-
"импортирует",
|
|
372
|
-
"импортирован",
|
|
373
|
-
"импорт",
|
|
374
|
-
"импортируется",
|
|
375
|
-
"экспортировать",
|
|
376
|
-
"экспортировал",
|
|
377
|
-
"экспортировала",
|
|
378
|
-
"экспортирую",
|
|
379
|
-
"экспортируем",
|
|
380
|
-
"экспортирует",
|
|
381
|
-
"экспортирован",
|
|
382
|
-
"экспорт",
|
|
383
|
-
"экспортируется",
|
|
384
|
-
// Russian file nouns
|
|
385
|
-
"файл",
|
|
386
|
-
"файла",
|
|
387
|
-
"файле",
|
|
388
|
-
"файлы",
|
|
389
|
-
"файлов",
|
|
390
|
-
"файлам",
|
|
391
|
-
"файлами",
|
|
392
|
-
"путь",
|
|
393
|
-
"пути",
|
|
394
|
-
"путей",
|
|
395
|
-
"путем",
|
|
396
|
-
"путям",
|
|
397
|
-
"путями",
|
|
398
|
-
"директория",
|
|
399
|
-
"директории",
|
|
400
|
-
"директорию",
|
|
401
|
-
"директорий",
|
|
402
|
-
"директориях",
|
|
403
|
-
"директориями",
|
|
404
|
-
"папка",
|
|
405
|
-
"папке",
|
|
406
|
-
"папки",
|
|
407
|
-
"папок",
|
|
408
|
-
"папкам",
|
|
409
|
-
"папками",
|
|
410
|
-
"модуль",
|
|
411
|
-
"модуля",
|
|
412
|
-
"модуле",
|
|
413
|
-
"модули",
|
|
414
|
-
"модулей",
|
|
415
|
-
"модулям",
|
|
416
|
-
"модулями",
|
|
417
|
-
"скрипт",
|
|
418
|
-
"скрипта",
|
|
419
|
-
"скрипте",
|
|
420
|
-
"скрипты",
|
|
421
|
-
"скриптов",
|
|
422
|
-
"скриптам",
|
|
423
|
-
"скриптами",
|
|
424
|
-
"код",
|
|
425
|
-
"кода",
|
|
426
|
-
"коде",
|
|
427
|
-
"коды",
|
|
428
|
-
"кодов",
|
|
429
|
-
"кодам",
|
|
430
|
-
"кодами",
|
|
431
|
-
]);
|
|
432
|
-
export class FactualCheck {
|
|
433
|
-
knownPaths = new Set();
|
|
434
|
-
createdPaths = new Set();
|
|
435
|
-
readFiles = new Set();
|
|
436
|
-
baseDir = process.cwd();
|
|
437
|
-
setBaseDir(dir) {
|
|
438
|
-
this.baseDir = dir;
|
|
439
|
-
}
|
|
440
|
-
trackReadPath(path) {
|
|
441
|
-
this.knownPaths.add(path);
|
|
442
|
-
const base = path.split(/[/\\]/).pop();
|
|
443
|
-
if (base && base !== path)
|
|
444
|
-
this.knownPaths.add(base);
|
|
445
|
-
// Track that this file was actually read by the agent
|
|
446
|
-
this.readFiles.add(base || path);
|
|
447
|
-
}
|
|
448
|
-
trackCreatedPath(path) {
|
|
449
|
-
this.knownPaths.add(path);
|
|
450
|
-
const base = path.split(/[/\\]/).pop();
|
|
451
|
-
if (base && base !== path)
|
|
452
|
-
this.knownPaths.add(base);
|
|
453
|
-
this.createdPaths.add(path);
|
|
454
|
-
}
|
|
455
|
-
trackDeletedPath(path) {
|
|
456
|
-
this.knownPaths.add(path);
|
|
457
|
-
const base = path.split(/[/\\]/).pop();
|
|
458
|
-
if (base && base !== path)
|
|
459
|
-
this.knownPaths.add(base);
|
|
460
|
-
this.createdPaths.delete(path);
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Register file paths found in a document (e.g. structure.md, README).
|
|
464
|
-
* Files mentioned in project documentation are not hallucinations.
|
|
465
|
-
*/
|
|
466
|
-
trackDocumentContent(content) {
|
|
467
|
-
// Match common path patterns in documentation
|
|
468
|
-
const pathPatterns = /(?:^|\s)([\w\-./]+\.\w{1,10})(?:\s|$|[,;)])/gm;
|
|
469
|
-
let match;
|
|
470
|
-
while ((match = pathPatterns.exec(content)) !== null) {
|
|
471
|
-
const file = match[1];
|
|
472
|
-
if (file.includes("/") || file.includes("\\")) {
|
|
473
|
-
this.knownPaths.add(file);
|
|
474
|
-
}
|
|
475
|
-
const base = file.split(/[/\\]/).pop();
|
|
476
|
-
if (base)
|
|
477
|
-
this.knownPaths.add(base);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
pathExistsOnDisk(path) {
|
|
481
|
-
try {
|
|
482
|
-
// Skip absolute paths — they're either system paths or outside the project.
|
|
483
|
-
// On Windows, existsSync('/...') can hang on certain paths.
|
|
484
|
-
if (path.startsWith("/") ||
|
|
485
|
-
path.startsWith("~") ||
|
|
486
|
-
/^[A-Za-z]:/.test(path)) {
|
|
487
|
-
return false;
|
|
488
|
-
}
|
|
489
|
-
return existsSync(join(this.baseDir, path));
|
|
490
|
-
}
|
|
491
|
-
catch {
|
|
492
|
-
return false;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Check whether a matched path-like string appears in a context that suggests
|
|
497
|
-
* the model is actually talking about a file operation, rather than just
|
|
498
|
-
* mentioning a technology or framework name (e.g. "React/Next.js").
|
|
499
|
-
*
|
|
500
|
-
* Looks at the 8 words immediately preceding the match.
|
|
501
|
-
*/
|
|
502
|
-
hasFileOperationContext(text, match) {
|
|
503
|
-
const escaped = match.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
504
|
-
const regex = new RegExp(escaped, "gi");
|
|
505
|
-
let result;
|
|
506
|
-
while ((result = regex.exec(text)) !== null) {
|
|
507
|
-
const start = result.index;
|
|
508
|
-
const before = text.slice(Math.max(0, start - 100), start);
|
|
509
|
-
const words = [...before.matchAll(/[a-zA-Zа-яА-ЯёЁ0-9]+/g)].map((m) => m[0].toLowerCase());
|
|
510
|
-
const lastWords = words.slice(-8);
|
|
511
|
-
if (lastWords.some((w) => FILE_OPERATION_CONTEXT.has(w))) {
|
|
512
|
-
return true;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
return false;
|
|
516
|
-
}
|
|
517
|
-
validate(response) {
|
|
518
|
-
const pathRegex = /[\w\-./]+\.\w+/g;
|
|
519
|
-
const mentionedPaths = response.match(pathRegex) || [];
|
|
520
|
-
const unknownPaths = mentionedPaths.filter((p) => {
|
|
521
|
-
if (this.knownPaths.has(p))
|
|
522
|
-
return false;
|
|
523
|
-
if (VERSION_PATTERN.test(p))
|
|
524
|
-
return false;
|
|
525
|
-
if (COMMON_WORDS.has(p))
|
|
526
|
-
return false;
|
|
527
|
-
// Skip absolute paths — agent responses use relative paths; absolute
|
|
528
|
-
// paths are either system paths or URLs, and existsSync can hang on
|
|
529
|
-
// Windows for root-relative paths like "/page.html".
|
|
530
|
-
if (p.startsWith("/") || p.startsWith("~") || /^[A-Za-z]:/.test(p))
|
|
531
|
-
return false;
|
|
532
|
-
if (this.pathExistsOnDisk(p))
|
|
533
|
-
return false;
|
|
534
|
-
const ext = p.split(".").pop()?.toLowerCase() || "";
|
|
535
|
-
if (!FILE_EXTENSIONS.has(ext))
|
|
536
|
-
return false;
|
|
537
|
-
const basename = p
|
|
538
|
-
.split("/")
|
|
539
|
-
.pop()
|
|
540
|
-
?.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
541
|
-
if (basename) {
|
|
542
|
-
const urlPattern = new RegExp(`(https?|file)://[^\\s]*${basename}`);
|
|
543
|
-
if (urlPattern.test(response))
|
|
544
|
-
return false;
|
|
545
|
-
}
|
|
546
|
-
if (!this.hasFileOperationContext(response, p))
|
|
547
|
-
return false;
|
|
548
|
-
return true;
|
|
549
|
-
});
|
|
550
|
-
if (unknownPaths.length > 0) {
|
|
551
|
-
const uniquePaths = [...new Set(unknownPaths)];
|
|
552
|
-
return {
|
|
553
|
-
status: "warn",
|
|
554
|
-
reason: t("hall.unknown_paths", {
|
|
555
|
-
paths: uniquePaths.slice(0, 3).join(", "),
|
|
556
|
-
}),
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
return { status: "pass" };
|
|
560
|
-
}
|
|
561
|
-
}
|