Repository navigation
Conversation
…nger take a picture's place (#150 follow-up)
The markers are special tokens only where the template writes them for an image item. The same strings in a
message's text (an agent reading chat_template.jinja, a tool result quoting it) became the same special ids, so text
with <|vision_start|><|image_pad|> before a picture took that picture's embeddings, every later picture moved up one
and the last real marker became text - every count still matched.
On 0.1.39 this takes #537's path: the vision markers join <think> / </think> as literal tags (frontend.VISION_TAGS),
swapped for private-use marks before the template and encoded as text through the tokenizer's plain spans. A pair
of markers cut apart across text parts is refused ("the prompt and its images do not match") instead of shifting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
424dd32 to
1273345
Compare
|
Rebased onto 0.1.39 as a new implementation (1273345). 0.1.39's #537 already solves the same problem for
The test ( |
|
Reopened as #1045, the same branch and commit. GitHub closed this one when my fork was made private by mistake, which took it out of the fork network. |
…tart|>, <|im_end|>, <|endoftext|>) (#931) Port of PR #931 onto the #554 vision-tag marks: literal_tags() adds the tokenizer's control tokens to LITERAL_TAGS, longest first; a prompt with none of them is rendered and encoded exactly as before. Co-authored-by: Chanyeong Lim <dlacksdud2@snu.ac.kr> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merged for 0.1.40: the vision markers inside a message's text stay text and no longer take a picture's place. |
…tart|>, <|im_end|>, <|endoftext|>) (Niko1221#931) Port of PR Niko1221#931 onto the Niko1221#554 vision-tag marks: literal_tags() adds the tokenizer's control tokens to LITERAL_TAGS, longest first; a prompt with none of them is rendered and encoded exactly as before. Co-authored-by: Chanyeong Lim <dlacksdud2@snu.ac.kr>
…tart|>, <|im_end|>, <|endoftext|>) (Niko1221#931) Port of PR Niko1221#931 onto the Niko1221#554 vision-tag marks: literal_tags() adds the tokenizer's control tokens to LITERAL_TAGS, longest first; a prompt with none of them is rendered and encoded exactly as before. Co-authored-by: Chanyeong Lim <dlacksdud2@snu.ac.kr>
…#554, on 0.1.39's Niko1221#537 literal marks) The markers are special tokens only where the template writes them for an image item; the same strings in a message's text took a picture's embeddings. 0.1.39 encodes a literal <think> / </think> as text with private-use marks and the tokenizer's plain spans (Niko1221#537); the vision markers now take the same path (frontend.VISION_TAGS), which replaces Niko1221#554's own placeholder tokenizing (prompt_ids, hide_markers). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
Service.prepare()renders the chat template and tokenizes the whole prompt withparse_special=True. The template writes<|vision_start|><|image_pad|><|vision_end|>for each image item, and the #150 guard turns only an<|image_pad|>not preceded by<|vision_start|>back into text.Message text that contains the whole marker pair becomes the same special ids as a real image. That happens with an agent reading
serve/chat_template.jinja, these docs, or a tool result quoting them. If such text comes before a real picture:The counts still match, so nothing reports it. With Claude Code reading a repository and a screenshot in one conversation, this is an ordinary case.
Fix (
serve/server.py,Service.prompt_ids()):parse_special=False) tokens. The pieces between placeholders are tokenized as before, since a special token split the text there anyway.count_tokensuses the same ids.Test
test_the_whole_marker_in_text_before_images: each picture's rows follow its own marker, and the text keeps its literal tokens. Without the fix it finds 3vision_startwhere 2 belong. The #150 test is unchanged. Serve tests: 173 OK (7 skipped).This touches the first lines of
prepare(), as #529 does; whichever goes in second needs a one-line rebase.🤖 Generated with Claude Code
https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio