feat: recognition job context, diary linkage, worker improvements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-19 22:22:44 +02:00
parent cf69a4a3d9
commit 9357c194eb
5 changed files with 130 additions and 5 deletions

View File

@@ -89,17 +89,28 @@ func (pool *WorkerPool) processJob(workerContext context.Context, jobID string)
go func(candidateIndex int) {
defer wg.Done()
candidate := result.Candidates[candidateIndex]
dishID, created, findError := pool.dishRepo.FindOrCreate(workerContext, candidate.DishName)
englishName := candidate.DishName
dishID, created, findError := pool.dishRepo.FindOrCreate(workerContext, englishName)
if findError != nil {
slog.Warn("worker: find or create dish", "name", candidate.DishName, "err", findError)
slog.Warn("worker: find or create dish", "name", englishName, "err", findError)
return
}
localizedName := englishName
if job.Lang != "en" {
// Translate synchronously so the saved result JSON already has the right name.
// resolveLocalizedDishName saves all language translations as a side-effect,
// so enrichDishInBackground is not needed afterwards.
localizedName = pool.resolveLocalizedDishName(workerContext, dishID, englishName, job.Lang, created)
} else if created {
go enrichDishInBackground(pool.recognizer, pool.dishRepo, dishID, englishName)
}
mu.Lock()
result.Candidates[candidateIndex].DishID = &dishID
result.Candidates[candidateIndex].DishName = localizedName
mu.Unlock()
if created {
go enrichDishInBackground(pool.recognizer, pool.dishRepo, dishID, candidate.DishName)
}
recipeID, _, recipeError := pool.dishRepo.FindOrCreateRecipe(
workerContext, dishID,
@@ -140,3 +151,40 @@ func enrichDishInBackground(recognizer Recognizer, dishRepo DishRepository, dish
}
}
}
// resolveLocalizedDishName returns the dish name in the requested language.
// For a newly created dish it always translates via AI (synchronously) and saves
// all translations to dish_translations as a side-effect.
// For an existing dish it checks dish_translations first; if the row is missing
// (e.g. background enrichment is still in flight) it falls back to AI translation.
// Returns englishName unchanged on any unrecoverable error.
func (pool *WorkerPool) resolveLocalizedDishName(
workerContext context.Context,
dishID, englishName, lang string,
isNewDish bool,
) string {
if !isNewDish {
translatedName, found, getError := pool.dishRepo.GetTranslation(workerContext, dishID, lang)
if getError != nil {
slog.Warn("worker: get dish translation", "dish_id", dishID, "lang", lang, "err", getError)
} else if found {
return translatedName
}
// Fall through to AI translation.
}
translations, translateError := pool.recognizer.TranslateDishName(workerContext, englishName)
if translateError != nil {
slog.Warn("worker: translate dish name", "name", englishName, "err", translateError)
return englishName
}
for languageCode, nameInLang := range translations {
if upsertError := pool.dishRepo.UpsertTranslation(workerContext, dishID, languageCode, nameInLang); upsertError != nil {
slog.Warn("worker: upsert dish translation", "dish_id", dishID, "lang", languageCode, "err", upsertError)
}
}
if localizedName, ok := translations[lang]; ok {
return localizedName
}
return englishName
}