runtime-split-patch-a
This commit is contained in:
parent
d206529162
commit
74609e481d
|
|
@ -0,0 +1,33 @@
|
|||
# Runtime split – Patch A
|
||||
|
||||
This patch introduces the runtime split scaffolding without performing the full behavioral cutover.
|
||||
|
||||
## Added
|
||||
- `dip.runtime.mode` with values `LEGACY` / `NEW`
|
||||
- `@ConditionalOnRuntimeMode`
|
||||
- `DipSearchProperties` (`dip.search.*`)
|
||||
- `LegacyTedProperties` (`legacy.ted.*`) as migration scaffold
|
||||
- example `application-legacy.yml` and `application-new.yml`
|
||||
|
||||
## First gated beans
|
||||
|
||||
### LEGACY
|
||||
- `GenericVectorizationRoute`
|
||||
- `DocumentEmbeddingProcessingService`
|
||||
- `ConfiguredEmbeddingModelStartupRunner`
|
||||
- `GenericVectorizationStartupRunner`
|
||||
|
||||
### NEW
|
||||
- `EmbeddingSubsystemStartupValidator`
|
||||
- `PgVectorSemanticSearchEngine`
|
||||
- `GenericSearchController`
|
||||
|
||||
## Intentional limitation of Patch A
|
||||
This patch does **not** yet switch import/runtime orchestration to the new embedding job flow.
|
||||
It only establishes the explicit runtime mode infrastructure and starts separating bean graphs.
|
||||
|
||||
## Follow-up patch (Patch B)
|
||||
- add a NEW-mode embedding job scheduler
|
||||
- make generic import enqueue new embedding jobs
|
||||
- disable legacy vector route in NEW mode operationally
|
||||
- move active search tuning from `ted.search.*` to `dip.search.*`
|
||||
|
|
@ -10,11 +10,14 @@ import org.springframework.boot.ApplicationArguments;
|
|||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(prefix = "dip.embedding", name = "enabled", havingValue = "true")
|
||||
@Slf4j
|
||||
@ConditionalOnRuntimeMode(RuntimeMode.NEW)
|
||||
public class EmbeddingSubsystemStartupValidator implements ApplicationRunner {
|
||||
|
||||
private final EmbeddingProperties properties;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package at.procon.dip.runtime.condition;
|
||||
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(RuntimeModeCondition.class)
|
||||
public @interface ConditionalOnRuntimeMode {
|
||||
RuntimeMode value();
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package at.procon.dip.runtime.condition;
|
||||
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
import java.util.Map;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
public class RuntimeModeCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnRuntimeMode.class.getName());
|
||||
if (attributes == null || !attributes.containsKey("value")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RuntimeMode expected = (RuntimeMode) attributes.get("value");
|
||||
String configured = context.getEnvironment().getProperty("dip.runtime.mode", RuntimeMode.LEGACY.name());
|
||||
RuntimeMode actual;
|
||||
try {
|
||||
actual = RuntimeMode.valueOf(configured.trim().toUpperCase());
|
||||
} catch (Exception ex) {
|
||||
actual = RuntimeMode.LEGACY;
|
||||
}
|
||||
return actual == expected;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package at.procon.dip.runtime.config;
|
||||
|
||||
public enum RuntimeMode {
|
||||
LEGACY,
|
||||
NEW
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package at.procon.dip.runtime.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "dip.runtime")
|
||||
@Data
|
||||
public class RuntimeModeProperties {
|
||||
/**
|
||||
* Patch A defaults to LEGACY so existing runtime behaviour is preserved until
|
||||
* NEW mode is explicitly enabled.
|
||||
*/
|
||||
private RuntimeMode mode = RuntimeMode.LEGACY;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package at.procon.dip.search.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "dip.search")
|
||||
@Data
|
||||
public class DipSearchProperties {
|
||||
|
||||
private Lexical lexical = new Lexical();
|
||||
private Semantic semantic = new Semantic();
|
||||
private Fusion fusion = new Fusion();
|
||||
private Chunking chunking = new Chunking();
|
||||
|
||||
@Data
|
||||
public static class Lexical {
|
||||
private double trigramSimilarityThreshold = 0.12;
|
||||
private int fulltextCandidateLimit = 120;
|
||||
private int trigramCandidateLimit = 120;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Semantic {
|
||||
private double similarityThreshold = 0.7;
|
||||
private int semanticCandidateLimit = 120;
|
||||
private String defaultModelKey;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Fusion {
|
||||
private double fulltextWeight = 0.35;
|
||||
private double trigramWeight = 0.20;
|
||||
private double semanticWeight = 0.45;
|
||||
private double recencyBoostWeight = 0.05;
|
||||
private int recencyHalfLifeDays = 30;
|
||||
private int debugTopHitsPerEngine = 10;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Chunking {
|
||||
private boolean enabled = true;
|
||||
private int targetChars = 1800;
|
||||
private int overlapChars = 200;
|
||||
private int maxChunksPerDocument = 12;
|
||||
private int startupLexicalBackfillLimit = 500;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,10 +14,13 @@ import java.util.List;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnRuntimeMode(RuntimeMode.NEW)
|
||||
public class PgVectorSemanticSearchEngine implements SearchEngine {
|
||||
|
||||
private final EmbeddingProperties embeddingProperties;
|
||||
|
|
|
|||
|
|
@ -14,10 +14,13 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/search")
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnRuntimeMode(RuntimeMode.NEW)
|
||||
public class GenericSearchController {
|
||||
|
||||
private final SearchOrchestrator searchOrchestrator;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import org.springframework.data.domain.PageRequest;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import at.procon.ted.config.TedProcessorProperties;
|
||||
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -24,6 +26,7 @@ import java.util.UUID;
|
|||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||
public class GenericVectorizationRoute extends RouteBuilder {
|
||||
|
||||
private static final String ROUTE_ID_TRIGGER = "generic-vectorization-trigger";
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import at.procon.ted.config.TedProcessorProperties;
|
|||
import at.procon.ted.model.entity.VectorizationStatus;
|
||||
import at.procon.ted.repository.ProcurementDocumentRepository;
|
||||
import at.procon.ted.service.VectorizationService;
|
||||
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -26,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||
public class DocumentEmbeddingProcessingService {
|
||||
|
||||
private final DocumentEmbeddingRepository embeddingRepository;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package at.procon.dip.vectorization.startup;
|
|||
import at.procon.dip.domain.document.service.DocumentEmbeddingService;
|
||||
import at.procon.dip.domain.document.service.command.RegisterEmbeddingModelCommand;
|
||||
import at.procon.ted.config.TedProcessorProperties;
|
||||
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
|
|
@ -15,6 +17,7 @@ import org.springframework.stereotype.Component;
|
|||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||
public class ConfiguredEmbeddingModelStartupRunner implements ApplicationRunner {
|
||||
|
||||
private final TedProcessorProperties properties;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package at.procon.dip.vectorization.startup;
|
|||
import at.procon.dip.domain.document.EmbeddingStatus;
|
||||
import at.procon.dip.domain.document.repository.DocumentEmbeddingRepository;
|
||||
import at.procon.ted.config.TedProcessorProperties;
|
||||
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
|
||||
import at.procon.dip.runtime.config.RuntimeMode;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -19,6 +21,7 @@ import org.springframework.stereotype.Component;
|
|||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
|
||||
public class GenericVectorizationStartupRunner implements ApplicationRunner {
|
||||
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package at.procon.ted.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Patch A scaffold for the legacy runtime configuration tree.
|
||||
*
|
||||
* The legacy runtime still uses {@link TedProcessorProperties} today. This class is
|
||||
* introduced so the old configuration can be moved gradually from `ted.*` to
|
||||
* `legacy.ted.*` without blocking the runtime split.
|
||||
*/
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "legacy.ted")
|
||||
public class LegacyTedProperties extends TedProcessorProperties {
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
dip:
|
||||
runtime:
|
||||
mode: LEGACY
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
dip:
|
||||
runtime:
|
||||
mode: NEW
|
||||
Loading…
Reference in New Issue