Repository navigation
Decisions move further into Laravel: a Decides validation rule checks input against a decision, and the AsDecision cast stores a decision object in a JSON column. Decision classes take float parameters, decideWithResult() keeps the raw probabilities next to the decision, and laya:eval measures a decision class against labelled examples.
// Validation
$request->validate([
'body' => ['required', 'string', Decides::that(Moderation::class, fn (Moderation $m) => ! $m->abusive && ! $m->spam)],
'comment' => [Decides::not('Does the text contain insults, hate speech or threats?')],
]);
// Eloquent
protected function casts(): array
{
return ['triage' => AsDecision::of(Triage::class)];
}
// The decision and the Result it came from
$decision = $laya->decideWithResult($ticket, Triage::class);
$decision->value; // Triage
$decision->result->choice('department')->probabilities;New
Decidesvalidation rule.Decides::that(Class::class, fn ($decision) => ...),Decides::yes('question')andDecides::not('question'), with->message()for your own wording. When laya-serve is unreachable or busy, validation fails unless you call->passWhenUnavailable(). Other errors are thrown. The defaultvalidation.layamessage ships in 12 languages under thelayanamespace, and an app's ownvalidation.layawins.AsDecisionEloquent cast.AsDecision::of(Triage::class)orAsDecision::class.':'.Triage::classstores a decision object in a JSON column. Enums are stored by backing value and read back through the constructor, so readonly classes and private promoted properties work. Lists (#[Of]), floats,#[Scale]enums andnullround-trip, and anullcolumn is anullattribute. Assigning anything other than an instance of the class, or reading JSON with a missing key, a wrong type or an unknown enum value, throws the newInvalidDecisionCastException, which names the model attribute.floatparameters in decision classes. Afloatwith#[Levels]becomes a score question and gets the expected level (ScoreAnswer::$score). Afloatwithout#[Levels]becomes a yes/no question and gets P(yes). You can now sort and rank withdecide()instead of dropping down topredict().thresholdon a float throwsInvalidQuestionException.Laya::fake()accepts a fractional expected level and splits it between the two neighbouring levels (1.3 → 70% level 1, 30% level 2).decideWithResult()anddecideManyWithResult(). They return aDecisionwith the hydrated$valueand the raw$result, so probabilities, confidences andtruncatedare available without asking the questions twice. The batch variant is@experimentallikedecideMany()and keeps the keys of$states.laya:evalandvendor/bin/laya-eval. Score a decision class against labelled rows from CSV or JSON Lines. The report covers accuracy, a confusion matrix (choice, score) or precision and recall (yes/no), calibration peranswerConfidencebucket, the lowestminConfidencethat reaches--precisionwith the share sent to a human, and the best-F1thresholdfor yes/no.--jsonprints it as JSON.vendor/bin/laya-evalruns without Laravel and reads--url/--api-keyorLAYA_URL/LAYA_API_KEY. TheEvaluationclass gives you the same report in PHP. Seeexamples/08-evaluation.php.Laya::questions(). It returns the questionsdecide()would send for a class and throws the sameInvalidQuestionExceptionfor a broken one, so a one-line test checks a decision class without a server or a fake.- Versioned cache keys. With
versionCache(LAYA_CACHE_VERSIONEDin Laravel, off by default), cache keys include laya-serve's checkpoint revisions, so cached answers from an old checkpoint aren't served after an upgrade./healthis called once per instance, and the revision hash is cached for 60 s. If/healthfails, the plain key is used and the prediction still goes through. - The full usage report on
Result.stateTokens,stateTokensDropped,truncatedQuestionsandcollapsedOptions(aCollapsedOptionsper question id withtotal,distinctandtokensPerOption). Servers that don't send them leave the fieldsnullor empty.laya:trynames the cut-off questions and the dropped tokens in its truncation warning, and its--jsonoutput includes the whole report. - HTTP client settings in Laravel.
LAYA_TIMEOUTandLAYA_RETRIESbuild asymfony/http-clientclient that retries on 503 and transport errors with backoff. To use your own client, setlaya.http_clientto a service id or bindPsr\Http\Client\ClientInterface; it takes precedence over both settings. Setting either one withoutsymfony/http-clientinstalled throws an exception that says what to install. - Configurable batch size. Pass
batchSize:tonew Laya()(Laravel:batch_size, fromLAYA_BATCH_SIZE). The default stays 64, and a size below 1 throwsInvalidOptionException. - Server limits in
Laya::fake(). The fake now rejects what laya-serve rejects: too many questions, choice options, score levels or total options, too long a state, and too large a batch, with laya-serve's wording. Passlimits:(MarcReichel\Laya\Testing\Limits) to change them, orLimits::none()to turn them off.
Improved
- Server errors from
decide()anddecideMany()name the decision class, e.g.Triage: laya-serve: too many questions (65 > 64) (HTTP 413).
Upgrading
There are no breaking API changes, but two things behave differently:
Laya::fake()enforces laya-serve's limits by default. A test that fakes more than 64 questions, an oversized batch or a very long state now throws aValidationException, just as the real server would answer. Passlimits: Limits::none()to get the old behaviour.ValidationExceptionmessages fromdecide()anddecideMany()start with the class name. Update any test that matches the exact message.
compose.yaml now builds Laya v0.4.6 (c73908d) instead of v0.4.0. The 0.4.1–0.4.6 changes are additive and need no SDK changes.