From d44bf1eb6250057b7c0a73e11310eeffaacad56c Mon Sep 17 00:00:00 2001 From: Afloat <132809511+Afloat16@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:22:43 +0800 Subject: [PATCH] Preserve string and boolean literal constraints in strict schemas --- deepseek-recipe/src/protocol/json_schema.rs | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/deepseek-recipe/src/protocol/json_schema.rs b/deepseek-recipe/src/protocol/json_schema.rs index f00a97d..c129ac4 100644 --- a/deepseek-recipe/src/protocol/json_schema.rs +++ b/deepseek-recipe/src/protocol/json_schema.rs @@ -78,6 +78,8 @@ pub(crate) enum JsonSchemaType { format: Option, #[serde(skip_serializing_if = "Option::is_none")] r#enum: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + r#const: Option, }, #[serde(rename_all = "camelCase")] Number { @@ -121,6 +123,8 @@ pub(crate) enum JsonSchemaType { Boolean { #[serde(skip_serializing_if = "Option::is_none")] description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + r#const: Option, }, #[serde(rename_all = "camelCase")] Array { @@ -364,3 +368,28 @@ mod tests { ); } } + +#[cfg(test)] +mod contribution_regression { + use super::*; + + #[test] + fn primitive_constants_survive_normalization() { + for property in [ + serde_json::json!({"type":"string","const":"fixed"}), + serde_json::json!({"type":"boolean","const":true}), + ] { + let result = normalize_strict_schema( + serde_json::json!({"type":"object","properties":{"value":property.clone()}}), + true, + |e| e.to_string(), + "root", + ) + .unwrap(); + assert_eq!(result["properties"]["value"], property); + let validator = jsonschema::options().offline().build(&result).unwrap(); + assert!(validator.is_valid(&serde_json::json!({"value":property["const"]}))); + assert!(!validator.is_valid(&serde_json::json!({"value":if property["type"] == "string" {serde_json::json!("other")} else {serde_json::json!(false)}}))); + } + } +}