Titellänge nun parametrisierbar

This commit is contained in:
2026-04-22 09:53:03 +02:00
parent 088fd85572
commit 8286d0f0e5
74 changed files with 1450 additions and 236 deletions
@@ -168,6 +168,7 @@ public class StartConfigurationValidator {
validateMaxRetriesTransient(config.maxRetriesTransient(), errors);
validateMaxPages(config.maxPages(), errors);
validateMaxTextCharacters(config.maxTextCharacters(), errors);
validateMaxTitleLength(config.maxTitleLength(), errors);
}
private void validateOptionalPaths(StartConfiguration config, List<String> errors) {
@@ -219,6 +220,45 @@ public class StartConfigurationValidator {
}
}
/**
* Validates the configured maximum base title length.
* <p>
* Hard errors (abort startup):
* <ul>
* <li>{@code value < 10}</li>
* <li>{@code value > 120}</li>
* </ul>
* Non-blocking warnings (logged but accepted):
* <ul>
* <li>{@code 10 <= value <= 19}: low-range warning (below the usual minimum)</li>
* <li>{@code 100 <= value <= 120}: high-range warning (filename compatibility with
* encrypted Synology volumes)</li>
* </ul>
*
* @param value the configured value
* @param errors collector for aggregated error messages
*/
private void validateMaxTitleLength(int value, List<String> errors) {
if (value < 10) {
errors.add("- max.title.length: must be >= 10 (got: " + value
+ "). Minimum ist 10 Zeichen.");
return;
}
if (value > 120) {
errors.add("- max.title.length: must be <= 120 (got: " + value
+ "). Überschreitet sicheres Limit für verschlüsselte Synology-Volumes.");
return;
}
if (value <= 19) {
LOG.warn("Titellänge {} unter 20 Zeichen ist für die meisten Dokumente nicht empfohlen",
value);
} else if (value >= 100) {
LOG.warn("Titellänge {} ist hoch Kompatibilität mit verschlüsselten Volumes "
+ "(Limit ~143 Zeichen inkl. Datumspräfix) prüfen",
value);
}
}
private void validatePromptTemplateFile(Path promptTemplateFile, List<String> errors) {
validateRequiredRegularFile(promptTemplateFile, "prompt.template.file", errors);
}
@@ -123,6 +123,7 @@ public class PropertiesConfigurationPortAdapter implements ConfigurationPort {
parseInt(getRequiredProperty(props, "max.retries.transient")),
parseInt(getRequiredProperty(props, "max.pages")),
parseInt(getRequiredProperty(props, "max.text.characters")),
parseMaxTitleLength(props),
Paths.get(getRequiredProperty(props, "prompt.template.file")),
Paths.get(getOptionalProperty(props, "runtime.lock.file", "")),
Paths.get(getOptionalProperty(props, "log.directory", "")),
@@ -169,6 +170,43 @@ public class PropertiesConfigurationPortAdapter implements ConfigurationPort {
}
}
/**
* Parses the {@code max.title.length} property.
* <p>
* This property controls the maximum length of the base title portion of the generated
* target filename. When the property is absent or blank in the properties file, the
* default value {@value #DEFAULT_MAX_TITLE_LENGTH} is returned for backward compatibility
* with configurations that pre-date this setting.
* <p>
* When the property is present but cannot be parsed as an integer, a
* {@link ConfigurationLoadingException} is thrown. The numeric range of the parsed value
* is enforced downstream in the start-configuration validator.
*
* @param props the raw loaded properties; must not be {@code null}
* @return the parsed maximum base title length
* @throws ConfigurationLoadingException if the property is present but cannot be parsed
*/
private int parseMaxTitleLength(Properties props) {
String raw = props.getProperty("max.title.length");
if (raw == null || raw.isBlank()) {
return DEFAULT_MAX_TITLE_LENGTH;
}
try {
return Integer.parseInt(raw.trim());
} catch (NumberFormatException e) {
throw new ConfigurationLoadingException(
"Invalid integer value for property max.title.length: '" + raw + "'", e);
}
}
/**
* Default value for {@code max.title.length} when the property is missing or blank.
* <p>
* Chosen to preserve the previously hardcoded limit, so existing configurations without
* the property continue to work unchanged.
*/
private static final int DEFAULT_MAX_TITLE_LENGTH = 60;
/**
* Parses the {@code log.ai.sensitive} configuration property with strict validation.
* <p>
@@ -102,8 +102,10 @@ public class FilesystemResourceCreationAdapter implements ResourceCreationPort {
* stilles Überschreiben). Der Inhalt wird als UTF-8-Text geschrieben.
* Die Aktion wird mit Zielpfad geloggt.
* <p>
* Der Inhalt der erzeugten Datei wird von {@link DefaultPromptTemplate#defaultContent()} geliefert.
* Es handelt sich um einen deutschen Standardprompt, der ohne weitere Anpassung funktioniert.
* Der Inhalt der erzeugten Datei wird von {@link DefaultPromptTemplate#defaultContent(int)}
* geliefert. Als Parameter wird die im {@code CreatePromptFile}-Vorschlag enthaltene
* konfigurierte maximale Titellänge verwendet. Es handelt sich um einen deutschen
* Standardprompt, der ohne weitere Anpassung funktioniert.
*
* @param suggestion der {@link CorrectionSuggestion.CreatePromptFile}-Vorschlag; darf nicht {@code null} sein
* @return Ergebnis der Ausführung; nie {@code null}
@@ -131,7 +133,8 @@ public class FilesystemResourceCreationAdapter implements ResourceCreationPort {
LOG.info("Prompt-Datei: Elternordner angelegt: {}", parent);
}
Files.writeString(path, DefaultPromptTemplate.defaultContent(), StandardCharsets.UTF_8,
Files.writeString(path, DefaultPromptTemplate.defaultContent(suggestion.maxTitleLength()),
StandardCharsets.UTF_8,
StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
LOG.info("Prompt-Datei erfolgreich erzeugt: {}", path.toAbsolutePath());
return new CorrectionOutcome.Applied(suggestion,