fix: flag inconsistent current image pairs
This commit is contained in:
parent
cb653d15d0
commit
0b51a76c21
4
TODO.md
4
TODO.md
@ -50,7 +50,9 @@ the first block before investing in architecture or polish.
|
||||
split before mixed German guillemet closes (`»…‹` / `«…›`), with an epigraph regression test.
|
||||
- [ ] **M — Make profile portraits and character sheets use one canonical identity.**
|
||||
Shared structured prompts now version consistent pairs as v3 and fingerprint canonical appearance fields; add
|
||||
cross-record validation so mismatched profile/reference files are detected even when both are current-version.
|
||||
cross-record validation so mismatched profile/reference files are detected even when both are current-version. The
|
||||
UI now flags missing, stale, changed, or explicitly inconsistent pair metadata; visual similarity itself still needs
|
||||
an image-level review workflow.
|
||||
- [x] **M — Add bounded duplicate-generation protection.** Consistent portrait/reference, voice-design, and casting
|
||||
jobs now deduplicate repeated requests with bounded, expiring `Idempotency-Key` contracts.
|
||||
- [ ] **M — Improve large-table performance.** Character productions and the flat voice library now paginate at 50
|
||||
|
||||
2
static/dist/main.min.js
vendored
2
static/dist/main.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1238,6 +1238,12 @@ async function _charDetailPage(rec, allChars, opts) {
|
||||
// in the Generation Prompts section further down, or squeezed into the
|
||||
// header at thumbnail size (confirmed live: 110px was too small to read
|
||||
// any actual detail in a multi-pose design sheet).
|
||||
const visualPairNeedsReview = !!(rec.image && sh.concept_art_image && (
|
||||
sh.consistent_prompt_version !== 'v3'
|
||||
|| !sh.consistent_identity_fingerprint
|
||||
|| sh.consistent_identity_fingerprint !== identityFingerprintShort
|
||||
|| sh.identity_consistent === false
|
||||
));
|
||||
const conceptArtHtml = '<div class="lcd-section-full lcd-conceptart">'
|
||||
+ '<div class="lcd-conceptart-headrow">'
|
||||
+ '<div class="lcd-section-label"><span class="mdi mdi-image-frame"></span> Konzeptbild</div>'
|
||||
@ -1252,7 +1258,7 @@ async function _charDetailPage(rec, allChars, opts) {
|
||||
+ '<span class="mdi mdi-creation"></span> ' + (sh.concept_art_image ? 'Neu generieren' : 'Generieren')
|
||||
+ '</button>'
|
||||
+ '</div>'
|
||||
+ (rec.image && sh.concept_art_image && (sh.consistent_prompt_version !== 'v3' || (sh.consistent_identity_fingerprint && sh.consistent_identity_fingerprint !== identityFingerprintShort))
|
||||
+ (visualPairNeedsReview
|
||||
? '<div class="note" style="color:var(--warning,#d97706);margin-top:6px"><span class="mdi mdi-alert-outline"></span> Dieses Bildpaar stammt aus einer älteren Prompt-Version und sollte für konsistente Identität neu erzeugt werden.</div>'
|
||||
: '')
|
||||
+ (sh.concept_art_image
|
||||
|
||||
@ -89,3 +89,27 @@ test('consistent image generation can be cancelled from its progress panel', asy
|
||||
await expect.poll(() => cancelRequests).toBe(1);
|
||||
await expect(page.locator('#cij-progress-panel')).toHaveClass(/cij-progress-err/, { timeout: 5_000 });
|
||||
});
|
||||
|
||||
test('current-version image pairs marked inconsistent show a review warning', async ({ page }) => {
|
||||
await page.route('**/api/characters*', route => route.fulfill({ json: {
|
||||
characters: [{
|
||||
id: 'c-mismatch', book: 'Smoke Book', name: 'Mismatch', image: '/profile.png',
|
||||
sheet: {
|
||||
image_prompt: 'portrait', concept_art_prompt: 'reference', concept_art_image: '/reference.png',
|
||||
consistent_prompt_version: 'v3', consistent_identity_fingerprint: 'same-current-version',
|
||||
identity_consistent: false,
|
||||
},
|
||||
}],
|
||||
} }));
|
||||
await page.route('**/api/voices*', route => route.fulfill({ json: [] }));
|
||||
await page.route('**/api/settings*', route => route.fulfill({ json: {} }));
|
||||
await page.route('**/api/book-profile*', route => route.fulfill({ json: { profile: {} } }));
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForFunction(() => typeof window.libraryRenderCharacters === 'function', null, { timeout: 15_000 });
|
||||
await page.waitForSelector('#lib-chars-list', { state: 'attached', timeout: 15_000 });
|
||||
await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 });
|
||||
await page.locator('#nav-library-head').evaluate(el => el.click());
|
||||
await page.locator('.lib-tab[data-library-view="characters"]').evaluate(el => el.click());
|
||||
await page.locator('.lib-char-card[data-char-id="c-mismatch"]').click();
|
||||
await expect(page.locator('.lcd-conceptart .note')).toContainText('konsistente Identität', { timeout: 15_000 });
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user