Skip to content

Commit 86420a4

Browse files
DayuanJiangdayuan.jiang
andauthored
fix: implement client-side caching for example diagrams (#150)
- Add client-side cache check in onFormSubmit to bypass API calls for example prompts - Use findCachedResponse to match input against cached examples - Directly set messages with cached tool response when example matches - Hide regenerate button for cached example responses (toolCallId starts with 'cached-') - Prevents unnecessary API calls when using example buttons Co-authored-by: dayuan.jiang <[email protected]>
1 parent 0baf21f commit 86420a4

File tree

3 files changed

+68
-2
lines changed

3 files changed

+68
-2
lines changed

app/api/chat/route.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,20 +195,44 @@ async function handleChatRequest(req: Request): Promise<Response> {
195195
const isFirstMessage = messages.length === 1
196196
const isEmptyDiagram = !xml || xml.trim() === "" || isMinimalDiagram(xml)
197197

198+
// DEBUG: Log cache check conditions
199+
console.log("[Cache DEBUG] messages.length:", messages.length)
200+
console.log("[Cache DEBUG] isFirstMessage:", isFirstMessage)
201+
console.log("[Cache DEBUG] xml length:", xml?.length || 0)
202+
console.log("[Cache DEBUG] xml preview:", xml?.substring(0, 200))
203+
console.log("[Cache DEBUG] isEmptyDiagram:", isEmptyDiagram)
204+
198205
if (isFirstMessage && isEmptyDiagram) {
199206
const lastMessage = messages[0]
200207
const textPart = lastMessage.parts?.find((p: any) => p.type === "text")
201208
const filePart = lastMessage.parts?.find((p: any) => p.type === "file")
202209

210+
console.log("[Cache DEBUG] textPart?.text:", textPart?.text)
211+
console.log("[Cache DEBUG] hasFilePart:", !!filePart)
212+
203213
const cached = findCachedResponse(textPart?.text || "", !!filePart)
204214

215+
console.log("[Cache DEBUG] cached found:", !!cached)
216+
205217
if (cached) {
206218
console.log(
207219
"[Cache] Returning cached response for:",
208220
textPart?.text,
209221
)
210222
return createCachedStreamResponse(cached.xml)
223+
} else {
224+
console.log("[Cache DEBUG] No cache match - checking why...")
225+
console.log(
226+
"[Cache DEBUG] Exact promptText:",
227+
JSON.stringify(textPart?.text),
228+
)
211229
}
230+
} else {
231+
console.log("[Cache DEBUG] Skipping cache check - conditions not met")
232+
if (!isFirstMessage)
233+
console.log("[Cache DEBUG] Reason: not first message")
234+
if (!isEmptyDiagram)
235+
console.log("[Cache DEBUG] Reason: diagram not empty")
212236
}
213237
// === CACHE CHECK END ===
214238

components/chat-message-display.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -676,9 +676,14 @@ export function ChatMessageDisplay({
676676
<Copy className="h-3.5 w-3.5" />
677677
)}
678678
</button>
679-
{/* Regenerate button - only on last assistant message */}
679+
{/* Regenerate button - only on last assistant message, not for cached examples */}
680680
{onRegenerate &&
681-
isLastAssistantMessage && (
681+
isLastAssistantMessage &&
682+
!message.parts?.some((p: any) =>
683+
p.toolCallId?.startsWith(
684+
"cached-",
685+
),
686+
) && (
682687
<button
683688
type="button"
684689
onClick={() =>

components/chat-panel.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const STORAGE_SESSION_ID_KEY = "next-ai-draw-io-session-id"
3232
const STORAGE_DIAGRAM_XML_KEY = "next-ai-draw-io-diagram-xml"
3333

3434
import { useDiagram } from "@/contexts/diagram-context"
35+
import { findCachedResponse } from "@/lib/cached-responses"
3536
import { formatXML } from "@/lib/utils"
3637
import { ChatMessageDisplay } from "./chat-message-display"
3738

@@ -450,6 +451,42 @@ Please retry with an adjusted search pattern or use display_diagram if retries a
450451
e.preventDefault()
451452
const isProcessing = status === "streaming" || status === "submitted"
452453
if (input.trim() && !isProcessing) {
454+
// Check if input matches a cached example (only when no messages yet)
455+
if (messages.length === 0) {
456+
const cached = findCachedResponse(
457+
input.trim(),
458+
files.length > 0,
459+
)
460+
if (cached) {
461+
// Add user message and fake assistant response to messages
462+
// The chat-message-display useEffect will handle displaying the diagram
463+
const toolCallId = `cached-${Date.now()}`
464+
setMessages([
465+
{
466+
id: `user-${Date.now()}`,
467+
role: "user" as const,
468+
parts: [{ type: "text" as const, text: input }],
469+
},
470+
{
471+
id: `assistant-${Date.now()}`,
472+
role: "assistant" as const,
473+
parts: [
474+
{
475+
type: "tool-display_diagram" as const,
476+
toolCallId,
477+
state: "output-available" as const,
478+
input: { xml: cached.xml },
479+
output: "Successfully displayed the diagram.",
480+
},
481+
],
482+
},
483+
] as any)
484+
setInput("")
485+
setFiles([])
486+
return
487+
}
488+
}
489+
453490
try {
454491
let chartXml = await onFetchChart()
455492
chartXml = formatXML(chartXml)

0 commit comments

Comments
 (0)