fix(translation): validate known request fields - #317
Conversation
Signed-off-by: nachiketb <nachiketb@nvidia.com>
WalkthroughChangesRequest validation and decoding
Estimated code review effort: 4 (Complex) | ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/switchyard-translation/src/codecs/openai_chat/buffered.rs (1)
582-585: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the shared validator.
validate_function_definitionis now crate-visible and is consumed bycrates/switchyard-translation/src/codecs/responses/buffered.rsthrough the re-export incrates/switchyard-translation/src/codecs/openai_chat/mod.rs. Add a short///comment that states the shape it validates and that both OpenAI Chat and OpenAI Responses tool definitions use it.♻️ Proposed doc comment
+/// Validates the shared OpenAI function-definition shape (`name`, +/// `description`, `parameters`, `strict`). OpenAI Chat and OpenAI Responses +/// tool definitions both use this contract. pub(crate) fn validate_function_definition( function: &Map<String, Value>, path: &str, ) -> Result<()> {As per coding guidelines: "Add concise
///documentation for public Rust items and comments for module intent, non-obvious private helpers, and important validation, routing, configuration, async, lifecycle, concurrency, and test behavior."🤖 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 `@crates/switchyard-translation/src/codecs/openai_chat/buffered.rs` around lines 582 - 585, Add a concise Rust doc comment directly above validate_function_definition describing the function-definition shape it validates and noting that both OpenAI Chat and OpenAI Responses tool definitions use this shared validator.Source: Coding guidelines
🤖 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.
Nitpick comments:
In `@crates/switchyard-translation/src/codecs/openai_chat/buffered.rs`:
- Around line 582-585: Add a concise Rust doc comment directly above
validate_function_definition describing the function-definition shape it
validates and noting that both OpenAI Chat and OpenAI Responses tool definitions
use this shared validator.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: b3ad9625-26cc-4a76-b76a-0511ed9648b7
📒 Files selected for processing (7)
crates/switchyard-translation/src/codecs/anthropic/buffered.rscrates/switchyard-translation/src/codecs/openai_chat/buffered.rscrates/switchyard-translation/src/codecs/openai_chat/mod.rscrates/switchyard-translation/src/codecs/responses/buffered.rscrates/switchyard-translation/src/util.rscrates/switchyard-translation/tests/lossless_roundtrip.rscrates/switchyard-translation/tests/request_translation.rs
What
Validate known request fields before decoding OpenAI Chat Completions, OpenAI Responses, and Anthropic Messages payloads into the neutral IR.
The decoder now distinguishes malformed JSON types (
InvalidType) from unsupported values (InvalidValue) and reports the exact JSON path. Unknown fields and unknown content-block types remain preserved for forward compatibility.This also corrects the Responses encoder's image and file shapes so payloads emitted by Switchyard satisfy the same validation contract.
Closes SWITCH-1205.
Why
Several decoder paths previously used permissive accessors such as
as_str,as_array, oras_object. A malformed known field could therefore be dropped, defaulted, or normalized into a different valid request instead of being rejected at the translation boundary.Error contract
InvalidTypeexpected <type> at <JSONPath>InvalidValueinvalid value at <JSONPath>: <reason>Source of truth and checks
OpenAI Chat Completions
Source: Create chat completion
model,stream,temperature,top_pInvalidType, e.g.expected boolean at $.streammax_completion_tokens, legacymax_tokensInvalidTypefor non-numeric JSON;InvalidValue, e.g.invalid value at $.max_completion_tokens: expected a non-negative integerresponse_formatInvalidTypeat$.response_formatreasoning_effortInvalidTypefor non-string JSON;InvalidValuenaming the unsupported value and allowed valuesmessagesand each messageInvalidType, e.g.expected object at $.messages[0]rolesystem,developer,user,assistant,tool, or legacyfunctionInvalidTypeorInvalidValueat$.messages[n].rolecontentInvalidType, e.g.expected string or array at $.messages[0].contentInvalidTypeorInvalidValueat the full block path, such as$.messages[0].content[0].image_url.detailtool_callsInvalidTypeorInvalidValue, e.g.expected string at $.messages[0].tool_calls[0].function.argumentstoolsand function definitionsstrictInvalidTypeat the exact definition fieldtool_choicenone,auto,required, or an object; validate named function selectionInvalidTypeorInvalidValueat$.tool_choiceor its nested fieldOpenAI Responses
Source: Create a response
model,stream,temperature,top_pInvalidTypeat the exact top-level pathmax_output_tokensInvalidTypeorInvalidValue, e.g.invalid value at $.max_output_tokens: expected a non-negative integerinstructionsInvalidType, e.g.expected string at $.instructionstext.formattypewhen presentInvalidTypeat$.text,$.text.format, or$.text.format.typereasoning.effortInvalidTypeorInvalidValueat$.reasoning.effortinputInvalidType, e.g.expected string or array at $.inputInvalidTypeorInvalidValueat$.input[n],.role, or.contentcall_id,name, andargumentsInvalidType, e.g.expected string at $.input[0].argumentscall_idand string-or-content-arrayoutputInvalidType, e.g.expected string or array at $.input[0].outputcontentandsummaryblocksInvalidTypeat the exact reasoning item pathInvalidTypeorInvalidValue, e.g.expected string at $.input[0].content[0].image_urltoolsInvalidTypeat$.toolsor the exact nested fieldtool_choicenone,auto,required, or an object; validate named function selectionInvalidTypeorInvalidValueat$.tool_choiceor its nested fieldAnthropic Messages
Sources: Create a Message and Effort
model,stream,temperature,top_p,top_kInvalidTypeorInvalidValueat the exact top-level pathmax_tokens0as documentedInvalidType, e.g.expected non-negative integer at $.max_tokens, orInvalidValuefor negative/fractional valuessystemInvalidType, e.g.expected string or array of text blocks at $.systemmessagesand each messageInvalidType, e.g.expected object at $.messages[0]roleuserorassistant; reject a message-levelsystemrole because Anthropic uses top-levelsystemInvalidTypeorInvalidValueat$.messages[n].rolecontentInvalidTypeat the exact content or block pathInvalidTypeat the exact block fieldtool_useandtool_resultblocksInvalidType, e.g.expected object at $.messages[0].content[0].inputsourcevalues to be objectsInvalidTypeat the block's.sourcepathtoolsinput_schemaInvalidType, e.g.expected object at $.tools[0].input_schematool_choiceauto,any,tool, ornone, optional tool name, and boolean parallel-use flagInvalidTypeorInvalidValueat$.tool_choiceor its nested fieldthinkingenabled,disabled, oradaptiveand a non-negative integer budgetInvalidTypeorInvalidValueat$.thinkingor its nested fieldoutput_configInvalidTypeorInvalidValueat$.output_configor its nested fieldScope boundaries
What to review
InvalidTypeversusInvalidValueis applied consistently.Validation
cargo test -p switchyard-translationcargo clippy -p switchyard-translation --all-targets -- -D warningscargo fmt,cargo clippy, andcommitlintSummary by CodeRabbit