added heterogenous memory - #1
Closed
Tushar-ml wants to merge 18 commits into
Closed
Conversation
Tushar-ml
pushed a commit
that referenced
this pull request
Jun 7, 2026
…king=false tool calls aren't scrubbed
Histogram _sum extrapolation was causing input/output token counts to appear doubled. Added dynamo_frontend_input_tokens_total IntCounterVec (incremented once per request at first token) following the same pattern as dynamo_frontend_output_tokens_total from PR ai-dynamo#4202. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
relay metrics on gemma-4-hybrid
|
This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days. |
|
This PR has been closed due to inactivity. If you believe this PR is still relevant, please feel free to reopen it with additional context or information. |
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.
Gemma4 Parser Changes vs
ai-dynamo/dynamoReference: Aligned with Tushar-ml/vllm PR #5 snapshot-based parsing and prefix-diff content cleaning.
Compared:
feat/gemma-4-hybrid-memoery(fork) vsorigin/main(ai-dynamo/dynamo)Executive summary
Upstream Dynamo had a ~530-line incremental state machine in
gemma4_parser.rswith logic duplicated across reasoning, tool parsing, and streaming paths. This fork centralizes Gemma4 protocol handling in a newformat.rsmodule (Rust + Python mirror), rewrites the reasoning parser as a thin snapshot wrapper, adds prefix-diff streaming cleaners, and extends content sanitization forenable_thinking=falseand post-tool leaks.ai-dynamo/dynamo)format.rsdid not exist)lib/parsers/.../gemma4/format.rs+gemma4_format.pyextract_reasoning_non_streaming()overlap()strip_trailing_incomplete_token→ snapshot diffenable_thinking=falsemainStreamingContentCleanerin preprocessorStreamingContentCleanerinjail.rs<|"|>,<tool_call|>leaksstrip_leaked_tool_grammar()enable_thinking=false)<|channel>thought\n<channel|>1. New shared module:
lib/parsers/src/tool_calling/gemma4/format.rsUpstream: No equivalent file. Helpers were inlined in
gemma4_parser.rsand scattered inparser.rs.New file (~589 lines) exports:
CHANNEL_START,CHANNEL_END,THOUGHT_PREFIXReasoningSnapshot{ reasoning, content }snapshot for batch/streamextract_reasoning_non_streaming()diff_reasoning_streaming_snapshots()strip_trailing_incomplete_token()StreamingContentCleanerstrip_leaked_empty_thinking()strip_leaked_tool_grammar()<|"|>and<tool_call|>strip_thought_shard_echoes()thoughtshard removalstrip_tool_call_suffix()finalize_client_content()strip_leaked→strip_tool_call_suffixclean_visible_prefix()extract_tool_handoff_text()strip_trailing_incomplete_tokenoverlap()helper insidegemma4_parser.rsfor partial<|channel>/<channel|>only.<|channel>,<channel|>,<|tool_call>,<tool_call|>,<|"|>, etc.). Iterates1..tok.len()so complete tokens (e.g.<|"|>) are never stripped.extract_reasoning_non_streamingsemantics (intentional breaking changes)<|channel>content, no reasoning<channel|>without startsome thinking<channel|>answer→ reasoning=some thinking)normal<channel|>answerpasses through)<|channel>…<channel|>spansreasoning_textcontentreasoningonly,content=None(pre-channel text held)<|tool_call>before closecontentcontent=Noneuntil more text arrivesstrip_leaked_empty_thinkingextensionsUpstream: Only triggered when channel markers or
thoughtpresent; stripped channels and thought shards.Fork additions:
may_contain_gemma4_control_leak()— also triggers on<|"|>,<tool_call|>,<|tool_call>strip_leaked_tool_grammar()— removes orphan<|"|>and<tool_call|>(not<|tool_call>, preserved for suffix strip)<|"|>Great news!...→Great news!...thought<tool_call|>→ empty<tool_call|>→ emptyDynamo-only extras retained
thought\n<|channel>thought\n<channel|>,thought\n<channel|>strip_thought_shard_echoesfor streaming cut artifacts (thoughtthought,thought×11 +tho, etc.)2. Reasoning parser rewrite:
lib/parsers/src/reasoning/gemma4_parser.rsUpstream: ~530 lines, fields:
buffer,in_reasoning,prefix_resolved,reasoning_accum, customoverlap().Fork: ~380 lines, fields:
cumulative_text,prev_cumulative_text,last_safe_text,emitted_content.Batch (
detect_and_parse_reasoning)Stream (
parse_reasoning_streaming_incremental)Removed upstream behaviors
<channel|>recovery intoreasoning_contentoverlap()-based partial marker buffering inside reasoning parser (moved toformat.rs)Test expectation changes
detect_dangling_end_marker_extracts_prefix_as_reasoningsome thinkingdetect_multiple_reasoning_spans3. Tool parser:
lib/parsers/src/tool_calling/gemma4/parser.rsUpstream: Inline content cleaning; less centralized.
Fork changes:
clean_visible_prefix()/finalize_client_content()fromformat.rsfor pre-toolnormal_textclean_visible_prefix(message[..idx].trim())on prefix before first<|tool_call>(includesstrip_leaked_empty_thinking)Exports (
gemma4/mod.rs,tool_calling/mod.rs):StreamingContentCleaner,ReasoningSnapshot,extract_reasoning_non_streaming,diff_reasoning_streaming_snapshots,strip_trailing_incomplete_token,extract_tool_handoff_text4. LLM postprocessor:
lib/llm/src/preprocessor.rsUpstream (
main): Whenenable_thinking=false, reasoning parser is simply not run — no dedicated content sanitizer.Fork: New branch when
enable_thinking=false+ gemma4 parser configured:sanitize_gemma4_leaked_content_from_stream:previous_text+StreamingContentCleanerpre_tool_content_delta(previous, current, delta)New integration test:
lib/llm/tests/postprocessor_parsing_stream.rs— split-channel chunks:5. Tool jail streaming:
lib/llm/src/protocols/openai/chat_completions/jail.rsUpstream: Pre-tool visible segments emitted raw via
create_choice_stream(prefix, ...).Fork: For
tool_call_parser=gemma4/gemma-4:ChoiceJailStateaddsvisible_raw_cumulative+gemma4_content_cleanervisible_content_for_emission()→StreamingContentCleaner.pre_tool_content_delta()push_visible_emission()used for PassThrough and Trailing emissionsnormal_text)6. Python frontend:
components/src/dynamo/frontend/New:
gemma4_format.py(~315 lines)Mirror of Rust
format.rs:StreamingContentCleanerextract_reasoning_non_streamingstrip_leaked_empty_thinking+_strip_leaked_tool_grammarstrip_trailing_incomplete_token(set-comprehension suffix table)has_gemma4_tool_markup,strip_tool_call_suffixUpdated:
prepost.py_clean_visible_content(delta)_streaming_content_delta(previous, current, delta)when_gemma4_toolsor_thinking_disabledstrip_leaked+strip_tool_call_suffix_clean_visible_contentstrip_tool_call_suffixNew tests:
tests/test_gemma4_format.pyStreamingContentCleanerprefix-diffstrip_leaked_empty_thinkingchannel cases7. Parity fixtures
Updated under
tests/parity/reasoning/fixtures/gemma4/:REASONING.batch.yamlREASONING.batch.4— dangling end → content (not reasoning)REASONING.batch.5— open channel, no pre-channelnormal_textREASONING.batch.6.a— first span onlyREASONING.batch.3.b— open channel → tool handoffREASONING.stream.yamlREASONING.stream.2.b— first span only in streamREASONING.stream.3.b— end marker split across chunksMany new toolcalling/parser parity YAML files also added under
tests/parity/toolcalling/fixtures/gemma4/andtests/parity/parser/fixtures/gemma4/.8. Chat template
Planned change (vLLM PR #5 / HF
google/gemma-4-26b-a4b-it): Remove generation-prompt injection:Current branch vs
origin/main: No diff onexamples/chat_templates/gemma4_tool.jinja— injection still present in both. Removing it remains a recommended follow-up to stop the model echoing empty thought channels when thinking is disabled.9. Architecture
flowchart TD subgraph batch [Non-streaming] fullText[Full model text] extract[extract_reasoning_non_streaming] finalize[finalize_client_content] fullText --> extract extract -->|reasoning| reasoningOut[reasoning_content] extract -->|content| finalize --> contentOut[content] end subgraph stream [Streaming] delta[Per-chunk delta] accum[Accumulate cumulative_text] safe[strip_trailing_incomplete_token] snap[extract_reasoning_non_streaming snapshot] diff[diff_reasoning_streaming_snapshots] delta --> accum --> safe --> snap --> diff end subgraph sanitize [enable_thinking=false] cleaner[StreamingContentCleaner prefix-diff] strip[strip_leaked_empty_thinking + strip_leaked_tool_grammar] cleaner --> strip end10. Files touched (parser-related)
lib/parsers/src/tool_calling/gemma4/format.rslib/parsers/src/reasoning/gemma4_parser.rslib/parsers/src/tool_calling/gemma4/parser.rslib/parsers/src/tool_calling/gemma4/mod.rslib/parsers/src/tool_calling/mod.rslib/llm/src/preprocessor.rslib/llm/src/protocols/openai/chat_completions/jail.rslib/llm/tests/postprocessor_parsing_stream.rscomponents/src/dynamo/frontend/gemma4_format.pycomponents/src/dynamo/frontend/prepost.pycomponents/src/dynamo/frontend/tests/test_gemma4_format.pytests/parity/reasoning/fixtures/gemma4/*.yamltests/parity/toolcalling/fixtures/gemma4/*.yamlexamples/chat_templates/gemma4_tool.jinja11. Verification
12. Remaining gaps / follow-ups
enable_thinking=false(still in template vs upstream).strip_leaked_empty_thinking(leakage suite showed<|"|>inT4:post_eligibility/T2:post_tool).REASONING.batch.6.b— Dynamo-specific stray<channel|>stripping innormal_text(bodyanswervs vLLMbody<channel|>answer); intentional Dynamo divergence documented in fixtures.