[Bugfix][Frontend] Normalize constrained Harmony recipients#45657
Conversation
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Once the PR is approved and ready to go, your PR reviewer(s) can run CI to test the changes comprehensively before merging. To run CI, PR reviewers can either: Add If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
yzong-rh
left a comment
There was a problem hiding this comment.
Minor comments. Thanks for the work.
| if recipient and recipient.startswith("<|constrain|>"): | ||
| return None |
There was a problem hiding this comment.
Should we discard anything that starts with <|constrain|>? or should be prune the recipient that may contain <|constrain|> ...?
Not sure if a case like to=function.foo <|constrain|> ... is buggy and possible. If it is, stripping away the ending <|constrain|> ... might work better.
| reasoning_token_count=reasoning_token_count, | ||
| ) | ||
|
|
||
| def _normalize_recipient(self, recipient: str | None) -> str | None: |
There was a problem hiding this comment.
Doesn't need the self here. A static method might be better.
| channel=channel, | ||
| recipient=recipient, | ||
| delta=delta, | ||
| completed_message=completed_message, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| (s.channel, s.recipient, s.delta) for s in result.segments if s.delta | ||
| ] == [("final", None, "Hello")] | ||
|
|
||
| def test_constrained_output_segment_recipient_normalized(self, harmony_parser): |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
@yzong-rh As a general note, if we were to swap over from openai-harmony to oss-harmony, these tokens would stop leaking into the vLLM layer at all (at least not in this way). Just an FYI, and not a reason to not fix this in this layer as well. |
36ae696 to
5ef0cd4
Compare
|
Hi @yzong-rh , thanks again for your previous guidance! I’ve addressed all the requested changes in the latest push. Please feel free to let me know if there are any further suggestions or concerns. |
|
Hi @yzong-rh , Sorry for missing this detail earlier. I looked into the recipient pruning question more closely. I tested these representative parser-level cases against openai_harmony 0.0.4 and 0.0.8:
So I did not find a real parsed case where Harmony returns something like However, I agree that pruning at the first I also normalized |
47c48a7 to
63e9762
Compare
yzong-rh
left a comment
There was a problem hiding this comment.
Sorry for the delay. This LGTM, thanks
cc maintainer @bbrowning
Hi @bbrowning, could you please take a look when you have a chance? This PR has been approved by @yzong-rh. Please let me know if there’s anything else I should update before it can move forward. Thanks! cc @sfeng33 |
|
This pull request has merge conflicts that must be resolved before it can be |
Normalize constrained content markers that older openai-harmony versions misparse as recipients at the HarmonyParser boundary. Add coverage for non-streaming parsing, streaming parser state, and emitted segments. Assisted-by: OpenAI Codex Signed-off-by: shaojunjie <626650687@qq.com>
63e9762 to
e27a5d2
Compare
| constrain_index = recipient.find("<|constrain|>") | ||
| if constrain_index == -1: | ||
| return recipient | ||
| return recipient[:constrain_index].rstrip() or None |
There was a problem hiding this comment.
Does this work in the real world? Here's a prior example I happened to have written down of how the constrain often leaks into the wrong place:
<|channel|>analysis<|message|>User asks: "What's the weather like in Paris today?" We need to get weather info. Use get_weather function. We need coordinates for Paris. Latitude 48.8566, longitude 2.3522.<|end|><|start|>assistant<|channel|>commentary <|constrain|>functions.get_weather<|message|>{"latitude":48.8566,"longitude":2.3522}<|call|>
I'm worried about the find and rstrip here in cases like this. It would potentially catch some cases, but I'm unsure how much it will hold up in the typical long context real-world failure scenarios we frequently see with these models. Do we have evidence this helps those broadly?
There was a problem hiding this comment.
Does this work in the real world? Here's a prior example I happened to have written down of how the constrain often leaks into the wrong place:
<|channel|>analysis<|message|>User asks: "What's the weather like in Paris today?" We need to get weather info. Use get_weather function. We need coordinates for Paris. Latitude 48.8566, longitude 2.3522.<|end|><|start|>assistant<|channel|>commentary <|constrain|>functions.get_weather<|message|>{"latitude":48.8566,"longitude":2.3522}<|call|>I'm worried about the find and rstrip here in cases like this. It would potentially catch some cases, but I'm unsure how much it will hold up in the typical long context real-world failure scenarios we frequently see with these models. Do we have evidence this helps those broadly?
Hi @bbrowning ,
Thanks for confirming, this works for the specific case addressed here. This PR is only a narrow fix for #45570.
I’ll continue looking into the broader <|constrain|> leakage issue and follow up with a more robust solution in a separate PR.
bbrowning
left a comment
There was a problem hiding this comment.
I'm approving this as it does help the cases where constrains leak into the final channel as recipients, which is what #45570 was about.
I noted an example where this does not solve the more generic problem of constrain tokens leaking into recipients for tool calling scenarios, but realize that was out of scope for the original intent of this PR so am ok with the narrower fix for the misidentified as MCP issue.
Hi @bbrowning, Thank you for the approval and clarification. The current CI failures appear unrelated to the changes in this PR. Would it be reasonable to proceed with the merge if you agree, or would you prefer another CI rerun? |
|
ci error fix: #47126 |
…ject#45657) Signed-off-by: shaojunjie <626650687@qq.com> Co-authored-by: Ben Browning <bbrownin@redhat.com>
…ject#45657) Signed-off-by: shaojunjie <626650687@qq.com> Co-authored-by: Ben Browning <bbrownin@redhat.com>
A constrained final message (<|channel|>final<|constrain|>json<|message|>) can be parsed by openai-harmony with recipient="<|constrain|>json", which the Responses converter routed to an mcp_call - leaking the control token into name/server_label and putting the JSON answer in arguments instead of output_text. Strip the <|constrain|> marker from the recipient before dispatch so the message is returned as normal output. Backport of upstream vllm-project#45657 (issue vllm-project#45570), adapted to the old-layout responses/harmony.py converter.
…ject#45657) Signed-off-by: shaojunjie <626650687@qq.com> Co-authored-by: Ben Browning <bbrownin@redhat.com>
…ject#45657) Signed-off-by: shaojunjie <626650687@qq.com> Co-authored-by: Ben Browning <bbrownin@redhat.com> Signed-off-by: wang.yuqi <yuqi.wang@daocloud.io>
…ject#45657) Signed-off-by: shaojunjie <626650687@qq.com> Co-authored-by: Ben Browning <bbrownin@redhat.com>
…ject#45657) Signed-off-by: shaojunjie <626650687@qq.com> Co-authored-by: Ben Browning <bbrownin@redhat.com>
Summary
Normalize standalone
<|constrain|>...markers that olderopenai-harmonyversions misparse as recipients at the sharedHarmonyParserboundary.This keeps constrained final output classified as regular content while exposing
recipient=NonethroughHarmonyParser.current_recipientandSegment.recipient. It covers non-streaming parsing, streaming parser state, and emitted segments.Related to #45570. This is the separate
HarmonyParserfollow-up requested in #45570 (comment).Duplicate check
45570 in:bodyand Harmony parser/constrained-recipient keywords only found [Bugfix][Frontend] Handle constrained Harmony output without recipient #45571.HarmonyParser, covering Chat Completions and the future unified Responses path described by the maintainer.Tests
Run remotely on Python 3.12.8 against vLLM
mainat64833f8158236a7bddeeb89efc6a3bde5d16f468:Also run on the changed files:
AI assistance disclosure: OpenAI Codex assisted with investigation, implementation, test execution, and drafting this PR. I reviewed the changed lines and validated the behavior and tests.