feat(risk): bind the scoring engine to the ML service, with honest attribution
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>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user