sangkyoonnam opened a new issue, #1150: URL: https://github.com/apache/flink-agents/issues/1150
### Search before asking - [x] I searched in the [issues](https://github.com/apache/flink-agents/issues) and found nothing similar. ### Description `ModelRouter.Builder#build()` checks that every rule key names a candidate, but not the candidate declaration itself. A duplicate candidate, an empty candidate name, an empty candidate list, or a `defaultModel(...)` that is not one of the candidates all pass `build()`. `AgentPlan#validateRoutingStrategies` (`plan/.../AgentPlan.java:749-810`) has the same gap for descriptor-built plans: it validates the strategy type tag, the `candidates` shape, rule keys and the judge model, but not the candidate names or `default_model`. Its comment leaves a missing `candidates` argument "to the constructor's own message". The checks exist only in the `ModelRouter` constructor (`api/.../ModelRouter.java:75-95`). That constructor runs on the TaskManager when the router resource is first resolved, inside the durable `route:<router>` call, per routed request (`ModelRoutingResolver#resolve` -> `ctx.getResource`). Under the default `FAIL` policy the job dies on the first routed record instead of at submission; under `IGNORE` every routed record is dropped while the job stays RUNNING. The same static-constraint reasoning already moved the judge-model and rule-key checks to plan construction in #1042. The docs table for `defaultModel(String)` already says "Must be a candidate." Expected: - `build()` rejects a duplicate or empty candidate, an empty candidate list, and a default model that is not a candidate, with the constructor's messages. - Plan construction rejects the same declarations for descriptors that never went through the builder, naming the router as the other plan-time router checks do. - One validation path shared by the constructor, the builder and plan construction, so the diagnostics cannot drift. Java side only: Python cannot register routers yet (`check_registrable_from_python`). ### How to reproduce ```java // Each of these passes build() today and throws only on the TaskManager per routed request. ModelRouter.of("small", "big") .strategy(Strategies.rules(Map.of())) .defaultModel("huge") .build(); ModelRouter.of("small", "small").strategy(Strategies.rules(Map.of())).build(); ModelRouter.of("small", "").strategy(Strategies.rules(Map.of())).build(); ModelRouter.of().strategy(Strategies.rules(Map.of())).build(); ``` A hand-built descriptor with `"default_model": "huge"` or `"candidates": ["small", "small"]` likewise passes `new AgentPlan(...)`; a regression test can be added next to `AgentPlanLlmJudgeValidationTest#ruleKeyNamingNonCandidateFailsAtPlanConstruction`. ### Version and environment Current `main` (`0.4-SNAPSHOT`, e8f79d51). Java 21, macOS ARM64; not platform specific. ### Are you willing to submit a PR? - [x] I'm willing to submit a PR! -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
