Young Devs Bin
๐Ÿ”ง Dev Log

Fixing Gemini JSON Parsing, Token Limits, and Calendar Font Scale in Tagmind

ยท3 min readยท#android#kotlin#compose#llm#gemini#tagmind#ux

What Was Built

A focused polish pass fixing a persistent Gemini response bug, improving resilience for all LLM providers, and fixing a UI regression at maximum system font size.


The Gemini JSON Bug (Root Cause Found)

For several sessions, Gemini was displaying {"answer":"Based on your notes... as the literal answer text instead of the parsed value. Multiple attempts to fix Gson deserialization didn't resolve it.

The actual root cause was maxOutputTokens: 512. Gemini's structured JSON response was getting cut off before the closing }, producing malformed JSON. JSONObject threw, lastIndexOf('}') returned -1, and the raw string fell through to the answer field.

Fixes applied:

  1. Raised token limits โ€” All providers now use 4096 output tokens. Gemini omits maxOutputTokens entirely for search calls, using the model's default (8192).

  2. Replaced Gson with JSONObject โ€” Android's built-in JSONObject(json).optString("answer", "") has no generic type-erasure issues and handles malformed input gracefully. Removed the LlmJsonResponse data class entirely.

  3. Truncation fallback โ€” Even if the JSON is malformed (no closing }), a string walk extracts whatever answer text was generated before the cutoff:

    val answerKey  = cleaned.indexOf("\"answer\"")
    val colonIdx   = cleaned.indexOf(':', answerKey + 8)
    val quoteOpen  = cleaned.indexOf('"', colonIdx + 1)
    val quoteClose = cleaned.indexOf('"', quoteOpen + 1)
    val answer = if (quoteClose == -1) cleaned.substring(quoteOpen + 1).trim()
                 else cleaned.substring(quoteOpen + 1, quoteClose).trim()

Token Limit Warnings

Added a yellow warning box to both Settings โ†’ LLM and the onboarding tutorial (Ask AI page):

"AI responses are limited to 4,096 tokens. Very long responses may be cut off mid-sentence."

The right place for this information is at the moment the user is configuring the feature โ€” not in a README.


Calendar Heatmap: Large Font Fix

At maximum system font size, the month labels in the Note Activity heatmap (labelMedium โ€” sp-based) grew wider than the 13dp cell boxes, making each column wider and overflowing the card.

Fix: Wrap month labels in CompositionLocalProvider with fontScale = 1f:

val d = LocalDensity.current
CompositionLocalProvider(
    LocalDensity provides Density(density = d.density, fontScale = 1f)
) {
    Text(monthName, style = MaterialTheme.typography.labelMedium, ...)
}

The labels are decorative (heatmap axis ticks), so locking them to font scale 1.0 is appropriate. The rest of the screen still respects the user's accessibility setting.


Files Changed

FileChange
LlmRepository.ktReplaced Gson parsing with JSONObject, removed LlmJsonResponse, raised maxTokens to 4096 (Gemini: unlimited), added truncation fallback
SettingsLlmScreen.ktAdded yellow token-limit warning box
OnboardingScreen.ktAdded same warning on Ask AI tutorial page
CalendarScreen.ktWrapped month labels in fontScale = 1f provider