Structured-output prompt schemas escape non-ASCII text as \uXXXX
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 76/100
調査の方向性
packages/data-designer-engine/src/data_designer/engine/models/recipes/response_recipes.pyを読み、特にschemaプロパティとgenerate_response_exampleを確認してください。issueにあるタイ語のpydanticおよびdictのスキーマ例で再現し、その後response recipeのテストのカバレッジを確認してください。スキーマのテキストが非ASCII文字を保持し、ASCII文字のみのスキーマは変更されないことが完了条件です。
索引モデルが issue の本文から書いたものです。
説明
Priority Level
Medium (Annoying but has workaround)
Describe the bug
StructuredResponseRecipe.schema and PydanticResponseRecipe.schema serialize the JSON schema with json.dumps(...) and its default ensure_ascii=True:
packages/data-designer-engine/src/data_designer/engine/models/recipes/response_recipes.pyL113:return json.dumps(self.json_schema)- same file L185:
return json.dumps(self.data_type.model_json_schema())
That string is placed inside <response_schema> in the user prompt (L101 and L193). Any non-ASCII text in the schema (field descriptions, enum values, LLM-judge score descriptions and option labels) therefore reaches the model as \uXXXX escapes rather than as readable text. This affects LLMJudgeColumnConfig (score rubrics are turned into a pydantic model) and LLMStructuredColumnConfig (output_format as a dict or a pydantic model) whenever the schema text is not ASCII, for example Thai, CJK, Cyrillic or accented Latin.
The same file already uses ensure_ascii=False in StructuredResponseRecipe.serialize_output (L116). Parsing a Thai response works today; only the prompt side is affected.
Steps/Code to reproduce bug
Against main at ba00b05, CPU only, no model needed:
import re
from data_designer.config.column_configs import LLMJudgeColumnConfig, LLMStructuredColumnConfig, Score
from data_designer.engine.column_generators.utils.prompt_renderer import create_response_recipe
judge = LLMJudgeColumnConfig(
name="j",
model_alias="m",
prompt="ให้คะแนน {{ x }}",
scores=[Score(name="accuracy", description="ประเมินความถูกต้อง", options={1: "ผิดทั้งหมด", 3: "ถูกต้องครบถ้วน"})],
)
p = create_response_recipe(judge).apply_recipe_to_user_prompt("ให้คะแนนคำตอบนี้")
print(p.rsplit("<response_schema>", 1)[1][:200])
print("escaped:", len(re.findall(r"\\u0e[0-9a-f]{2}", p)), "literal:", len(re.findall(r"[-]", p)))
st = LLMStructuredColumnConfig(
name="s",
model_alias="m",
prompt="x",
output_format={"type": "object", "properties": {"region": {"type": "string", "description": "ภาค", "enum": ["ภาคเหนือ", "ภาคใต้"]}}},
)
print(create_response_recipe(st).schema)
Output on main:
{"$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
{"type": "object", "properties": {"region": {"type": "string", "description": "\u0e20\u0e32\u0e04", "enum": ["\u0e20\u0e32\u0e04\u0e40\u0e2b\u0e19\u0e37\u0e2d", "\u0e20\u0e32\u0e04\u0e43\u0e15\u0e49"]}}}
The 16 literal Thai characters are the user's own instruction. Every Thai character from the rubric and the schema is escaped.
Expected behavior
The schema in <response_schema> keeps non-ASCII text as written, the way serialize_output already does:
{"$defs": {"accuracy": {"description": "ประเมินความถูกต้อง", ... "* 1: ผิดทั้งหมด\n* 3: ถูกต้องครบถ้วน"}}, ...
{"type": "object", "properties": {"region": {"type": "string", "description": "ภาค", "enum": ["ภาคเหนือ", "ภาคใต้"]}}}
ASCII-only schemas should stay byte-identical.
Agent Diagnostic / Prior Investigation
I investigated this with Claude Code (Anthropic) on a clone of main at ba00b05. I reviewed the findings and reran them myself.
- Root cause:
json.dumpsdefaults toensure_ascii=TrueinStructuredResponseRecipe.schema(L113) andPydanticResponseRecipe.schema(L185).StructuredResponseRecipe.generate_response_example(L109) has the same default. It has no production callers, but it is the documented example format for that recipe. - The
schemaproperty is used in only one place, the<response_schema>block oftask_instructions. Nothing hashes or caches it, so changing its serialization changes only the prompt text. - Size: for a three-option Thai judge rubric, the full user prompt is 623 prompt tokens on main and 315 with
ensure_ascii=False, as reported by a local vLLM server (Qwen3.6-35B-A3B tokenizer). The difference comes entirely from the escapes. - Live check: one request each way (temperature 0, thinking disabled). Both replies were valid fenced JSON and parsed with the recipe. One sample does not show a difference in judge quality, and I am not claiming one. What changes is what the model reads:
\u0e1c\u0e34\u0e14...on main,ผิดทั้งหมดwith the fix. - Duplicate search (issues and PRs, open and closed: "ensure_ascii", "non-ASCII", "unicode", "Thai", "escape", "multilingual", "response_schema"): no match. #800 is the only other
ensure_asciihit, and it is about JSONL scheduler events. #971/#972 changed parsing in the same recipes, not schema serialization. #1125 (open feature request: opt-in native schema-enforcement mode forLLMStructuredColumnConfig) is a different feature. No open PR touchesresponse_recipes.py. - Proposed fix: pass
ensure_ascii=Falsein the twoschemaproperties and ingenerate_response_example, and add parametrized tests (Thai pydantic model and Thai dict schema) that fail on main and pass with the change. That is +30/-3 lines. I have it ready and can open a PR once this issue is triaged.
Additional context
- Workaround: write rubrics, descriptions and enum values in English. That does not help when the enum values themselves have to be in the target language.
- Possible follow-up, not part of this fix:
engine/mcp/io.pyL502 and L531 alsojson.dumpsMCP tool results into text blocks with the defaultensure_ascii=True. I have not checked how those blocks reach the model, so I am leaving them out of this issue's scope. - Environment: Linux aarch64 (DGX Spark), Python 3.14.6 (
uv sync --all-packages --group dev), CPU-only reproduction.
Checklist
- I reproduced this issue or provided a minimal example
- I searched the docs/issues myself, or had my agent do so
- If I used an agent, I included its diagnostics above
- 主要言語
- Python
- スター
- 2.4k
- フォーク
- 243
- 平均マージ
- 1日 3時間
- マージ済み PR(30日)
- 88
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
NVIDIA-NeMo/DataDesigner のほかの issue
-
Default OpenAI reasoning/vision model `gpt-5` is retired on 2026-12-11対応中かも @pcbeingused333 が今日担当しました。 オープンneeds-discussion triaged
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
NVIDIA-NeMo/DataDesigner#1129 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
Allow huggingface-hub 2.x (widen cap from <2 to <3)対応中かも @davanstrien が 9 日前に担当しました。 オープンenhancement triaged
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
NVIDIA-NeMo/DataDesigner#1110 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Local seed path containing a single quote breaks the DuckDB query再び着手できるかも このイシューのプルリクエストはマージされずにクローズされました。 オープンbug triaged
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
NVIDIA-NeMo/DataDesigner#1102 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
enforce \from future import annotations` via ruff FA102 rule`対応中かも @chethanuk が 58 日前に担当しました。 オープンtask
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
NVIDIA-NeMo/DataDesigner#760 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
NVIDIA-NeMo/DataDesigner#1140 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
NVIDIA-NeMo/DataDesigner の issue をすべて見る
似ている issue
-
community enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
NVIDIA/auto-ontology#463 ·
メンテナーはふだん 1 日以内に返信
-
Update Atari link on Gymnasium docs page対応中かも @qwist1233-cpu が今日担当しました。 オープンhelp wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
Farama-Foundation/Gymnasium#1845 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1〜3時間 初心者へのやさしさ 91/100
WingedGuardian/GENesis-AGI#4101 ·
メンテナーはふだん 1 日以内に返信