feat(guided): add guided.py with structural_tag compilation - #4843
Open
lvhan028 wants to merge 3 commits into
Open
feat(guided): add guided.py with structural_tag compilation#4843lvhan028 wants to merge 3 commits into
lvhan028 wants to merge 3 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a dedicated guided-decoding compilation module for chat-completions, including structural_tag support across the PyTorch engine and a guarded/degrading path for Turbomind, while also continuing the chat_completions endpoint refactor into a package with split protocol/validation/logprobs utilities.
Changes:
- Introduce
lmdeploy/serve/openai/endpoints/chat_completions/guided.pyas the shared xgrammar compilation surface forstructural_tagandchoice. - Add
structural_tagcompilation branches to PyTorch (GuidedDecodingManager) and Turbomind (guarded byhasattr(..., compile_structural_tag)). - Add compile-time unit tests for
structural_tagand migration-equivalence tests for the chat_completions package split.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/test_guided_structural_tag.py | Adds compile-time tests for structural_tag and choice grammar construction/compilation. |
| tests/test_chat_completions_package_migration.py | Adds structural “package migration” invariants for the chat_completions refactor. |
| lmdeploy/turbomind/turbomind.py | Adds structural_tag response_format handling with explicit unsupported-path ValueError. |
| lmdeploy/serve/openai/protocol.py | Removes inlined chat-completions models and re-exports them from the new package module. |
| lmdeploy/serve/openai/endpoints/chat_completions/validation.py | Extracts request validation logic into a dedicated module. |
| lmdeploy/serve/openai/endpoints/chat_completions/serving.py | Updates serving module to use the new split helpers (protocol/validation/logprobs/logits). |
| lmdeploy/serve/openai/endpoints/chat_completions/protocol.py | Introduces chat-completions-specific Pydantic models in the new package location. |
| lmdeploy/serve/openai/endpoints/chat_completions/logprobs.py | Extracts chat logprobs construction helpers. |
| lmdeploy/serve/openai/endpoints/chat_completions/logits_processors.py | Extracts chat logits processor(s), notably logit_bias. |
| lmdeploy/serve/openai/endpoints/chat_completions/guided.py | New shared xgrammar compilation helpers for structural_tag and choice. |
| lmdeploy/serve/openai/endpoints/chat_completions/init.py | Adds lazy register exposure to avoid import cycles after refactor. |
| lmdeploy/serve/openai/endpoints/init.py | Makes create_openai_router lazy to avoid circular imports with protocol re-exports. |
| lmdeploy/pytorch/engine/guided_process.py | Adds structural_tag support and refactors response_format compilation into helpers. |
Suppressed comments (1)
lmdeploy/serve/openai/endpoints/chat_completions/guided.py:101
- In the multi-tag branch, iterating payload['tags'] and indexing item['begin']/item['end'] can raise TypeError/KeyError/ValidationError, which would currently propagate past the engines'
except ValueErrorand potentially crash a request. Wrap this conversion so any such failures are normalized to ValueError.
for item in payload['tags']:
tag_formats.append({
'type': 'tag',
'begin': item['begin'],
'end': item['end'],
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+63
to
+66
| if not isinstance(payload, dict): | ||
| raise TypeError( | ||
| f'structural_tag payload must be a dict or xgr.StructuralTag, ' | ||
| f'got {type(payload).__name__}') |
lvhan028
force-pushed
the
feat/guided-structural-tag
branch
3 times, most recently
from
August 10, 2026 02:40
831c4c7 to
ca54bc9
Compare
Aligns with the responses/ package layout. Splits the 633-line chat_completions.py into protocol/validation/logprobs/logits_processors/ serving modules. Chat-specific models move to endpoints/chat_completions/protocol.py; shared models stay in the top-level protocol.py with backward-compat re-exports. No behavior change. Co-Authored-By: Claude <noreply@anthropic.com>
Reuses the existing grammar-agnostic matcher path (pytorch GuidedDecodingManager; turbomind set_grammar). No C++ kernel changes. Co-Authored-By: Claude <noreply@anthropic.com>
Rebuild compile_choice with xgr.Grammar.union of ConstStringFormat grammars so option strings are opaque literals (no EBNF escaping, no lexer crash on ", no alternation injection). Also wrap _to_xgr_structural_tag native/single/multi-tag branches to convert pydantic ValidationError into ValueError so the engines' except ValueError still catches malformed payloads. Co-Authored-By: Claude <noreply@anthropic.com>
lvhan028
force-pushed
the
feat/guided-structural-tag
branch
from
August 10, 2026 03:02
ca54bc9 to
4614a7c
Compare
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.
What
Add
guided.py— the single module that owns all xgrammar compilation of constrained-decoding response formats, plusstructural_tagsupport on both engines.guided.pyfunctions:_to_xgr_structural_tag(payload) -> xgr.StructuralTag,compile_structural_tag_payload(payload) -> xgr.Grammar,compile_choice(options) -> xgr.Grammar.compile_choicebuilds a const-string union viaConstStringFormat+Grammar.union(no EBNF escaping — avoids injection / crash from unescaped quotes).pytorch/turbomind) compilestructural_tagthroughguided.py. This is the infrastructure PR that later tool / structured-output PRs build on.Task
Task 7 of the chat-completions feature plan.
Files
lmdeploy/serve/openai/endpoints/chat_completions/guided.py— NEW (sole creator).lmdeploy/pytorch/engine/guided_process.py—structural_tagbranch in_extract_schema/_compile.lmdeploy/turbomind/turbomind.py—structural_tagcompile branch withhasattr(compiler, 'compile_structural_tag')guard.tests/test_guided_structural_tag.py— 10 tests.Tests
pytest tests/test_guided_structural_tag.py -v→ 10 passed.Dependency
Depends on #4840 (
refactor/chat-completions-package). Merge #4840 first, then rebase ontomain. This PR is itself a dependency for thetool-strict-requiredandstructured-outputsPRs — merge it before those.Notes — turbomind graceful degradation (known, accepted)
turbomind's bundled_xgrammarC++ binding (src/turbomind/python/xgrammar_bind.cpp) only exposescompile_json_schema/compile_regex— notcompile_structural_tag/compile_grammar. So on turbomind,structural_tag(andgrammar/choicein the downstream PR) hits ahasattrguard → raisesValueError→ caught by the existingtry/except→ logs a warning and disables guided decoding (no crash). pytorch is fully functional (uses pipxgrammar). This satisfies the plan's "an engine that cannot support a feature must degrade explicitly, never silently swallow" rule. Fix = future C++ binding extension (addcompile_structural_tag/compile_grammartoxgrammar_bind.cpp+ rebuild) — out of this PR's scope (no C++ changes).--no-verifylocally (env lacks python3.10 for the docformatter pre-commit hook); CI runs the hook with the correct interpreter.🤖 Generated with Claude Code