fix: strip null enum values unconditionally in enforce_strict_schema - #3768
Open
Wang-tianhao wants to merge 1 commit into
Open
fix: strip null enum values unconditionally in enforce_strict_schema#3768Wang-tianhao wants to merge 1 commit into
Wang-tianhao wants to merge 1 commit into
Conversation
When kimi-k3 is routed through OpenRouter, the EnforceStrictToolSchema transformer is not applied (it only matches the direct MOONSHOT/KIMI_CODING provider IDs). As a result, the tool schema sent to Moonshot retains a null literal inside the output_mode enum array, which Moonshot rejects with: 400 Bad Request: enum value (<nil>) does not match any type in [string] The null entry originates from schemars' AddNullable transform applied in ToolCatalog::schema (catalog.rs:893) for Option<Enum> fields. Fix: hoist the null-stripping logic out of the strict_mode branch so it runs unconditionally. The anyOf conversion remains strict-mode-only. This makes the non-strict path safe for all OpenAI-compatible providers, not just the ones explicitly gated into strict mode. Fixes tailcallhq#3767
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.
Summary
Fixes #3767
When
kimi-k3is routed through OpenRouter, every request fails with a400 Bad Requestbecause the tool schema contains an invalidnullvalue inside theoutput_modeenum array, which Moonshot AI rejects:Root Cause
ToolCatalog::schema(crates/forge_domain/src/tools/catalog.rs:893) applies schemars'AddNullabletransform, which appendsnullto enum arrays forOption<Enum>fields.enforce_strict_schema(crates/forge_app/src/utils.rs) only stripped those null entries inside thestrict_modebranch.EnforceStrictToolSchematransformer (crates/forge_app/src/dto/openai/transformers/pipeline.rs:102-112) is only enabled for the directMOONSHOT/KIMI_CODINGprovider IDs — not forOPEN_ROUTER. So when kimi is accessed via OpenRouter, the null enum value survives and is sent to Moonshot, which rejects it.Fix (Approach B)
Hoist the null-stripping logic out of the
strict_modebranch so it runs unconditionally. TheanyOfconversion (andnullableremoval) remains strict-mode-only.This makes the non-strict path safe for all OpenAI-compatible providers, not just the ones explicitly gated into strict mode. A
nullliteral inside a typedenumarray is a non-standard representation that multiple providers reject, so stripping it unconditionally is the more robust behavior.Changes
crates/forge_app/src/utils.rs: moved theenum_values.retain(|v| !v.is_null())call to run before thestrict_mode/nullablecheck.test_null_enum_values_stripped_in_non_strict_modeverifying null is removed in non-strict mode whilenullable: trueis preserved.Test Plan
cargo test -p forge_app --lib utils::tests— 64 passed (including new test)cargo test -p forge_app --lib dto::openai::transformers— 106 passedcargo test -p forge_repo --lib provider::openai_responses— 139 passed (including catalog tool snapshot tests)cargo test -p forge_app— full suite greenAlternative Considered
Approach A (narrower) would add
|| when_model("kimi")(request)to thestrict_schemacondition inpipeline.rs. That only fixes kimi specifically and leaves the latent issue for any other OpenAI-compatible provider that rejects null enum values. Approach B is broader and prevents the class of bug entirely.