feat(translation): re-translate on model change — artist-scoped + global re-run (#146)
retranslate_posts resets the 5 translation columns to NULL for a scoped set of posts (all, or WHERE artist_id IN ids) then reuses the untranslated sweep to re-run them, chasing the tail until drained (run-until-done). Interpreter cache keys on engine_version so a changed model re-translates, an unchanged one is cache-fast. Reset only happens when the service is configured+healthy so translations are never wiped when they can't be rebuilt. New POST /settings/translation/retranslate (artist_id | all=true). UI: per-artist 'Re-translate posts' on the Artist Management tab + 'Re-translate all' in the Settings Translation card, both with confirm dialogs. No migration (reuses m143 columns). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -101,6 +101,47 @@
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<section class="fc-artist-mgmt__sec">
|
||||
<h2 class="fc-h2">Translation</h2>
|
||||
<p class="fc-muted text-body-2 mb-3">
|
||||
Re-translate every post by {{ overview.name }} through the current
|
||||
Interpreter model — clears the stored translations and rebuilds them.
|
||||
Use this after switching translation models to refresh just this artist.
|
||||
</p>
|
||||
<div class="d-flex align-center flex-wrap" style="gap: 10px;">
|
||||
<v-btn
|
||||
size="small" variant="tonal" rounded="pill"
|
||||
prepend-icon="mdi-translate"
|
||||
:loading="retranslating" :disabled="!translationEnabled"
|
||||
@click="confirmRetranslate = true"
|
||||
>Re-translate posts</v-btn>
|
||||
<span v-if="!translationEnabled" class="fc-muted text-caption">
|
||||
Translation is off — enable it in Settings → Maintenance.
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- m146: per-artist re-translate — clears + rebuilds this artist's stored
|
||||
translations (used after a translation-model change). -->
|
||||
<v-dialog v-model="confirmRetranslate" max-width="440">
|
||||
<v-card>
|
||||
<v-card-title class="fc-h2">Re-translate {{ overview.name }}?</v-card-title>
|
||||
<v-card-text class="text-body-2">
|
||||
Clears the stored translation for every post by
|
||||
<strong>{{ overview.name }}</strong> and re-runs them through your
|
||||
current Interpreter model. Unchanged text comes back from the cache
|
||||
instantly. Runs in the background until done.
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="text" @click="confirmRetranslate = false">Cancel</v-btn>
|
||||
<v-btn color="accent" :loading="retranslating" @click="onRetranslate">
|
||||
Re-translate
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<section class="fc-artist-mgmt__sec">
|
||||
<h2 class="fc-h2">Danger zone</h2>
|
||||
<ArtistDangerZone
|
||||
@@ -113,9 +154,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter, RouterLink } from 'vue-router'
|
||||
|
||||
import { useApi } from '../../composables/useApi.js'
|
||||
import { useImportStore } from '../../stores/import.js'
|
||||
import { useSourcesStore } from '../../stores/sources.js'
|
||||
import { toast } from '../../utils/toast.js'
|
||||
import ArtistDangerZone from './ArtistDangerZone.vue'
|
||||
@@ -125,8 +168,38 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const api = useApi()
|
||||
const importStore = useImportStore()
|
||||
const sources = useSourcesStore()
|
||||
|
||||
// m146: per-artist re-translate. Gated on translation being enabled globally
|
||||
// (the endpoint 400s otherwise) — read the shared import settings once.
|
||||
const translationEnabled = ref(false)
|
||||
const retranslating = ref(false)
|
||||
const confirmRetranslate = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await importStore.loadSettings()
|
||||
translationEnabled.value = !!importStore.settings?.translation_enabled
|
||||
} catch { /* non-fatal — the button just stays disabled */ }
|
||||
})
|
||||
|
||||
async function onRetranslate () {
|
||||
retranslating.value = true
|
||||
try {
|
||||
await api.post('/api/settings/translation/retranslate', {
|
||||
body: { artist_id: props.overview.id },
|
||||
})
|
||||
confirmRetranslate.value = false
|
||||
toast({ text: `Re-translating ${props.overview.name}…`, type: 'success' })
|
||||
} catch (e) {
|
||||
toast({ text: `Re-translate failed: ${e.message}`, type: 'error' })
|
||||
} finally {
|
||||
retranslating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// #130: move a source into another artist.
|
||||
const moveOpen = ref(false)
|
||||
const moveSource = ref(null)
|
||||
|
||||
Reference in New Issue
Block a user