diff --git a/src/encoding.rs b/src/encoding.rs index 60257e7..d255d06 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -838,17 +838,30 @@ impl Render for HarmonyEncoding { } }; - // next render the header recipient, if there is one - if let Some(recipient) = &message.recipient { - if recipient != "all" { - self.render_text_into(format!(" to={recipient}"), into)?; + // Header channel vs recipient order: commentary uses <|channel|>… before to=…; + // other channels keep to=… before <|channel|>… (legacy order). + let commentary_channel_first = message.channel.as_deref() == Some("commentary"); + + if commentary_channel_first { + if let Some(channel) = &message.channel { + self.render_formatting_token_into(FormattingToken::Channel, into)?; + self.render_text_into(channel, into)?; + } + if let Some(recipient) = &message.recipient { + if recipient != "all" { + self.render_text_into(format!(" to={recipient}"), into)?; + } + } + } else { + if let Some(recipient) = &message.recipient { + if recipient != "all" { + self.render_text_into(format!(" to={recipient}"), into)?; + } + } + if let Some(channel) = &message.channel { + self.render_formatting_token_into(FormattingToken::Channel, into)?; + self.render_text_into(channel, into)?; } - } - - // next header channel - if let Some(channel) = &message.channel { - self.render_formatting_token_into(FormattingToken::Channel, into)?; - self.render_text_into(channel, into)?; } // finally content type @@ -865,7 +878,9 @@ impl Render for HarmonyEncoding { self.render_text_into(rest, into)?; } } else { - self.render_text_into(format!(" {content_type}"), into)?; + self.render_text_into(" ", into)?; + self.render_formatting_token_into(FormattingToken::ConstrainedFormat, into)?; + self.render_text_into(content_type, into)?; } } else { self.render_text_into(format!(" {content_type}"), into)?; diff --git a/test-data/test_does_not_drop_if_ongoing_analysis.txt b/test-data/test_does_not_drop_if_ongoing_analysis.txt index 29f8519..b837442 100644 --- a/test-data/test_does_not_drop_if_ongoing_analysis.txt +++ b/test-data/test_does_not_drop_if_ongoing_analysis.txt @@ -1 +1 @@ -<|start|>user<|message|>What is the weather in SF?<|end|><|start|>assistant<|channel|>analysis<|message|>User asks: “What is the weather in SF?” We need to use lookup_weather tool.<|end|><|start|>assistant to=functions.lookup_weather<|channel|>commentary <|constrain|>json<|message|>{"location": "San Francisco"}<|call|><|start|>functions.lookup_weather<|message|>{"temperature": 20, "description": "sunny"}<|end|><|start|>assistant \ No newline at end of file +<|start|>user<|message|>What is the weather in SF?<|end|><|start|>assistant<|channel|>analysis<|message|>User asks: “What is the weather in SF?” We need to use lookup_weather tool.<|end|><|start|>assistant<|channel|>commentary to=functions.lookup_weather <|constrain|>json<|message|>{"location": "San Francisco"}<|call|><|start|>functions.lookup_weather<|message|>{"temperature": 20, "description": "sunny"}<|end|><|start|>assistant diff --git a/test-data/test_tool_response_parsing.txt b/test-data/test_tool_response_parsing.txt index a1d4b87..a958572 100644 --- a/test-data/test_tool_response_parsing.txt +++ b/test-data/test_tool_response_parsing.txt @@ -1 +1 @@ -<|start|>browser.search to=assistant<|channel|>commentary<|message|>{"result": "https://openai.com/"} \ No newline at end of file +<|start|>browser.search<|channel|>commentary to=assistant<|message|>{"result": "https://openai.com/"} diff --git a/tests/test_harmony.py b/tests/test_harmony.py index dbb9925..688621a 100644 --- a/tests/test_harmony.py +++ b/tests/test_harmony.py @@ -245,7 +245,7 @@ def test_tool_call_with_constrain_tokenized_correctly(encoding_name): """ encoding = load_harmony_encoding(encoding_name) text = ( - "<|start|>assistant to=functions.get_weather<|channel|>commentary" + "<|start|>assistant<|channel|>commentary to=functions.get_weather" ' <|constrain|>json<|message|>{"location": "Tokyo"}<|call|>' ) tokens = encoding.encode(text, allowed_special="all")