feat: Fachwortliste für Whisper aus einer Quelle (T7 L2 Task 12)

npm run export:voice-vocabulary schreibt ApiCore-Whisper/vocabulary.txt
aus whisperInitialPrompt(); ein Wächter vergleicht Datei und Quelle im
Monorepo (im Backend-Image-Build ohne Nachbarordner übersprungen).
Datei mit LF über .gitattributes; scripts/ aus dem Nest-Build
ausgeschlossen.

Refs #27

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-16 09:44:58 +02:00
1 parent 9795f98006
commit 61985afc4b
6 files changed
+49 -2

No files matched your search

+4
View File
@@ -1,3 +1,7 @@
# Shell scripts must keep LF line endings, also on Windows checkouts with
# core.autocrlf, or bash fails on the carriage returns.
*.sh text eol=lf
# The Whisper word list is generated (npm run export:voice-vocabulary) and
# copied into the image; keep LF so the file stays byte-identical everywhere.
ApiCore-Whisper/vocabulary.txt text eol=lf
+2 -1
View File
@@ -22,7 +22,8 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"migrate:deploy": "prisma migrate deploy",
"seed": "node dist/seed.js"
"seed": "node dist/seed.js",
"export:voice-vocabulary": "ts-node scripts/export-voice-vocabulary.ts"
},
"dependencies": {
"@nestjs/common": "^11.0.1",
@@ -0,0 +1,15 @@
/**
* Writes the Whisper initial prompt to ApiCore-Whisper/vocabulary.txt, so the
* Python image uses the same word list as the backend (one source:
* src/voice/core/voice-vocabulary.ts). Run `npm run export:voice-vocabulary`
* after changing the list; voice-vocabulary-export.spec.ts fails until you do.
*/
import { mkdirSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { whisperInitialPrompt } from '../src/voice/core/voice-vocabulary';
const target = join(__dirname, '..', '..', 'ApiCore-Whisper', 'vocabulary.txt');
mkdirSync(dirname(target), { recursive: true });
writeFileSync(target, `${whisperInitialPrompt()}\n`, 'utf8');
console.log(`Wrote ${target}`);
@@ -0,0 +1,26 @@
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { whisperInitialPrompt } from './voice-vocabulary';
const MONOREPO_ROOT = join(__dirname, '..', '..', '..', '..');
const VOCABULARY_FILE = join(
MONOREPO_ROOT,
'ApiCore-Whisper',
'vocabulary.txt',
);
// The backend image build only sees ApiCore-Backend, so the comparison runs in
// the monorepo checkout and is skipped inside that build.
const inMonorepo = existsSync(join(MONOREPO_ROOT, 'ApiCore-Frontend'));
const describeInMonorepo = inMonorepo ? describe : describe.skip;
describeInMonorepo('whisper vocabulary export', () => {
it('the whisper vocabulary file equals whisperInitialPrompt()', () => {
// Windows checkouts may turn LF into CRLF; the content is what counts.
const onDisk = readFileSync(VOCABULARY_FILE, 'utf8')
.replace(/\r\n/g, '\n')
.replace(/\n$/, '');
expect(onDisk).toBe(whisperInitialPrompt());
});
});
+1 -1
View File
@@ -6,5 +6,5 @@
// outside it would make the next build skip "unchanged" files.
"incremental": false
},
"exclude": ["node_modules", "test", "dist", "**/*spec.ts", "**/*.spec-helper.ts", "prisma.config.ts", "prisma/seed.ts"]
"exclude": ["node_modules", "test", "scripts", "dist", "**/*spec.ts", "**/*.spec-helper.ts", "prisma.config.ts", "prisma/seed.ts"]
}
+1
View File
@@ -0,0 +1 @@
Durchsicht am Bienenvolk: Königin, Stifte, Weiselzellen, Brutwaben, Futterkranz, Honigraum, Varroa, Schwarmstimmung, sanftmütig, Wabensitz, Drohnenbrut, Brutnest, Brutfläche, Spielnäpfchen, Schwarmzellen, Absperrgitter, Futterteig, Ableger, Jungvolk, Rähmchen, Mittelwand, Flugloch, Beute, Zarge, Ameisensäure, Oxalsäure, Windel, Faulbrut, Kalkbrut, Nosema, Wachsmotten, Pollen.