From a9896cd0727215c9f140fce8f60b61ca067b961b Mon Sep 17 00:00:00 2001 From: spped2000 Date: Wed, 4 Nov 2026 20:43:08 +0700 Subject: [PATCH] fix: keep non-ASCII in prompt schemas (#1130) StructuredResponseRecipe.schema and PydanticResponseRecipe.schema serialize the JSON schema with json.dumps' default ensure_ascii=True. The schema is embedded in the user prompt, so non-ASCII descriptions, enum values and LLM-judge rubrics (Thai, CJK, accented Latin, ...) reach the model as \uXXXX escapes instead of readable text. Serialize with ensure_ascii=False, as serialize_output in the same file already does. ASCII-only schemas are unchanged. generate_response_example gets the same change for consistency. Tests cover Thai descriptions and enum values and check that an ASCII-only schema serializes exactly as before. Fixes #1130 Signed-off-by: spped2000 Co-Authored-By: Claude Opus 5.5 --- .../engine/models/recipes/response_recipes.py | 6 +-- .../models/recipes/test_response_recipes.py | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/data-designer-engine/src/data_designer/engine/models/recipes/response_recipes.py b/packages/data-designer-engine/src/data_designer/engine/models/recipes/response_recipes.py index e32f212cf..675303f6b 100644 --- a/packages/data-designer-engine/src/data_designer/engine/models/recipes/response_recipes.py +++ b/packages/data-designer-engine/src/data_designer/engine/models/recipes/response_recipes.py @@ -106,11 +106,11 @@ def example_template(self) -> str: return "```json\n{example}\n```" def generate_response_example(self, example: dict) -> str: - return self.example_template.format(example=json.dumps(example)) + return self.example_template.format(example=json.dumps(example, ensure_ascii=False)) @property def schema(self) -> str: - return json.dumps(self.json_schema) + return json.dumps(self.json_schema, ensure_ascii=False) def serialize_output(self, output: dict) -> str: return json.dumps(output, ensure_ascii=False) @@ -182,7 +182,7 @@ def __init__(self, data_type: type[BaseModel], **kwargs): @property def schema(self) -> str: - return json.dumps(self.data_type.model_json_schema()) + return json.dumps(self.data_type.model_json_schema(), ensure_ascii=False) @property def task_instructions(self) -> str: diff --git a/packages/data-designer-engine/tests/engine/models/recipes/test_response_recipes.py b/packages/data-designer-engine/tests/engine/models/recipes/test_response_recipes.py index de868fd54..6482af326 100644 --- a/packages/data-designer-engine/tests/engine/models/recipes/test_response_recipes.py +++ b/packages/data-designer-engine/tests/engine/models/recipes/test_response_recipes.py @@ -322,6 +322,47 @@ def test_structured_response_task_instructions(): assert "Baz!!" in recipe.task_instructions +class ThaiFoo(BaseModel): + province: str = Field(..., description="ชื่อจังหวัด") + region: str = Field(..., description="ภาค", json_schema_extra={"enum": ["ภาคเหนือ", "ภาคใต้"]}) + + +class AsciiFoo(BaseModel): + province: str = Field(..., description="Province name") + region: str = Field(..., description="Region", json_schema_extra={"enum": ["North", "South"]}) + + +@pytest.mark.parametrize( + "recipe", + [PydanticResponseRecipe(ThaiFoo), StructuredResponseRecipe(ThaiFoo.model_json_schema())], + ids=["pydantic", "structured"], +) +def test_json_recipe_task_instructions_keep_non_ascii_text( + recipe: PydanticResponseRecipe | StructuredResponseRecipe, +) -> None: + assert "ชื่อจังหวัด" in recipe.task_instructions + assert "ภาคเหนือ" in recipe.task_instructions + assert "\\u0e" not in recipe.task_instructions + assert json.loads(recipe.schema) == ThaiFoo.model_json_schema() + + +@pytest.mark.parametrize( + "recipe", + [PydanticResponseRecipe(AsciiFoo), StructuredResponseRecipe(AsciiFoo.model_json_schema())], + ids=["pydantic", "structured"], +) +def test_json_recipe_schema_unchanged_for_ascii(recipe: PydanticResponseRecipe | StructuredResponseRecipe) -> None: + assert recipe.schema == json.dumps(AsciiFoo.model_json_schema()) + + +def test_structured_response_example_keeps_non_ascii_text() -> None: + recipe = StructuredResponseRecipe(ThaiFoo.model_json_schema()) + example = {"province": "เชียงใหม่", "region": "ภาคเหนือ"} + + assert "เชียงใหม่" in recipe.generate_response_example(example) + assert recipe.parse(recipe.generate_response_example(example)) == example + + def test_pydantic_response_patched_case_single_lb(): recipe = PydanticResponseRecipe(Foo) example = Foo(bar=Bar(baz=42))