Skip to content

fix(chart): gate the chart instruction + render varied chart-JSON shapes#27

Merged
sagar-develop merged 2 commits into
mainfrom
fix/chart-model-gate
Jun 5, 2026
Merged

fix(chart): gate the chart instruction + render varied chart-JSON shapes#27
sagar-develop merged 2 commits into
mainfrom
fix/chart-model-gate

Conversation

@sagar-develop

@sagar-develop sagar-develop commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Fixes both halves of the chart problem surfaced on-device: tiny models forcing charts into unrelated answers, and capable models' chart JSON failing to render because of shape differences. Branched off main, independent of the UI PR #22.

1 — Gate the chart instruction by model capability (566d5c3)

The chart instruction was appended to every chat's system prompt. Tiny models can't follow its "only when it genuinely helps" guardrail — observed with Qwen3 0.6B: "Help me draft an email" came back full of Sales Trends: [Chart data here] and a placeholder {"chart":{…}} block.

  • ModelDescriptor.supportsCharts (default false), mirroring supportsVision.
  • Opt in only ≥1.5B-class models (DeepSeek-R1 1.5B, Gemma 4 E2B/E4B, Phi-4 mini, Qwen3 4B) + the engine Gemma bundles. Qwen3 0.6B and Gemma 3 1B stay off.
  • System instruction built per active model.

2 — Render chart JSON that models wrap or emit without a type (c5fa222)

DeepSeek-R1 1.5B emits {"chart":{"name":"…","title":"…","data":[{label,value}]}} — wrapped, no top-level "type" — which ChartParser rejected → code-block fallback.

ChartParser now resolves tolerantly, strict type path first so Gemma is unchanged: accept chartType/kind synonyms; unwrap a single chart-wrapper key (type from the wrapper name → inner type → inferred from payload); bare untyped/unwrapped JSON still returns null (ordinary code stays code). New ChartParserTest cases (CI :lib).

On-device verification (release)

  • Qwen3 0.6B (tablet): chart-free — clean text breakdown, no spurious JSON.
  • DeepSeek-R1 1.5B (tablet): wrapped {"chart":{…}} now renders a donut.
  • Gemma 4 E2B (phone): still renders its donutno regression (unchanged {"type":"donut"} path).

sagar-develop and others added 2 commits June 5, 2026 16:16
Tiny models (e.g. Qwen3 0.6B) parrot the in-prompt chart examples into
unrelated answers instead of honoring the "only when it genuinely helps"
guardrail — e.g. "Help me draft an email" came back full of "Sales Trends:
[Chart data here]" and a placeholder {"chart":{...}} block (which then can't
render, so it falls back to a code block). The chart instruction was added
unconditionally to every chat's system prompt.

- Add ModelDescriptor.supportsCharts (default false), mirroring supportsVision.
- Opt in only the >=1.5B-class models: DeepSeek-R1 1.5B, Gemma 4 E2B/E4B,
  Phi-4 mini, Qwen3 4B (app catalog) and the Gemma bundles (engine sample
  catalog). Qwen3 0.6B and Gemma 3 1B stay off.
- Build the chat system instruction per active model: append
  ChartInstruction.SYSTEM only when the active model supportsCharts; otherwise
  use the plain base instruction.
- Test: chartsGatedToCapableModelsOnly.

Verified on-device (Redmi Pad SE, Qwen3 0.6B, release): "Help me draft an
email" now returns a clean, chart-free reply. Capable models (e.g. Gemma 4
E2B) keep charts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capable non-Gemma models (e.g. DeepSeek-R1 1.5B) emit chart-shaped JSON in a
shape ChartParser didn't accept — nested under a "chart" wrapper and with no
top-level "type" (e.g. {"chart":{"name":"…","title":"…","data":[{label,value}]}}).
That fell through to a code block, so the chart never rendered.

ChartParser now resolves the chart tolerantly while keeping the strict path
FIRST (so Gemma's {"type":"donut",…} is byte-for-byte unchanged):
- accept `chartType` / `kind` as `type` synonyms;
- unwrap a single chart-wrapper key (chart / graph / donut / pie / bar / line /
  progress / …), taking the type from the wrapper name when it names one,
  else the inner type, else inferring from the payload (labeled data → donut,
  series → line, value → progress);
- a bare untyped, unwrapped object still returns null (ordinary JSON stays a
  code block — conservative, content never lost).

Tests: wrapper-without-type → donut, type-from-wrapper-key, chartType synonym,
bare-untyped stays null.

Verified on-device (release): DeepSeek-R1 1.5B's wrapped JSON now renders a
donut on the tablet, and Gemma 4 E2B still renders its donut on the phone
(no regression).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sagar-develop sagar-develop changed the title fix(chart): gate the chart instruction to models that can follow it fix(chart): gate the chart instruction + render varied chart-JSON shapes Jun 5, 2026
@sagar-develop sagar-develop merged commit 3a4dc21 into main Jun 5, 2026
1 check passed
@sagar-develop sagar-develop deleted the fix/chart-model-gate branch June 5, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant