Read Tool & Reasoning Tokens from GenAI API instead of Catalog Metadata#838
Draft
sayanshaw24 wants to merge 3 commits into
Draft
Read Tool & Reasoning Tokens from GenAI API instead of Catalog Metadata#838sayanshaw24 wants to merge 3 commits into
sayanshaw24 wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
skottmckay
reviewed
Jun 24, 2026
skottmckay
reviewed
Jun 24, 2026
…uildToolCallContext
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrate tool/reasoning metadata from catalog to GenAI + optimize decode loop
Summary
Two goals:
Catalog v2 migration: Tool-call and reasoning token metadata (
toolCallStart,toolCallEnd,reasoningStart,reasoningEnd) is being removed from the catalog. This PR sources it from GenAI'sgenai_config.jsoninstead, via the newOgaModel::GetTagId()API, and writes the decoded strings intoModelInfoso all existing downstream consumers continue working.Decode loop optimization: The current per-token detection of special tokens (double-decode +
string.find()) is replaced with a single integer comparison against cached token IDs — the same pattern used for EOS detection.Performance
Before (per token): 2 tokenizer stream decodes + 2
string.find()+ string comparisonAfter (per token): 1 integer comparison (fast path) or unchanged (slow path fallback)
The fast path also eliminates the special tokenizer stream entirely — when tag IDs are available, only the normal stream is maintained.
Storage (follows EOS pattern)
eos_token_ids_GenAIModelInstanceDecode()— filters EOS tokenstag_info_(IDs + strings)GenAIModelInstanceDecode()— fast-path detectionModelInfo.string_propertiesBuildToolCallContext()→ToolCallStreamAccumulatorChanges
GenAIModelInstance(genai_model_instance.h/.cc)TagInfostruct: caches both IDs (for the decode loop) and decoded strings (for text pipeline)GetTagInfo()— lazy-cached viastd::call_once(same pattern asGetEosTokenIds())OgaModel::GetTagId()for each tag (4 calls total, one-time)tokenizer_with_special_->Decode(&id, 1)to get the string (4 decodes, one-time)OnnxChatGenerator::Decode()(onnx_chat_generator.cc)string.find()behavior preservedModel::Load()(model.cc)ModelInfostring properties fromGetTagInfo()cached stringssupports_tool_calling/supports_reasoningfrom tag ID presence (>= 0)No changes to
ToolCallContext,ToolCallStreamAccumulator, orBuildToolCallContext()ModelInfoas before — single source of truthDependency
Requires updated
onnxruntime-genainuget withOgaModelGetTagIdAPI.deps_versions.jsonbump needed once GenAI nuget publishesHow it works
Before (per token, every single generated token):
Cost per token: 2 tokenizer stream decodes + 2
string.find()+ string comparison — on every token, even though markers appear maybe 2-4 times per generation.After (per token):
Cost per token: 1 integer comparison + 1 normal decode. No special stream. No string search.
Estimated performance gain
For a typical 500-token generation:
~50% reduction in tokenizer decode calls and complete elimination of string search operations in the hot loop. The special tokenizer stream is never even created/maintained for models with tag IDs configured.
Tag names supported by the GenAI API
"tool_call_start","tool_call_end"— tool call delimiters"reasoning_start","reasoning_end"— chain-of-thought reasoning delimitersTesting
Will be validated end-to-end once the GenAI nuget is available. The slow path (no tag IDs) is unchanged, so existing behavior is preserved for models without the new config fields.