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
@@ -349,17 +349,19 @@ public class BootstrapRunner {
new DocumentProcessingCoordinator(documentRecordRepository, processingAttemptRepository,
unitOfWorkPort, targetFolderPort, targetFileCopyPort, coordinatorLogger,
startConfig.maxRetriesTransient(),
startConfig.maxTitleLength(),
activeFamily.getIdentifier());
PromptPort promptPort = new FilesystemPromptPortAdapter(startConfig.promptTemplateFile());
ClockPort clockPort = new SystemClockAdapter();
AiResponseValidator aiResponseValidator = new AiResponseValidator(clockPort);
AiResponseValidator aiResponseValidator = new AiResponseValidator(clockPort, startConfig.maxTitleLength());
AiNamingService aiNamingService = new AiNamingService(
aiInvocationPort,
promptPort,
aiResponseValidator,
providerConfig.model(),
startConfig.maxTextCharacters());
startConfig.maxTextCharacters(),
startConfig.maxTitleLength());
ProcessingLogger useCaseLogger = new Log4jProcessingLogger(
DefaultBatchRunProcessingUseCase.class, aiContentSensitivity);
@@ -51,6 +51,7 @@ import de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily;
* max.retries.transient=...
* max.pages=...
* max.text.characters=...
* max.title.length=...
* prompt.template.file=...
* # Logging
* log.ai.sensitive=...
@@ -200,6 +201,7 @@ public final class GuiConfigurationPropertiesWriter implements GuiConfigurationF
appendKeyValue(sb, "max.retries.transient", values.maxRetriesTransient());
appendKeyValue(sb, "max.pages", values.maxPages());
appendKeyValue(sb, "max.text.characters", values.maxTextCharacters());
appendKeyValue(sb, "max.title.length", values.maxTitleLength());
appendKeyValue(sb, "prompt.template.file", values.promptTemplateFile());
appendLine(sb, "");
@@ -426,7 +426,7 @@ class BootstrapRunnerConfigPathSemanticsTest {
tempDir.resolve("target"),
sqliteFile,
multiConfig,
3, 100, 50000,
3, 100, 50000, 60,
promptFile,
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
@@ -70,6 +70,7 @@ class BootstrapRunnerEdgeCasesTest {
3,
100,
50000,
60,
Files.createFile(tempDir.resolve("prompt.txt")),
null, // null runtimeLockFile
tempDir.resolve("logs"),
@@ -114,7 +115,7 @@ class BootstrapRunnerEdgeCasesTest {
Files.createDirectories(tempDir.resolve("target")),
sqliteFile,
validMultiProviderConfig(),
3, 100, 50000,
3, 100, 50000, 60,
Files.createFile(tempDir.resolve("prompt.txt")),
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
@@ -139,7 +140,7 @@ class BootstrapRunnerEdgeCasesTest {
Files.createDirectories(tempDir.resolve("target")),
sqliteFile,
validMultiProviderConfig(),
3, 100, 50000,
3, 100, 50000, 60,
Files.createFile(tempDir.resolve("prompt.txt")),
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
@@ -164,7 +165,7 @@ class BootstrapRunnerEdgeCasesTest {
Files.createDirectories(tempDir.resolve("target")),
Files.createFile(tempDir.resolve("db.sqlite")),
validMultiProviderConfig(),
3, 100, 50000,
3, 100, 50000, 60,
Files.createFile(tempDir.resolve("prompt.txt")),
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
@@ -231,7 +232,7 @@ class BootstrapRunnerEdgeCasesTest {
Path dbFile = Files.createFile(tempDir.resolve("db.sqlite"));
Path promptFile = Files.createFile(tempDir.resolve("prompt.txt"));
return new StartConfiguration(sourceDir, targetDir, dbFile,
validMultiProviderConfig(), 3, 100, 50000,
validMultiProviderConfig(), 3, 100, 50000, 60,
promptFile, tempDir.resolve("lock.lock"), tempDir.resolve("logs"),
"INFO", false);
} catch (Exception e) {
@@ -538,6 +539,7 @@ class BootstrapRunnerEdgeCasesTest {
3,
100,
50000,
60,
promptTemplateFile,
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
@@ -223,7 +223,7 @@ class BootstrapRunnerStartupDispatchTest {
tempDir.resolve("target"),
sqliteFile,
multiConfig,
3, 100, 50000,
3, 100, 50000, 60,
promptFile,
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
@@ -197,6 +197,7 @@ class BootstrapRunnerTest {
3,
100,
50000,
60,
promptFile,
Paths.get(""), // empty simulates unconfigured runtime.lock.file
tempDir.resolve("logs"),
@@ -280,6 +281,7 @@ class BootstrapRunnerTest {
0, // max.retries.transient = 0 is invalid (must be >= 1)
100,
50000,
60,
promptFile,
tempDir.resolve("lock-mrt.lock"),
null,
@@ -516,6 +518,7 @@ class BootstrapRunnerTest {
3,
100,
50000,
60,
promptTemplateFile,
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
@@ -160,6 +160,7 @@ class BootstrapSmokeTest {
3,
10,
5000,
60,
promptFile,
tempDir.resolve("run.lock"),
tempDir.resolve("logs"),
@@ -128,6 +128,7 @@ class GuiConfigurationPropertiesWriterTest {
assertEquals("3", props.getProperty("max.retries.transient"));
assertEquals("10", props.getProperty("max.pages"));
assertEquals("5000", props.getProperty("max.text.characters"));
assertEquals("60", props.getProperty("max.title.length"));
assertEquals("./prompt.txt", props.getProperty("prompt.template.file"));
assertEquals("false", props.getProperty("log.ai.sensitive"));
assertEquals("./logs", props.getProperty("log.directory"));
@@ -196,6 +197,20 @@ class GuiConfigurationPropertiesWriterTest {
assertTrue(logAiPos < lockPos, "Logging section must appear before runtime section");
}
@Test
void buildPropertiesContent_maxTitleLengthAppearsAfterMaxTextCharacters() {
GuiConfigurationValues values = buildTestValues("claude", "", "");
String content = writer.buildPropertiesContent(values);
int maxCharsPos = content.indexOf("max.text.characters=");
int maxTitleLengthPos = content.indexOf("max.title.length=");
assertTrue(maxCharsPos >= 0, "max.text.characters must be present");
assertTrue(maxTitleLengthPos >= 0, "max.title.length must be present");
assertTrue(maxCharsPos < maxTitleLengthPos,
"max.title.length must appear after max.text.characters");
}
@Test
void buildPropertiesContent_containsGroupingComments() {
GuiConfigurationValues values = buildTestValues("claude", "", "");
@@ -295,6 +310,7 @@ class GuiConfigurationPropertiesWriterTest {
"3",
"10",
"5000",
"60",
"false",
activeProvider,
providerConfigurations);
@@ -113,6 +113,9 @@ public final class E2ETestContext implements AutoCloseable {
*/
static final int MAX_RETRIES_TRANSIENT = 3;
/** Default maximum base title length used in the E2E test context. */
static final int MAX_TITLE_LENGTH = 60;
/** Model name carried in attempt history (no real inference occurs). */
static final String AI_MODEL = "e2e-stub-model";
@@ -400,13 +403,14 @@ public final class E2ETestContext implements AutoCloseable {
targetFileCopyPort,
coordinatorLogger,
MAX_RETRIES_TRANSIENT,
MAX_TITLE_LENGTH,
providerIdentifier);
PromptPort promptPort = new FilesystemPromptPortAdapter(promptFile);
ClockPort clockPort = new SystemClockAdapter();
AiResponseValidator aiResponseValidator = new AiResponseValidator(clockPort);
AiResponseValidator aiResponseValidator = new AiResponseValidator(clockPort, MAX_TITLE_LENGTH);
AiNamingService aiNamingService = new AiNamingService(
aiStub, promptPort, aiResponseValidator, AI_MODEL, MAX_TEXT_CHARS);
aiStub, promptPort, aiResponseValidator, AI_MODEL, MAX_TEXT_CHARS, MAX_TITLE_LENGTH);
ProcessingLogger useCaseLogger = new Log4jProcessingLogger(
DefaultBatchRunProcessingUseCase.class);