Skip to content

Commit d59a574

Browse files
committed
Fix guided generation: stop on grammar termination, include <|endoftext|> (#117)
Two fixes for the same bug (defense in depth): 1. When the grammar session terminates (isTerminated), return nil immediately instead of emitting the terminal token's decoded text. This prevents ANY special token from leaking into structured output, regardless of whether it's in the stop sequences list. 2. Add 'endoftext' to turnEndPatterns so <|endoftext|> (token 151643) is picked up from added_tokens_decoder as an additional stop token. Qwen3 declares eos_token as <|im_end|> (151645) but xgrammar can also produce <|endoftext|> as a valid grammar terminal.
1 parent 534b289 commit d59a574

2 files changed

Lines changed: 6 additions & 5 deletions

File tree

swift/Sources/CoreAILanguageModels/Bundle/LanguageConfig.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public struct LanguageConfig: Codable, Sendable, Equatable {
126126
// 3. Check added_tokens_decoder for turn-ending special tokens
127127
// (e.g. Gemma's <end_of_turn> ID 106, Qwen's <|im_end|>)
128128
// Only include tokens whose content matches known turn-ending patterns.
129-
let turnEndPatterns = ["end_of_turn", "im_end", "eot_id"]
129+
let turnEndPatterns = ["end_of_turn", "im_end", "eot_id", "endoftext"]
130130
if let addedTokens = json["added_tokens_decoder"] as? [String: Any] {
131131
for (idString, value) in addedTokens {
132132
guard let dict = value as? [String: Any],

swift/Sources/CoreAILanguageModels/DecodingStrategies/ConstrainedDecodingStrategy.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,17 +324,18 @@ extension ConstrainedDecodingStrategy.ConstrainedDecodedSequence {
324324
generatedTokens.append(bestToken)
325325
tokenStep += 1
326326

327+
if terminatedAfterAccept {
328+
finished = true
329+
return nil
330+
}
331+
327332
let delta = ConstrainedDecodingStrategy.computeTextDelta(
328333
generatedTokens: generatedTokens,
329334
previousDecodedText: &previousDecodedText,
330335
tokenizer: tokenizer,
331336
tokenStep: tokenStep
332337
)
333338

334-
if terminatedAfterAccept {
335-
finished = true
336-
}
337-
338339
return GenerationResult(text: delta, tokenId: bestToken, rawLogits: logits)
339340
}
340341

0 commit comments

Comments
 (0)