diff --git a/changelog/2026-05-19_02.md b/changelog/2026-05-19_02.md new file mode 100644 index 0000000..594f5b9 --- /dev/null +++ b/changelog/2026-05-19_02.md @@ -0,0 +1,116 @@ +--- +title: "Gemini joins the provider lineup — and brings the per-claim citation that Claude's dynamic-filter lost" +lede: "Perplexed now ships a fourth AI provider: Gemini, with Google Search grounding. Gemini's `groundingSupports[]` carries per-segment attribution (text span → source URL) that survives intact, where Claude's `web_search_20260209` dynamic-filter sandbox drops it on the floor. Same modal UX as Ask Claude, same Citations footer shape so cite-wide hex substitution stays provider-agnostic. Two Gemini-specific quirks surfaced when we dissected a real response with curl: chunk URLs are short-lived `vertexaisearch.cloud.google.com` redirects that rot in ~30 days (the plugin resolves them to durable source URLs before writing), and the spec's `searchEntryPoint.renderedContent` is 5KB of inline-styled HTML that Obsidian's Markdown renderer can't display (replaced with a Markdown list of the queries, each linked to google.com/search)." +date_work_started: 2026-05-19 +date_work_completed: 2026-05-19 +date_created: 2026-05-19 +date_modified: 2026-05-19 +publish: true +category: Changelog +tags: + - Gemini + - Google-Search-Grounding + - Provider-Lineup + - Citations + - GroundingSupports + - Grounding-Redirect-URLs + - Obsidian-Plugin +--- + +# Ask Gemini — fourth provider, first one with surviving per-claim citations + +## Why care? + +If you write research notes in Obsidian, you now have a fourth way to ground a draft in live web sources without leaving the editor: + +- **Ask Gemini** — runs Google Search behind the scenes and streams the answer into your note at the cursor, just like Ask Claude and Ask Perplexity. +- **Per-claim citations actually work.** Gemini's response carries a mapping from each cited sentence to the specific URL it came from, which means the `### Citations` section at the bottom of your note is not a flat URL dump — it's a quote-per-source list you can verify by reading. +- **You opt in per-question.** A toggle in the Ask Gemini modal turns Google Search grounding on or off without touching settings, so you can compare grounded vs. ungrounded answers on the same prompt. + +## What's new? + +Five settings, all behind one Settings → Perplexed → Gemini (Google) section: + +``` +Gemini (Google) +├── Gemini API key (paste from Google AI Studio) +├── Default Gemini model (2.5 pro / 2.5 flash / 2.0 flash) +├── Enable Google search grounding by default (on) +├── Include Google searches list in notes (on — Markdown bullets, not HTML chip) +└── Resolve citation urls (durable, slower) (on — fixes the 30-day URL rot) +``` + +- **`Ask Gemini` command** opens a modal matching the Ask Claude UX — full-width question textarea, model dropdown with taglines, three behavior toggles (grounding / suggestions chip / streaming), Cmd-Enter to submit. +- **`Check Gemini service status` command** reports whether the service initialized and whether an API key is configured. +- **Citations footer** mirrors Claude's exactly — `[N]: [Title](url). > cited_text` — so cite-wide's hex-substitution pass treats Gemini output the same as everything else. + +## How it works + +The Gemini response shape carries two layers of provenance: + +| Layer | Field | What it gives you | +|---|---|---| +| Page-level | `groundingChunks[]` | URL + title per page Gemini consulted | +| Segment-level | `groundingSupports[]` | Text span (`segment.text`) → indices into `groundingChunks[]` | + +The plugin walks both layers and merges them by URL: page-level entries come in as URL-and-title fallbacks; segment-level entries enrich them with the actual sentence Gemini grounded against. First quote per source wins, on the principle that the earliest segment is closest to the source's lede. + +Compare to the Claude path documented in `context-v/issues/Getting-Claude-to-Respond-With-Research.md`: + +> *"web_search_20260209's dynamic-filtering pass post-processes search results in a code-execution sandbox, and per-claim web_search_result_location citations don't survive that round-trip — text blocks come back with citations: null."* + +Gemini doesn't have that round-trip. `groundingSupports[]` ships in the final response unmodified, so the `### Citations` section you get is actually attached to the prose above it rather than guessed from string matches. + +### What the response actually contains (dissected with curl, 2026-05-19) + +Two things bit us once we ran a real `gemini-2.5-flash` request with `google_search` enabled: + +| Surface | Documented as | Actually | What the plugin does | +|---|---|---|---| +| `chunk.web.uri` | "source URL" | `vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIY…` — a Google-controlled redirect that expires ~30 days after the response | `resolveCitationUrls()` does a parallel `fetch(uri, { redirect: 'manual' })` per cited source (3s timeout each), reads the `Location` header, falls back to the redirect URL on failure | +| `chunk.web.title` | "title" | The source DOMAIN (`"nobelprize.org"`), not the page title | Rendered as the link text in `[domain.com](resolved-url)` — acceptable; a future pass could grab `