Skip to content

fix: keep non-ASCII in prompt schemas - #1131

Merged
andreatnvidia merged 2 commits into
NVIDIA-NeMo:mainfrom
spped2000:fix/non-ascii-prompt-schemas
Nov 9, 2026
Merged

andreatnvidia merged 2 commits into
NVIDIA-NeMo:mainfrom
spped2000:fix/non-ascii-prompt-schemas

Conversation

@spped2000

Copy link
Copy Markdown
Contributor

📋 Summary

StructuredResponseRecipe and PydanticResponseRecipe serialize the response schema with json.dumps, which defaults to ensure_ascii=True. Any non-ASCII text in a structured-output or LLM-judge schema (descriptions, enum values, score rubrics) is therefore sent to the model as \uXXXX escapes inside <response_schema>. This PR serializes the schema with ensure_ascii=False, as serialize_output in the same file already does. ASCII-only schemas are unchanged.

🔗 Related Issue

Fixes #1130

🔄 Changes

  • StructuredResponseRecipe.schema and PydanticResponseRecipe.schema: json.dumps(..., ensure_ascii=False). These are the strings embedded in <response_schema> in task_instructions.
  • StructuredResponseRecipe.generate_response_example: same change. It has no production callers today, so this is for consistency with serialize_output, not a behavior fix.
  • test_response_recipes.py: one parametrized test (a Thai pydantic model and the same schema as a dict) checks that task_instructions contains the Thai text, has no \u0e escapes, and that schema still round-trips through json.loads. One more test covers the example round-trip. A third (AsciiFoo, pydantic and dict) checks that an ASCII-only schema serializes exactly as json.dumps did before, as requested in Structured-output prompt schemas escape non-ASCII text as \uXXXX #1130.

Before (main), LLM-judge rubric Score(description="ประเมินความถูกต้อง", options={1: "ผิดทั้งหมด", 3: "ถูกต้องครบถ้วน"}) rendered through create_response_recipe():

{"$defs": {"accuracy": {"description": "\u0e1b\u0e23\u0e30\u0e40\u0e21\u0e34\u0e19\u0e04\u0e27\u0e32\u0e21\u0e16\u0e39\u0e01\u0e15\u0e49\u0e2d\u0e07", ...
escaped: 42 literal: 16

After:

{"$defs": {"accuracy": {"description": "ประเมินความถูกต้อง", ... "* 1: ผิดทั้งหมด\n* 3: ถูกต้องครบถ้วน"}}, ...
escaped: 0 literal: 58

For a three-option Thai rubric, a local vLLM server (Qwen3.6-35B-A3B tokenizer) counted 623 prompt tokens before and 315 after. In one request each way, both replies were valid fenced JSON and parsed with the recipe. That single sample makes no claim about judge quality; the change is to what the model reads.

🧪 Testing

CPU only (CUDA_VISIBLE_DEVICES=), dev environment from uv sync --all-packages --group dev (Python 3.14.6):

pytest -q packages/data-designer-engine/tests/engine/models/recipes/test_response_recipes.py
  with fix:                               102 passed
  src reverted to main (tests kept):      3 failed, 99 passed
    FAILED ...::test_json_recipe_task_instructions_keep_non_ascii_text[pydantic]
    FAILED ...::test_json_recipe_task_instructions_keep_non_ascii_text[structured]
    FAILED ...::test_structured_response_example_keeps_non_ascii_text
    (each fails on the `assert '<Thai text>' in ...` check; the ASCII test passes on main too)
pytest -q packages/data-designer-engine/tests   2949 passed
pytest -q packages/data-designer-config/tests   830 passed
pytest -q packages/data-designer/tests          1335 passed, 3 skipped
make check-all-fix                              All checks passed, no files changed
  • make test passes (ran the three package test suites above directly with pytest)
  • Unit tests added/updated
  • E2E tests added/updated (if applicable): not applicable

Scope and limitations:

  • Only the prompt-side schema serialization changes. Response parsing and serialize_output already handle non-ASCII text.
  • engine/mcp/io.py (L502, L531) also uses the default ensure_ascii=True for MCP tool results. It is mentioned in the issue as a possible follow-up and left out of this PR.
  • Prompts built from non-ASCII schemas change text (escapes become literal characters). Anyone who compares rendered prompts byte for byte across versions will see a difference for those schemas only.

Why this is not a duplicate: I found no issue or PR about schema escaping. #800 is the only other ensure_ascii change (JSONL scheduler events). #971/#972 changed bare-JSON parsing in the same recipes. #1125 is an open feature request for an opt-in native schema-enforcement mode, a separate feature with no PR yet. No open PR touches response_recipes.py.

I prepared this with help from Claude (Anthropic) and reviewed and tested it myself.

✅ Checklist

  • Follows commit message conventions
  • Commits are signed off (DCO)
  • If I am an external contributor, the linked issue is open and labeled triaged
  • Architecture docs updated (if applicable): not applicable, no architecture or Fern page describes schema serialization

🤖 Generated with Claude Code

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 NVIDIA-NeMo#1130

Signed-off-by: spped2000 <spped2000@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@spped2000
spped2000 requested a review from a team as a code owner November 4, 2026 16:57
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@greptile-apps

greptile-apps Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge.

Summary

The PR keeps non-ASCII text readable in schemas sent to the model.

  • Structured and judge prompts keep non-ASCII schema text readable.
  • Structured response examples keep non-ASCII values readable.

Reviews (2) · Last reviewed commit: "Merge branch 'main' into fix/non-ascii-p..." · Reviewed by Greptile

@spped2000

Copy link
Copy Markdown
Contributor Author

I have read the DCO document and I hereby sign the DCO.

@andreatnvidia andreatnvidia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together! This covers the changes and tests we discussed in #1130. Looks good to me.

@andreatnvidia
andreatnvidia merged commit 00e20c0 into NVIDIA-NeMo:main Nov 9, 2026
54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Structured-output prompt schemas escape non-ASCII text as \uXXXX

2 participants