Skip to content

feat: implement thinking budget configuration for Gemma-4 think mode #343

Description

@lokhor

Problem

Gemma-4 E4B natively supports a "thinking" mode where it generates internal reasoning tokens (<think>...</think>) before producing its visible response. Our app already captures these tokens:

// LiteRtInferenceEngine.kt, lines 180-183
val thought = message.channels["thought"]
if (thought != null) {
    // Routes as GenerationResult.Thinking
}

However, we have zero control over this behavior:

  1. No thinking budget — we can't tell the model to use more or fewer thinking tokens. It uses whatever its default is.
  2. No on/off toggle — users can't disable thinking mode for simple queries (wastes tokens and adds latency) or enable it for complex reasoning tasks.
  3. No UI configurationModelSettingsEntity has no thinking-related fields. The settings screen has no thinking controls.
  4. Unknown API surface — we need to investigate what LiteRT-LM actually exposes for thinking budget configuration.

Impact

  • TTFT (Time to First Token): Thinking adds significant latency because the model generates reasoning tokens before the visible response. For simple queries like "turn on torch" or "what time is it", this is wasted time.
  • Token budget: Thinking tokens consume context window space. On a 4096-token window, even 200 thinking tokens is meaningful.
  • Quality: For complex queries (multi-step reasoning, math, planning), thinking dramatically improves output quality. We should enable users to leverage this.

Research Needed

LiteRT-LM Thinking Configuration API

Before implementation, investigate:

  1. Does LiteRT-LM support thinkingConfig / thinkingBudget?

    • Check the LiteRT-LM Kotlin SDK for configuration options when creating/initializing the engine
    • Look for ThinkingConfig, thinkingBudget, enableThinking, or similar parameters
    • Reference: https://github.com/AIDevelopersMonster/LiteRT-LM (check Kotlin API)
  2. How does Gemma-4 E4B handle thinking natively?

    • The model likely uses <think> tags in its tokenizer vocabulary
    • Can we suppress thinking by including "Do not think" in the system prompt? (crude but may work)
    • Is there a generation config parameter that controls thinking token budget?
  3. What does Edge Gallery do?

    • Check if AI Edge Gallery configures thinking budget in its LlmChatModelHelper.initialize()
    • Look for any thinkingConfig in their model initialization code

Implementation Plan

Phase A: Research + Basic Toggle

  1. Investigate LiteRT-LM API — document what's available for thinking configuration:

    • Check LlmModelConfig, SamplerConfig, or engine initialization params
    • File findings in a comment on this issue
  2. Add thinkingEnabled: Boolean to ModelSettingsEntity:

    @Entity(tableName = "model_settings")
    data class ModelSettingsEntity(
        @PrimaryKey val modelName: String,
        val contextWindowSize: Int = 4096,
        val temperature: Float = 0.7f,
        val topP: Float = 0.95f,
        val topK: Int = 40,
        val thinkingEnabled: Boolean = true  // NEW
    )
    • Room migration required
  3. Add thinking toggle to ModelSettingsScreen.kt:

    • Switch: "Enable thinking mode"
    • Description: "Model reasons internally before responding. Improves quality but adds latency."
    • Default: ON (for Gemma-4 models that support it)
  4. Apply toggle in LiteRtInferenceEngine:

    • If thinkingEnabled == false:
      • Option A (API): Pass thinkingBudget = 0 to LiteRT-LM (if supported)
      • Option B (prompt): Prepend "Do not use internal reasoning" to system prompt
      • Option C (filter): Suppress channels["thought"] output (model still thinks, we just ignore it — least useful)

Phase B: Budget Control (if API supports it)

  1. Add thinkingBudget: Int to ModelSettingsEntity (nullable, null = auto):

    val thinkingBudget: Int? = null  // null = model default, 0 = off, 100-2000 = custom
  2. Add thinking budget slider to settings UI:

    • Only visible when thinking toggle is ON
    • Range: 0-2000 tokens (step 50)
    • Special values: "Auto" (null/default), "Off" (0)
    • Label: "Thinking budget (tokens)"
  3. Pass budget to LiteRT-LM during inference:

    // Pseudocode — actual API TBD from research
    val thinkingConfig = ThinkingConfig(
        enabled = settings.thinkingEnabled,
        budget = settings.thinkingBudget ?: -1  // -1 = auto
    )
    engine.configure(thinkingConfig = thinkingConfig)

Phase C: Smart Defaults

  1. Auto-detect thinking capability — not all models support thinking:

    • If model doesn't emit channels["thought"] tokens, hide thinking settings
    • Show thinking settings only for compatible models (Gemma-4 E4B, future thinking models)
  2. Task-aware thinking (advanced, optional):

    • Disable thinking for simple tool calls (alarm, torch, timer)
    • Enable thinking for complex queries (math, reasoning, planning)
    • Implementation: lightweight classifier before inference, or just use low thinking budget for tool-call turns
    • Decision needed: worth the complexity? May defer.

UI Mockup

Model Settings
├── Context Window: [====|========] 4096
├── Temperature:    [===|=========] 0.7
├── Top-P:          [========|====] 0.95
├── Top-K:          [====|========] 40
├── ── Presets: [Precise] [Balanced] [Creative]
├── 
├── Thinking Mode
│   ├── [✓] Enable thinking mode
│   ├── Budget: [Auto ▼] / [=====|=====] 500 tokens
│   └── ℹ️ "Model reasons internally before responding"

Files to Modify

File Changes
ModelSettingsEntity.kt Add thinkingEnabled, thinkingBudget fields
ModelSettingsDao.kt Room migration
ModelSettingsScreen.kt Add thinking toggle + budget slider
ChatViewModel.kt Pass thinking config to engine
LiteRtInferenceEngine.kt Apply thinking configuration to LiteRT-LM
ModelConfig.kt Add thinking defaults

Success Criteria

  • LiteRT-LM thinking API documented (or workaround identified)
  • Thinking toggle works — OFF disables thinking tokens, ON enables them
  • Budget slider controls thinking depth (if API supports it)
  • TTFT measurably faster with thinking OFF on simple queries
  • Quality measurably better with thinking ON for complex reasoning
  • Settings hidden for models that don't support thinking

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions