Adds MlClient and the two model-backed strategies behind the existing
ScoringStrategy / ForecastStrategy seams, so switching engines stays a config
value rather than a code change.
MlClient never throws. Every call returns Optional.empty() on any failure —
timeout, connection refused, malformed body — and ModelScoringStrategy falls
back to the rule engine for that enterprise. A nightly run must complete, and
a demo must survive the Python process dying mid-presentation.
The important part is what the fallback records. A run configured MODEL used
to stamp every score MODEL even when the ML service was down and rules had
actually produced the number, which made the provenance column a lie exactly
when it mattered. ScoreOutcome now carries an `engine` field and the fallback
returns .attributedTo("RULE_FALLBACK"). Verified by killing the service
mid-run: run 285 MODEL, run 286 RULE_FALLBACK, 260 of 263 scores differ.
ModelMetric and its read-only controller expose the measured backtest at
GET /admin/model/metrics. syntheticLabelShare is stored on the same row as
every metric so accuracy cannot be quoted without the caveat travelling with
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
33 lines
1.0 KiB
Java
33 lines
1.0 KiB
Java
package com.lincfusion.udyamlinc.ml;
|
|
|
|
import lombok.Getter;
|
|
import lombok.Setter;
|
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
/**
|
|
* Where the inference service is, and how long we are willing to wait for it.
|
|
*
|
|
* <p>Timeouts are short on purpose. A scoring run over the whole book must finish
|
|
* inside its window, and an inference service that has become slow is
|
|
* operationally the same as one that is down — both mean "score this enterprise
|
|
* on rules and move on".
|
|
*/
|
|
@Component
|
|
@ConfigurationProperties(prefix = "udyam.ml")
|
|
@Getter
|
|
@Setter
|
|
public class MlProperties {
|
|
|
|
/** Off by default: a checkout with no Python running must still boot and score. */
|
|
private boolean enabled = false;
|
|
|
|
private String baseUrl = "http://127.0.0.1:8000";
|
|
|
|
/** Per-enterprise call. */
|
|
private int timeoutMs = 2000;
|
|
|
|
/** Batch call over the book — larger, because it is one call for 263 rows. */
|
|
private int batchTimeoutMs = 30000;
|
|
}
|