[api][plan][python][examples] Framework-managed LLM-as-judge routing (Strategies.llm) - #1042
Open
purushah wants to merge 2 commits into
Open
[api][plan][python][examples] Framework-managed LLM-as-judge routing (Strategies.llm)#1042purushah wants to merge 2 commits into
purushah wants to merge 2 commits into
Conversation
…(Strategies.llm) The follow-up promised in discussion apache#897 and the v1 javadoc: the engine — not the strategy — executes the judge call, on the same durable, metered, observable chat path as any model call. - Strategies.llm(judgeModel[, promptTemplate]): declarative built-in; the judge is any registered CHAT_MODEL; candidate describe(...) lines are its decision criteria. - LlmJudgeRoutingStrategy carries config plus two pure functions (prompt build, verdict parse); route() throws — it is never invoked. The verdict parser scans every "model" match for a candidate, so a chatty judge that quotes the format contract still parses. - ModelRoutingResolver runs the judge via the invoker under durable id "judge:<router>" (engine retries, trace events, token attribution to the judge model), derives the decision as a pure function of the verdict, and persists it — with its source and judge-inclusive decision_ms — under the standard "route:<router>" id; replayed decisions are guarded against candidate-set changes like the strategy path. - Failure policy: unparseable or non-candidate verdicts abstain to the default model; a judge that exhausts its retries honors the request's error-handling strategy (FAIL is loud, IGNORE degrades to the default with the cause recorded); interrupts propagate. - Shared retry-policy helpers on ChatModelInvoker (used by both the chat path and the judge path). - Python: add_resource(MODEL_ROUTER) now raises an explicit not-yet-supported error instead of dropping silently (per apache#964 review). - ModelRoutingJudgeExample mirrors ModelRoutingExample with a judge. Generated-by: Claude Code 2.1.239 (Claude Fable 5)
A typo'd judge model name would not fail the job: every judge call would fail and (under IGNORE) abstain to the default model, silently disabling routing. All resources are known at plan construction, so fail there instead — same fail-fast standard as the router/chat-model name-clash check. Validation instantiates the strategy exactly as the runtime does (instanceof dispatch + getJudgeModel()), so subclasses are judged by what they actually return; anything not instantiable at plan time is left to the runtime's own error. Null-tolerant for previously-legal constructor inputs. Generated-by: Claude Code 2.1.239 (Claude Fable 5)
purushah
force-pushed
the
model-routing-v2
branch
from
August 23, 2026 19:11
1c16662 to
bb615cb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements the
Strategies.llm(...)follow-up promised in discussion #897 and documented as the framework-managed follow-up in the #964 javadocs: the engine — not the strategy — executes the judge call, on the same durable, metered, observable chat path as any model call.Design (per the #897 agreement)
LlmJudgeRoutingStrategycarries configuration plus two pure functions (prompt build, verdict parse);route()is never invoked — the resolver detects the strategy and runs the judge chat under durable id"judge:<router>"(engine retries, trace events, token attribution to the judge model), then persists the decision — with its source and judge-inclusivedecision_ms— under the standard"route:<router>"id. Judge abstains persist as abstains, so replay after a candidate-set change resolves to the current default exactly like the strategy path.describe(...)lines; an unparseable, ambiguous (multiple distinct candidates named), or non-candidate verdict abstains to the default model with the cause recorded. A hijacked judge cannot steer routing outside the declared candidates.FAILsurfaces the outage loudly,IGNOREabstains to the default with the cause recorded); interrupts/cancellation propagate and are never persisted as routing outcomes (including theInterruptedIOExceptionshapes HTTP stacks use — withSocketTimeoutExceptionexplicitly treated as an ordinary failure, not cancellation).build()constructs the strategy so its argument rules apply at the registration call site, andAgentPlanvalidates at plan construction — by instantiating the strategy exactly as the runtime does — that the judge name resolves to a registeredCHAT_MODEL. A judge setup with a bound prompt, tools, or skills is rejected (it would silently corrupt every verdict). Plan-time validation never instantiates non-judge custom strategies (gated by assignability before construction; regression-tested).add_resource(..., MODEL_ROUTER)now raises an explicit not-yet-supported error instead of dropping silently (flagged in the [api][plan][runtime][examples] Add pluggable in-chat model routing (MODEL_ROUTER) #964 review); a cross-language test proves Java plans carrying the new strategy args still deserialize in Python.Compatibility impact
decision_sourcevaluellm_judgeand new optional metadata keys (judge_model,judge_prompt_tokens,judge_completion_tokens,decision_source).route:<router>in addition tojudge:<router>) to keep the route-record schema uniform across router types and preserve replayeddecision_ms.Test evidence
flink runof the newModelRoutingJudgeExample, local Ollama): judge verdicts routed correctly with full metadata; fallback composition exercised live.Generative AI was used for this change.
Generated-by: Claude Code 2.1.239 (Claude Fable 5)