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

- модель адресует файл номером строки нашего списка вместо копии пути: ответ
  на 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
+42 -13
View File
@@ -151,14 +151,28 @@ func (w *Worker) finishRecognition(ctx context.Context, id, claim string, res re
provider, providerID = res.Match.Provider, res.Match.ProviderID
}
// Снимок списка файлов раздачи — из того же среза, по которому построен
// план (Result.Files), иначе номера в раскладке разъедутся с адресацией
// модели. Не сериализовался — оставляем NULL: UI назовёт список
// недоступным, а молча показать план как полный перечень нельзя.
var sourceFiles sql.NullString
if len(res.Files) > 0 {
if b, ferr := json.Marshal(res.Files); ferr != nil {
log.Warn("recognition marshal source files failed", "error", ferr)
} else {
sourceFiles = store.NullString(string(b))
}
}
rec := &store.Recognition{
DownloadID: id,
MediaType: store.NullString(string(res.Plan.Type)),
Title: store.NullString(res.Plan.Title),
Provider: store.NullString(provider),
ProviderID: store.NullString(providerID),
Plan: store.NullString(string(planJSON)),
RawLLM: store.NullString(res.Raw),
DownloadID: id,
MediaType: store.NullString(string(res.Plan.Type)),
Title: store.NullString(res.Plan.Title),
Provider: store.NullString(provider),
ProviderID: store.NullString(providerID),
Plan: store.NullString(string(planJSON)),
SourceFiles: sourceFiles,
RawLLM: store.NullString(res.Raw),
}
if res.Plan.OriginalTitle != "" {
rec.OriginalTitle = store.NullString(res.Plan.OriginalTitle)
@@ -968,12 +982,19 @@ type ReviewData struct {
// Задача в review без записанной причины (обычный «нет матча») иначе оставила
// бы экран без объяснения, почему пропала кнопка «Применить».
PreviewError string
Candidates []store.MetadataCandidate // кандидаты базы для ручного выбора
Sources []SourceOption // единый список источников совпадения (нейронка + кандидаты)
Provider string // эффективный провайдер (с учётом выбора)
ProviderID string // эффективный id в базе
Hints []string
Overrides map[string]string
// SourceFiles — снимок списка файлов раздачи момента распознавания, в
// порядке нумерации промпта (номер строки = номер адресации модели).
SourceFiles []recognize.File
// SourceFilesKnown — есть ли снимок вообще. false у записей старше
// миграции 0012: транспорт обязан сказать, что полнота не гарантирована,
// а не выдавать план за полный перечень файлов.
SourceFilesKnown bool
Candidates []store.MetadataCandidate // кандидаты базы для ручного выбора
Sources []SourceOption // единый список источников совпадения (нейронка + кандидаты)
Provider string // эффективный провайдер (с учётом выбора)
ProviderID string // эффективный id в базе
Hints []string
Overrides map[string]string
}
// MatchURL — ссылка на подтверждённую запись метабазы для этой загрузки. Общий
@@ -1068,6 +1089,14 @@ func (w *Worker) ReviewData(ctx context.Context, id string) (*ReviewData, error)
Download: *d, Recognition: rec, Hints: hints, Overrides: overrides,
Provider: prov, ProviderID: pid,
}
if rec != nil && rec.SourceFiles.Valid {
var files []recognize.File
if err := json.Unmarshal([]byte(rec.SourceFiles.String), &files); err != nil {
log.Warn("review data unmarshal source files failed", "error", err)
} else {
rd.SourceFiles, rd.SourceFilesKnown = files, true
}
}
if rec != nil {
if cands, cerr := w.store.ListCandidatesByRecognition(ctx, rec.ID); cerr == nil {
rd.Candidates = cands