Repository navigation
feat(gateway): honor per-model parser overrides from model cards - #2098
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe change adds ChangesParser metadata and resolution
gRPC integration
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Request
participant ParserResolver
participant ResponseProcessor
participant ParserFactory
Request->>ResponseProcessor: process model request
ResponseProcessor->>ParserResolver: resolve tool and reasoning parsers
ParserResolver-->>ResponseProcessor: return effective parser names
ResponseProcessor->>ParserFactory: check availability and create parsers
ParserFactory-->>ResponseProcessor: return parser instances
ResponseProcessor-->>Request: return parsed response
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Clean PR. The ParserResolver abstraction is well-designed — precedence chain is correct (card override → configured global → auto-detect), alias handling is safe via get_by_model, validation rejects unknown parser names at registration time, and tests cover all branches. No issues found.
0 🔴 Important | 0 🟡 Nit | 0 🟣 Pre-existing
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
model_gateway/src/routers/grpc/regular/streaming.rs (1)
320-370: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win🔴 Important Keep the resolved parser names stable for one request.
The availability checks use a name resolved at request start. Later helpers query the live
WorkerRegistryagain. A worker registration or removal can change the selected override during processing. Chat streaming can then reach theexpectatprocess_reasoning_streamafter availability was checked for a different parser.
model_gateway/src/routers/grpc/regular/streaming.rs#L320-L370: pass the resolved Chat parser names intoprocess_reasoning_streamandprocess_tool_calls_stream.model_gateway/src/routers/grpc/regular/streaming.rs#L1889-L1944: pass the resolved Messages reasoning parser name intoprocess_messages_reasoning.model_gateway/src/routers/grpc/regular/processor.rs#L246-L271: pass the resolved Chat parser names intoprocess_single_choiceandparse_tool_calls.model_gateway/src/routers/grpc/regular/processor.rs#L510-L576: keep the resolved Messages parser names through parser creation and tool parsing.Add tests that change registry membership after availability checks and verify that the request continues with its initial parser selection.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@model_gateway/src/routers/grpc/regular/streaming.rs` around lines 320 - 370, Keep parser selection stable for each request by threading the names resolved during initial availability checks through all downstream parser operations. In model_gateway/src/routers/grpc/regular/streaming.rs:320-370, pass the resolved Chat names to process_reasoning_stream and process_tool_calls_stream; in streaming.rs:1889-1944, pass the resolved Messages reasoning name to process_messages_reasoning; in model_gateway/src/routers/grpc/regular/processor.rs:246-271, pass the resolved Chat names to process_single_choice and parse_tool_calls; and in processor.rs:510-576, retain the resolved Messages names through parser creation and tool parsing instead of rereading WorkerRegistry. Add tests that modify registry membership after availability checks and verify the request continues using its initial parser selection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@model_gateway/src/routers/grpc/utils/parsers.rs`:
- Around line 78-87: Update parser resolution around the registry lookup and
worker-registration flow so same-model workers cannot silently select the first
parser override by iteration order. Reject conflicting parser names for workers
registered under one model, or resolve the parser using the selected output
worker in both regular and PD dual-dispatch paths; add coverage with same-model
workers declaring different parsers and verify the conflict is rejected or the
output worker’s parser is used.
---
Outside diff comments:
In `@model_gateway/src/routers/grpc/regular/streaming.rs`:
- Around line 320-370: Keep parser selection stable for each request by
threading the names resolved during initial availability checks through all
downstream parser operations. In
model_gateway/src/routers/grpc/regular/streaming.rs:320-370, pass the resolved
Chat names to process_reasoning_stream and process_tool_calls_stream; in
streaming.rs:1889-1944, pass the resolved Messages reasoning name to
process_messages_reasoning; in
model_gateway/src/routers/grpc/regular/processor.rs:246-271, pass the resolved
Chat names to process_single_choice and parse_tool_calls; and in
processor.rs:510-576, retain the resolved Messages names through parser creation
and tool parsing instead of rereading WorkerRegistry. Add tests that modify
registry membership after availability checks and verify the request continues
using its initial parser selection.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5f3b02df-027b-4c7a-bc99-a79240417d8e
📒 Files selected for processing (10)
model_gateway/src/routers/grpc/context.rsmodel_gateway/src/routers/grpc/pipeline.rsmodel_gateway/src/routers/grpc/regular/processor.rsmodel_gateway/src/routers/grpc/regular/stages/chat/preparation.rsmodel_gateway/src/routers/grpc/regular/stages/messages/preparation.rsmodel_gateway/src/routers/grpc/regular/streaming.rsmodel_gateway/src/routers/grpc/router.rsmodel_gateway/src/routers/grpc/utils/mod.rsmodel_gateway/src/routers/grpc/utils/parsers.rsmodel_gateway/src/workflow/steps/local/create_worker.rs
e7744cd to
dd7aa79
Compare
Parser selection on the gRPC serving path was process-global: one --tool-call-parser / --reasoning-parser name per router, while the tokenizer side is already per-model. A gateway serving two model families could not parse both correctly, and the ModelCard tool_parser/reasoning_parser fields existed with builders but had no read sites. Wire them end to end: - build_model_card maps tool_parser / reasoning_parser worker labels into the card (explicit WorkerSpec cards keep their own values, the same precedence tokenizer_path uses), so overrides flow through the existing label pipeline with no new plumbing. - Worker registration rejects override names the parser registries don't know, instead of shipping silently unparsed output at serve time. - A ParserResolver replaces the configured-name plumbing in the gRPC processors, preparation stages, and shared components: per request, the model's card override wins, then the configured global name, then the factories' existing name-based auto-detection, unchanged. Behavior without overrides is identical; the debug /parse endpoints keep their explicit per-request override. Signed-off-by: yifeng liu <31553858+pallasathena92@users.noreply.github.com>
dd7aa79 to
95e9ca0
Compare
|
Addressed both findings in the amended commit:
|
Description
Problem
Parser selection on the gRPC serving path is process-global: one
--tool-call-parser/--reasoning-parsername per router process. The tokenizer side is already per-model (TokenizerRegistry), so a gateway serving two model families selects the right tokenizer for each but not the right parsers — one family's tool calls come back unparsed incontent. MeanwhileModelCard.tool_parser/ModelCard.reasoning_parserexist incrates/protocolswith builders and zero read sites.Solution
Wire the card fields end to end via the existing label pipeline: workers (or explicit
WorkerSpeccards) declare per-model parser names; the gRPC path resolves per request with precedence card override → configured global → existing name-based auto-detection (unchanged). Unknown names fail worker registration loudly instead of shipping silently unparsed output.Changes
build_model_cardmapstool_parser/reasoning_parserlabels into the card; explicit user-provided cards keep their own values (same precedencetokenizer_pathuses).CreateLocalWorkerStepvalidates override names against the parser registries at registration (same fail-fast the global CLI names get inAppContext).ParserResolverreplaces the twoOption<String>s threaded throughResponseProcessor,StreamingProcessor, chat/messages preparation stages, andSharedComponents; resolution borrows from worker metadata (no card clones on the hot path); conflicting same-model overrides resolve to the lexicographically smallest name (deterministic) with a loud registration-time warning, and resolved names are threaded through all downstream helpers so one request never mixes parsers even if the registry changes mid-stream.disabled()resolver — prior behavior byte-for-byte. Debug/parse/*endpoints keep their explicit per-request override.Test Plan
build_model_card: label mapping, empty-label = unset, explicit-card precedence.validate_parser_overrides: known/unknown names, absent factories skip.ParserResolver: card override wins over configured, configured fallback without a card, none→none, disabled resolver, deterministic conflict resolution under both registration orders.cargo test -p smg --lib: 1456 passed.cargo +nightly fmtclean;cargo clippy -p smg --all-targets -- -D warningsclean locally (--all-featuresvia CI).Checklist
cargo +nightly fmtpassescargo clippy --all-targets --all-features -- -D warningspasses