feat(a2ui): use pexels to generate images#2684
Conversation
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughServer resolves A2UI Image component queries to concrete URLs (Pexels with deterministic Picsum fallback) and integrates resolution into validation/stream completion paths. Client-side readA2UIResponse gains a publishPartialMessages option to defer intermediate message-array publishing during SSE streaming; suggested prompts updated. ChangesImage URL Resolution and Streaming Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/genui/a2ui-playground/src/pages/AIChatPage.tsx`:
- Line 433: The quiz prompt string in AIChatPage.tsx contains a grammatical
error ("and photos" inserted into the answer list); update the prompt used where
the string is defined so the answer list reads cleanly (e.g., "Triangle, Square,
Circle, Hexagon") or, if you intended photos for an option, clarify like "Circle
(with photos)" so "and photos" is not breaking the list; locate and edit the
prompt text used in the Create a trivia quiz card string.
In `@packages/genui/server/agent/image-resolver.ts`:
- Line 26: The global imageCache (const imageCache = new Map<string,
Promise<string>>()) is unbounded and must be limited; replace it with a bounded
LRU-style cache (or use a small dependency like lru-cache) and apply the same
change to the other cache/location referenced around lines 107-113.
Specifically, create a capped cache that evicts oldest entries when max size is
reached (e.g., max entries 1000 or configurable), keep the same key/value type
(string -> Promise<string>), and update any code that accesses imageCache to use
the new cache API (get/set/has or .get/.set for lru-cache); ensure race
conditions are preserved by storing the Promise as before.
- Around line 126-128: The Pexels request (the line with "const res = await
fetch(url, { headers: { Authorization: apiKey } })") needs an AbortController
timeout so a hung upstream doesn't leave the cached Promise unresolved; create
an AbortController, start a setTimeout to call controller.abort() after a
sensible timeout, pass controller.signal into fetch, and in a finally block
clear the timeout; catch aborts/errors and return null on failure so the
existing picsum fallback runs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 40167e12-0bc6-42fa-b0e5-a8ac90aa2d44
📒 Files selected for processing (7)
packages/genui/a2ui-playground/src/pages/AIChatPage.tsxpackages/genui/server/AGENTS.mdpackages/genui/server/agent/a2ui-prompt.tspackages/genui/server/agent/image-resolver.tspackages/genui/server/app/a2ui/action/stream/route.tspackages/genui/server/app/a2ui/stream/route.tspackages/genui/server/service/a2ui-agent.ts
03c5e0c to
e914d71
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (2)
packages/genui/server/agent/image-resolver.ts (2)
26-26:⚠️ Potential issue | 🟠 Major | ⚡ Quick winBound the process-wide image cache.
imageCachegrows without eviction, so high query cardinality can cause unbounded memory growth over time.Proposed fix
const imageCache = new Map<string, Promise<string>>(); +const MAX_IMAGE_CACHE_ENTRIES = 1000; @@ if (!cached) { cached = resolvePexelsImage(query).then( (url) => url ?? picsumUrl(query), () => picsumUrl(query), ); + if (imageCache.size >= MAX_IMAGE_CACHE_ENTRIES) { + const oldestKey = imageCache.keys().next().value; + if (oldestKey) imageCache.delete(oldestKey); + } imageCache.set(cacheKey, cached); }Also applies to: 106-113
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/genui/server/agent/image-resolver.ts` at line 26, The global imageCache (const imageCache = new Map<string, Promise<string>>()) is unbounded and must be bounded to prevent memory growth; replace it with a size-limited cache (either swap to an LRU implementation like the lru-cache package or implement simple FIFO/LRU eviction) and ensure all accesses (the existing imageCache.get / imageCache.set / imageCache.has usages around the resolver functions) use the new bounded cache API or a wrapper helper (e.g., getCachedImage(key) / setCachedImage(key, promise)) that enforces eviction when cache.size > MAX_IMAGE_CACHE_SIZE; pick a sensible MAX_IMAGE_CACHE_SIZE constant, document it, and update any code paths that set entries (the sites currently writing into imageCache) to use the wrapper or new cache instance.
126-128:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winAdd a timeout to the Pexels fetch.
The outbound fetch has no timeout/signal. A hung upstream can block this path and delay fallback behavior.
Proposed fix
async function resolvePexelsImage(query: string): Promise<string | null> { @@ - const res = await fetch(url, { - headers: { Authorization: apiKey }, - }); - if (!res.ok) return null; - - const data = await res.json() as PexelsSearchResponse; - const src = data.photos?.[0]?.src; - return src?.large2x ?? src?.large ?? src?.medium ?? src?.original ?? null; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 5000); + try { + const res = await fetch(url, { + headers: { Authorization: apiKey }, + signal: controller.signal, + }); + if (!res.ok) return null; + const data = await res.json() as PexelsSearchResponse; + const src = data.photos?.[0]?.src; + return src?.large2x ?? src?.large ?? src?.medium ?? src?.original ?? null; + } catch { + return null; + } finally { + clearTimeout(timeout); + } }#!/bin/bash set -euo pipefail FILE="packages/genui/server/agent/image-resolver.ts" # Verify current fetch call has no signal/timeout control. nl -ba "$FILE" | sed -n '117,140p' rg -n "fetch\\(|AbortController|signal\\s*:" "$FILE"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/genui/server/agent/image-resolver.ts` around lines 126 - 128, The fetch that assigns const res currently has no timeout; wrap it with an AbortController: create a controller, start a setTimeout (e.g. 5000 ms) that calls controller.abort(), pass controller.signal into the fetch call (preserving headers including Authorization: apiKey), and clear the timeout once the fetch completes; also ensure the surrounding async flow catches the abort/DOMException and triggers the existing fallback path instead of hanging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@packages/genui/server/agent/image-resolver.ts`:
- Line 26: The global imageCache (const imageCache = new Map<string,
Promise<string>>()) is unbounded and must be bounded to prevent memory growth;
replace it with a size-limited cache (either swap to an LRU implementation like
the lru-cache package or implement simple FIFO/LRU eviction) and ensure all
accesses (the existing imageCache.get / imageCache.set / imageCache.has usages
around the resolver functions) use the new bounded cache API or a wrapper helper
(e.g., getCachedImage(key) / setCachedImage(key, promise)) that enforces
eviction when cache.size > MAX_IMAGE_CACHE_SIZE; pick a sensible
MAX_IMAGE_CACHE_SIZE constant, document it, and update any code paths that set
entries (the sites currently writing into imageCache) to use the wrapper or new
cache instance.
- Around line 126-128: The fetch that assigns const res currently has no
timeout; wrap it with an AbortController: create a controller, start a
setTimeout (e.g. 5000 ms) that calls controller.abort(), pass controller.signal
into the fetch call (preserving headers including Authorization: apiKey), and
clear the timeout once the fetch completes; also ensure the surrounding async
flow catches the abort/DOMException and triggers the existing fallback path
instead of hanging.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 28c2bc4e-6ddf-40d8-bd96-b0d4fac79a49
📒 Files selected for processing (7)
packages/genui/a2ui-playground/src/pages/AIChatPage.tsxpackages/genui/server/AGENTS.mdpackages/genui/server/agent/a2ui-prompt.tspackages/genui/server/agent/image-resolver.tspackages/genui/server/app/a2ui/action/stream/route.tspackages/genui/server/app/a2ui/stream/route.tspackages/genui/server/service/a2ui-agent.ts
✅ Files skipped from review due to trivial changes (1)
- packages/genui/server/AGENTS.md
e914d71 to
abda536
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will not alter performance
Comparing Footnotes
|
Web Explorer#10116 Bundle Size — 903.53KiB (0%).abda536(current) vs 5986b31 main#10113(baseline) Bundle metrics
Bundle size by type
|
| Current #10116 |
Baseline #10113 |
|
|---|---|---|
499.15KiB |
499.15KiB |
|
402.16KiB |
402.16KiB |
|
2.22KiB |
2.22KiB |
Bundle analysis report Branch Sherry-hue:feat/genui-server-ima... Project dashboard
Generated by RelativeCI Documentation Report issue
React MTF Example#1675 Bundle Size — 208.75KiB (0%).abda536(current) vs 5986b31 main#1672(baseline) Bundle metrics
|
| Current #1675 |
Baseline #1672 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
3 |
3 |
|
195 |
195 |
|
77 |
77 |
|
44.17% |
44.17% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #1675 |
Baseline #1672 |
|
|---|---|---|
111.23KiB |
111.23KiB |
|
97.52KiB |
97.52KiB |
Bundle analysis report Branch Sherry-hue:feat/genui-server-ima... Project dashboard
Generated by RelativeCI Documentation Report issue
React Example with Element Template#811 Bundle Size — 202.16KiB (0%).abda536(current) vs 5986b31 main#808(baseline) Bundle metrics
|
| Current #811 |
Baseline #808 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
4 |
4 |
|
100 |
100 |
|
30 |
30 |
|
39.22% |
39.22% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #811 |
Baseline #808 |
|
|---|---|---|
145.76KiB |
145.76KiB |
|
56.41KiB |
56.41KiB |
Bundle analysis report Branch Sherry-hue:feat/genui-server-ima... Project dashboard
Generated by RelativeCI Documentation Report issue
React External#1657 Bundle Size — 698.01KiB (0%).abda536(current) vs 5986b31 main#1654(baseline) Bundle metrics
|
| Current #1657 |
Baseline #1654 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
3 |
3 |
|
17 |
17 |
|
5 |
5 |
|
8.59% |
8.59% |
|
0 |
0 |
|
0 |
0 |
Bundle analysis report Branch Sherry-hue:feat/genui-server-ima... Project dashboard
Generated by RelativeCI Documentation Report issue
React Example#8542 Bundle Size — 237.81KiB (0%).abda536(current) vs 5986b31 main#8539(baseline) Bundle metrics
|
| Current #8542 |
Baseline #8539 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
4 |
4 |
|
200 |
200 |
|
80 |
80 |
|
44.68% |
44.68% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #8542 |
Baseline #8539 |
|
|---|---|---|
145.76KiB |
145.76KiB |
|
92.05KiB |
92.05KiB |
Bundle analysis report Branch Sherry-hue:feat/genui-server-ima... Project dashboard
Generated by RelativeCI Documentation Report issue
Summary by CodeRabbit
New Features
New Behavior
Updates
Documentation
Checklist