Vor dem Generieren waehlen: Anschreiben, Lebenslauf oder beides

Auf der Bewerbungsseite laesst sich jetzt auswaehlen, welche Unterlagen die KI
erzeugt. Standard bleibt beides; die letzte Auswahl wird gemerkt und ist beim
naechsten Generieren vorbelegt. Ohne Haken bricht das Formular ab statt still
beides zu erzeugen.

Nicht gewaehlte Dokumente werden gar nicht erst angefragt: JSON-Schema und
Skelett im Prompt werden auf die gewuenschten Teile reduziert, das spart einen
Gutteil der Generierungszeit. Gerendert wird ebenfalls nur das Gewaehlte - auch
wenn das Modell sich nicht an das Schema haelt und trotzdem alles liefert.

Fuer den E-Mail-Versand mitgedacht:
- Wird kein Lebenslauf erzeugt, fuehrt ihn das Anschreiben nicht mehr unter
  "Anlagen" auf. Sonst kuendigt der Brief eine Anlage an, die nie mitgeht.
- Der Prompt bekommt zusaetzlich die Liste der Dateien, die der Bewerbungsmail
  tatsaechlich anhaengen (inkl. Anschreiben selbst), damit der Begleittext keine
  Unterlagen nennt, die nicht dabei sind.
- Die Anhang-Checkboxen im Mailformular ziehen sich aus den erzeugten Anhaengen,
  greifen also automatisch.

Die REST-API kennt das Feld ebenfalls (POST .../generate, "dokumente"), inkl.
OpenAPI-Doku.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 02:18:02 +02:00
co-authored by Claude Opus 4.8
parent 66366659d2
commit d1116d522d
5 changed files with 223 additions and 51 deletions
+19 -2
View File
@@ -27,7 +27,10 @@ const multer = require('multer');
}
})();
const { generateApplicationDocuments, generateEmailReply, renderDesignVorschau } = require('./lib/documents');
const {
generateApplicationDocuments, generateEmailReply, renderDesignVorschau,
DOKUMENT_TYPEN, normalizeDokumente,
} = require('./lib/documents');
const chat = require('./lib/chat');
const promptStore = require('./lib/prompts');
const designStore = require('./lib/design');
@@ -617,6 +620,9 @@ async function runGeneration(bewerbungId, options = {}) {
const anlagenIds = Array.isArray(options.anlagenIds) ? options.anlagenIds.map(Number) : [];
const selectedAnhaenge = basisAnhaenge.filter((a) => anlagenIds.includes(a.id));
// Which documents to produce; unset means both (the default everywhere).
const dokumente = normalizeDokumente(options.dokumente);
const { documents, email } = await generateApplicationDocuments({
job: {
firma: bewerbung.firma,
@@ -629,6 +635,7 @@ async function runGeneration(bewerbungId, options = {}) {
settings,
prompts,
design,
dokumente,
// Names of the selected attachments so the cover letter (and the LLM) lists
// exactly these under "Anlagen".
zusatzAnlagen: selectedAnhaenge.map((a) => a.name || a.dateiname),
@@ -851,6 +858,9 @@ function initializeDatabase() {
`);
// Migration: labels (JSON array of work-location labels) on both Stellen tables.
db.run('ALTER TABLE bewerbungen ADD COLUMN labels TEXT', () => {});
// Which documents the last generation run produced ("anschreiben,lebenslauf").
// Remembered so the form comes back pre-ticked with the user's last choice.
db.run('ALTER TABLE bewerbungen ADD COLUMN generierung_dokumente TEXT', () => {});
db.run('ALTER TABLE jobangebote ADD COLUMN labels TEXT', () => {});
// Migration: add columns to pre-existing jobangebote tables.
db.run('ALTER TABLE jobangebote ADD COLUMN anzeige_datum DATE', () => {});
@@ -1463,6 +1473,10 @@ initializeDatabase().then(() => {
anhaenge,
interneAnhaenge,
emails,
// Last choice of documents (defaults to both), for pre-ticking the form.
dokumentAuswahl: normalizeDokumente(
application.generierung_dokumente ? String(application.generierung_dokumente).split(',') : null
),
mailConfigured: mailer.isConfigured(),
mailFrom: mailer.isConfigured() ? mailer.fromField() : '',
mailError: req.query.mailerror ? String(req.query.mailerror) : '',
@@ -2355,7 +2369,10 @@ initializeDatabase().then(() => {
// Selected extra attachments (checkbox values); none by default.
const anlagenIds = [].concat(req.body.anlage || []).map((v) => parseInt(v, 10)).filter((n) => !Number.isNaN(n));
runGeneration(id, { anlagenIds });
// Which documents to generate; nothing ticked = both (the default).
const dokumente = normalizeDokumente(req.body.dokument);
await dbRun('UPDATE bewerbungen SET generierung_dokumente = ? WHERE id = ?', [dokumente.join(','), id]);
runGeneration(id, { anlagenIds, dokumente });
res.redirect('/bewerbung/' + id);
} catch (error) {
console.error('Error generating documents:', error);