Skip to content

Granite switch decision 256 options - #30693

Merged
CISC merged 6 commits into
ggml-org:masterfrom
gabe-l-hart:GraniteSwitchDecisionFullOptions
Oct 17, 2026
Merged

CISC merged 6 commits into
ggml-org:masterfrom
gabe-l-hart:GraniteSwitchDecisionFullOptions

Conversation

@gabe-l-hart

@gabe-l-hart gabe-l-hart commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Overview

Small follow-up to #30676 to increase the number of supported options in the decision template.

Additional information

The model supports up to 256 unique choices. The challenge is that each choice needs to be represented by a single vocab index so that logits can map to probabilities, but there are not 256 obvious single-token markers that make a cohesive semantic set. Since the model is based on an LLM, the markers themselves should carry real semantic meaning (vs arbitrary tokens). To handle this on a model-by-model basis, this PR uses a search process to find combinations of the standard english capital letter set that map to single tokens in the model's vocab (eg A, AA, B, BB, etc). This is done at conversion time so it has no runtime impact and is static to the converted GGUF model.

Requirements

  • I have read and agree with the contributing guidelines Yes
  • AI usage disclosure: No (mostly). The original token search snippet was generated by Claude by a colleague, but I adapted it and verified the logic here by hand.

…ion markers

Each switch model needs to identify a collection of single-token option
markers that can be mapped to their respective logits for the output
probability distribution. The simple upper/lower/numbers set only yields 66
unique markers, but the model can support up to 255. The challenge is that
which combinations of characters result in single-token markers is model-
specific, so we do a discovery exercise at conversion time by looking
through the specific model's vocab for single-token sequences of 1+ plain
letter strings (eg `A`, `AA`, `BB`, etc).

Branch: GraniteSwitchDecisionFullOptions
AI-usage: none
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
Branch: GraniteSwitchDecisionFullOptions
AI-usage: none
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
@gabe-l-hart
gabe-l-hart requested review from a team and CISC as code owners October 16, 2026 19:44
@gabe-l-hart

Copy link
Copy Markdown
Collaborator Author

cc @ngxson for a small follow-up to increase the option set from 56 -> 256 for Granite Switch as a decision model.

@github-actions github-actions Bot added documentation Improvements or additions to documentation server conversion labels Oct 16, 2026
Branch: GraniteSwitchDecisionFullOptions
AI-usage: none
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
Comment thread conversion/granite.py Outdated
@gabe-l-hart

Copy link
Copy Markdown
Collaborator Author

Hmm, looks like this might not actually fix the benchmark failures. Drafting until we get to the bottom of it.

@gabe-l-hart
gabe-l-hart marked this pull request as draft October 16, 2026 20:11
@gabe-l-hart

Copy link
Copy Markdown
Collaborator Author

@CISC @ngxson It looks like there's a mirror of the option label logic between the conversion and server_decision_context::init (here). I think there are three ways to tackle this:

  1. Copy the new conversion logic over to determine the set dynamically from the tokenizer (would need to be kept in sync)
  2. Parse from the template string
  3. Add a new hparam to explicitly hold the list

I think (3) is probably the most extensible and avoids multiple sources of truth for this (similar to historical switch from hard-coded templates to embedded jinja2), but it would be slightly more invasive. Opinions? Mine is weakly held, so I'm happy to be minimally-invasive too.

@gabe-l-hart

Copy link
Copy Markdown
Collaborator Author

It looks like the challenge of (3) is having a decision-specific array hparam somewhat violates encapsulation. The other decision-specific hparams all get pulled into the llama_model.gguf_kv, but array-types get skipped, so without the actual llama_model_loader handy, we can't easily pull out array types from the GGUF without doing it somewhere in src (llama-hparams.cpp probably) which leaks the decision encapsulation into libllama which seems to have been deliberately avoided so far except for model-specific KVs.

Given that, I think it may make more sense to just piggyback on the existing hard-coding for the other dynamic-single-token architectures.

… source of truth

Branch: GraniteSwitchDecisionFullOptions
AI-usage: full (Claude + Sonnet 5.5)
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
Branch: GraniteSwitchDecisionFullOptions
AI-usage: full (Claude + Sonnet 5.5)
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
Branch: GraniteSwitchDecisionFullOptions
AI-usage: full (Claude + Sonnet 5.5)
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
@gabe-l-hart
gabe-l-hart force-pushed the GraniteSwitchDecisionFullOptions branch from c763e91 to c10c2b0 Compare October 16, 2026 21:28
@gabe-l-hart
gabe-l-hart marked this pull request as ready for review October 16, 2026 21:37
@gabe-l-hart

Copy link
Copy Markdown
Collaborator Author

Ok, I found that the LEV case was already more sophisticated than I realized, so that allows moving all the option determination code out of generation and leaving this all in the c++ side.

Ready for re-review @CISC (sorry for the churn)

@ngxson

ngxson commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

not 100% sure if it fits the use case, but we recently added decision.labels array that allows having labels in gguf instead of hard-coding in cpp

@gabe-l-hart

Copy link
Copy Markdown
Collaborator Author

not 100% sure if it fits the use case, but we recently added decision.labels array that allows having labels in gguf instead of hard-coding in cpp

Oh! That's exactly the route I was trying to go. I must have been a few commits behind

@gabe-l-hart

Copy link
Copy Markdown
Collaborator Author

Given that the LEV path is both well-established and easy to support, for now I'd say let's stick with this as written rather than trying to get the labels plumbed through conversion and in the GGUF.

@CISC
CISC merged commit 1268eda into ggml-org:master Oct 17, 2026
14 checks passed
@gabe-l-hart
gabe-l-hart deleted the GraniteSwitchDecisionFullOptions branch October 21, 2026 15:37
auRose94 pushed a commit to auRose94/llama.cpp that referenced this pull request Oct 28, 2026
* feat(convert): Attempt to come up with 256 single-token, semantic option markers

Each switch model needs to identify a collection of single-token option
markers that can be mapped to their respective logits for the output
probability distribution. The simple upper/lower/numbers set only yields 66
unique markers, but the model can support up to 255. The challenge is that
which combinations of characters result in single-token markers is model-
specific, so we do a discovery exercise at conversion time by looking
through the specific model's vocab for single-token sequences of 1+ plain
letter strings (eg `A`, `AA`, `BB`, etc).

Branch: GraniteSwitchDecisionFullOptions
AI-usage: none
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>

* docs: Note granite-switch with 256 option markers

Branch: GraniteSwitchDecisionFullOptions
AI-usage: none
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>

* style: Remove extra newline

Branch: GraniteSwitchDecisionFullOptions
AI-usage: none
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>

* feat(convert): Use the computed labels in the template so c++ side is source of truth

Branch: GraniteSwitchDecisionFullOptions
AI-usage: full (Claude + Sonnet 5.5)
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>

* feat: Use LEV case for GRANITE_SWITCH w/ computed labels

Branch: GraniteSwitchDecisionFullOptions
AI-usage: full (Claude + Sonnet 5.5)
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>

* docs: Consolidate g-s into 255 bucket

Branch: GraniteSwitchDecisionFullOptions
AI-usage: full (Claude + Sonnet 5.5)
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conversion documentation Improvements or additions to documentation server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants