Repository navigation
1.3.0
Decision classes get two new shapes: an enum can be a score scale, and an array of enum cases asks one yes/no question per case. laya-serve can now abstain on low-confidence answers itself, predictions dispatch PSR-14 events, and php artisan laya:try lets you test a decision class on a piece of text without writing code.
final class Triage
{
public function __construct(
#[Ask('How urgent is this?')]
public Urgency $urgency, // #[Scale] enum: Low, Normal, Blocking
#[Ask('Does the message mention {case}?')]
#[Of(Topic::class)]
public array $topics, // [Topic::Billing, Topic::Account]
) {}
}
$triage = $laya->decide($ticket, Triage::class, minConfidence: 0.7);New
#[Of(Enum::class)]on array parameters. laya asks one yes/no question per case and fills{case}in the question with the case's#[Describe]text or its value. You get the cases with P(yes) ≥ 0.5 (or yourthreshold), in declaration order. The questions have ids liketopics.billing. A class that needs more than 64 questions throws anInvalidQuestionException.#[Scale]enums. A backed enum marked#[Scale]becomes a score question. Its cases are the levels, lowest first, and you get back the case at the most likely level instead of an index. A backed enum without#[Scale]stays a choice question.- Server-side abstention.
predict(),predictMany(),decide()anddecideMany()takeminConfidence. Pass one threshold from 0 to 1, or a map with a threshold per option-count bucket (['choice:3-5' => 0.6, 'noul:2' => 0.8, 'default' => 0.7]). Answers report what the gate decided inabstention(the newAbstentionenum),abstentionThresholdandlowConfidence. A malformed value throws the newInvalidOptionExceptionbefore anything is sent. - PSR-14 events. Pass a dispatcher as
events:to getPredictionMadeafter each prediction (cache hits included),PredictionFailedright before an exception is thrown, andCacheFailedwhen the cache misbehaves. States are left out of the events unless you setincludeState. In Laravel, the service provider wires up the app's dispatcher, so listeners andEvent::fake()work without any setup. Turn events off withLAYA_EVENTS=false, or include states withLAYA_EVENTS_INCLUDE_STATE=true. php artisan laya:try. It runs a decision class on text from an argument,--fileor STDIN. For each parameter it prints the question, the hydrated value, the answer confidence and a bar for every option's probability. It also shows the routed checkpoint and warns when the text was cut off. It takes--model,--max-len,--head-max-lenand--json, and resolves short class names underApp\.- Idle unload in
laya:health.HealthStatusexposesidleUnloadSecondsandidleSeconds, andlaya:healthprints them. When laya-serve has unloaded its checkpoints after a quiet period, an empty "Loaded" list now comes with the reason. - More
Laya::fake().when()gives states that match a callback their own answers.sequence()hands out answers in order.assertNotPredicted()andassertNotDecided()check that something didn't happen.- The fake dispatches events, answers
#[Of]and#[Scale]parameters, and appliesminConfidencethe way laya-serve does.
- New example.
examples/07-model-routing.phproutes each prompt to the cheapest Claude model that can handle it.
Improved
- The cache is best-effort. If a cache read throws or returns something that isn't a laya response, Laya treats it as a miss. If a write throws, you still get the result, just uncached. Both dispatch
CacheFailed. - Identical states in one
predictMany()call are sent once. Each copy still gets its own result and its ownPredictionMade. - Decision classes are reflected once per process.
decide(),decideMany()andassertDecided()share one cached reading of each class. - A
PredictionFailedlistener that throws no longer hides the laya error. You still get the originalLayaException, such asServerBusyException.
Upgrading
There are no breaking API changes, but a few things behave differently:
- Responses that don't fit the decision class throw
ServerException. When laya-serve's answers are missing, have the wrong type or carry a label outside the enum,decide()anddecideMany()used to throwOutOfBoundsException,UnexpectedValueExceptionorValueError. They now throw aServerExceptionthat names the parameter and keeps the original exception asprevious. CatchLayaExceptionto handle these along with the other laya errors. - Events are on by default in Laravel. If you listen for nothing, they cost next to nothing. To turn them off, set
LAYA_EVENTS=false. psr/simple-cache^1.0 and ^2.0 are allowed as well as ^3.0.- Laya 0.4.0 changes the default routing.
compose.yamlnow builds laya v0.4.0 (3cf26cb). In 0.4.0, text whose language laya can't identify, such as very short text or text without letters, goes to themultilingualcheckpoint instead ofenglish. If your traffic is mostly English, start laya-serve withLAYA_DEFAULT_MODEL=englishto keep the old behaviour.Laya::fake()now reportsmultilingualwhen no model is pinned.
Each feature needs a minimum laya-serve version:
| Feature | laya-serve |
|---|---|
minConfidence per bucket, idle-unload fields |
0.3.25 |
| Multilingual default routing | 0.4.0 |