распознавание: большие раздачи размечаются целиком, файлы вне плана видны

- модель адресует файл номером строки нашего списка вместо копии пути: ответ
  на 180 файлов вместо ~15k токенов стоит ~2.5k, усечение сотней снято,
  max_files и max_tokens ушли в [recognition], correction-ретрай больше не
  переприсылает список
- негодный элемент ответа отбрасывается поимённой причиной, обрыв генерации и
  отказ по размеру запроса названы своими причинами, покрытие плана блокирует
  авто только при непокрытом видеофайле
- раскладка показывает все файлы раздачи со строками «не в плане» и полным
  порядком сортировки; снимок списка файлов лёг рядом с планом (миграция 0012)
This commit is contained in:
av
2026-09-02 08:54:58 +03:00
parent 01ac0430a9
commit fc9a3b4066
26 changed files with 1978 additions and 201 deletions
+51
View File
@@ -131,3 +131,54 @@ func TestValidate_Errors(t *testing.T) {
})
}
}
// Пределы распознавания: дефолты применяются к конфигу, где секции нет вовсе,
// а нулевые/отрицательные значения отвергаются — молча дефолтить их нельзя,
// иначе выключенный предохранитель неотличим от настроенного.
func TestValidate_RecognitionLimits(t *testing.T) {
if d := Default().Recognition; d.MaxFiles != 500 || d.MaxTokens != 8000 {
t.Errorf("дефолты = %+v, want max_files 500 / max_tokens 8000", d)
}
dir := t.TempDir()
for _, p := range []string{"downloads", "movies", "series"} {
if err := os.MkdirAll(filepath.Join(dir, p), 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
}
path := filepath.Join(dir, "config.toml")
body := "[qbittorrent]\nurl = \"http://qbit:8080\"\n" +
"[paths]\ndownloads = \"" + filepath.Join(dir, "downloads") + "\"\n" +
"movies = \"" + filepath.Join(dir, "movies") + "\"\n" +
"series = \"" + filepath.Join(dir, "series") + "\"\n"
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatalf("write config: %v", err)
}
cfg, err := Load(path)
if err != nil {
t.Fatalf("Load: %v", err)
}
if cfg.Recognition.MaxFiles != 500 || cfg.Recognition.MaxTokens != 8000 {
t.Errorf("конфиг без секции не получил дефолты: %+v", cfg.Recognition)
}
for _, tt := range []struct {
name string
mut func(*Config)
want string
}{
{"max_files 0", func(c *Config) { c.Recognition.MaxFiles = 0 }, "recognition.max_files"},
{"max_files negative", func(c *Config) { c.Recognition.MaxFiles = -1 }, "recognition.max_files"},
{"max_tokens 0", func(c *Config) { c.Recognition.MaxTokens = 0 }, "recognition.max_tokens"},
{"max_tokens negative", func(c *Config) { c.Recognition.MaxTokens = -10 }, "recognition.max_tokens"},
} {
t.Run(tt.name, func(t *testing.T) {
c := validCfg(t)
tt.mut(c)
err := c.validate()
if err == nil || !strings.Contains(err.Error(), tt.want) {
t.Errorf("err = %v, want contains %q", err, tt.want)
}
})
}
}