Skip to content

Commit 40a78ea

Browse files
7418claude
andcommitted
fix: image gen result persistence with fallback for temp message IDs, bump v0.17.3
Root cause: assistant messages use temporary IDs (temp-assistant-{timestamp}) in the frontend, which don't match real DB message IDs. The PUT update by message_id always found 0 rows, so results were never persisted. Fix — fallback search by session + prompt: - db.ts: updateMessageContent now returns changes count; new updateMessageBySessionAndHint() finds the latest assistant message containing image-gen-request with matching prompt text and updates it - /api/chat/messages PUT: tries message_id first, falls back to session_id + prompt_hint search; returns 404 if neither finds a match; response includes updated_message_id and fallback_used for debugging - ImageGenConfirmation: always sends session_id and prompt_hint in PUT request, regardless of whether messageId is a real or temp ID Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 20a5934 commit 40a78ea

File tree

5 files changed

+68
-13
lines changed

5 files changed

+68
-13
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codepilot",
3-
"version": "0.17.2",
3+
"version": "0.17.3",
44
"private": true,
55
"author": {
66
"name": "op7418",

src/app/api/chat/messages/route.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { NextRequest, NextResponse } from 'next/server';
2-
import { addMessage, updateMessageContent, getSession } from '@/lib/db';
2+
import { addMessage, updateMessageContent, updateMessageBySessionAndHint, getSession } from '@/lib/db';
33

44
export const runtime = 'nodejs';
55

@@ -42,24 +42,54 @@ export async function POST(request: NextRequest) {
4242
* PUT /api/chat/messages
4343
* Update the content of an existing message.
4444
* Used to replace image-gen-request with image-gen-result after generation.
45+
*
46+
* Tries message_id first. If no rows updated (temp ID), falls back to
47+
* searching by session_id + prompt_hint for the real DB message.
4548
*/
4649
export async function PUT(request: NextRequest) {
4750
try {
4851
const body = await request.json();
49-
const { message_id, content } = body as {
52+
const { message_id, content, session_id, prompt_hint } = body as {
5053
message_id: string;
5154
content: string;
55+
session_id?: string;
56+
prompt_hint?: string;
5257
};
5358

54-
if (!message_id || !content) {
59+
if (!content) {
5560
return NextResponse.json(
56-
{ error: 'message_id and content are required' },
61+
{ error: 'content is required' },
5762
{ status: 400 },
5863
);
5964
}
6065

61-
updateMessageContent(message_id, content);
62-
return NextResponse.json({ ok: true });
66+
// Try direct update by message_id
67+
let changes = 0;
68+
let updatedMessageId = message_id;
69+
let fallbackUsed = false;
70+
71+
if (message_id) {
72+
changes = updateMessageContent(message_id, content);
73+
}
74+
75+
// Fallback: search by session + prompt hint
76+
if (changes === 0 && session_id && prompt_hint) {
77+
const result = updateMessageBySessionAndHint(session_id, prompt_hint, content);
78+
changes = result.changes;
79+
if (result.messageId) {
80+
updatedMessageId = result.messageId;
81+
fallbackUsed = true;
82+
}
83+
}
84+
85+
if (changes === 0) {
86+
return NextResponse.json(
87+
{ error: 'Message not found' },
88+
{ status: 404 },
89+
);
90+
}
91+
92+
return NextResponse.json({ ok: true, updated_message_id: updatedMessageId, fallback_used: fallbackUsed });
6393
} catch (error) {
6494
const msg = error instanceof Error ? error.message : 'Failed to update message';
6595
return NextResponse.json({ error: msg }, { status: 500 });

src/components/chat/ImageGenConfirmation.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export function ImageGenConfirmation({
143143
}
144144

145145
// Persist result to DB by replacing image-gen-request with image-gen-result
146-
if (messageId) {
146+
{
147147
const resultBlock = JSON.stringify({
148148
status: 'completed',
149149
prompt,
@@ -158,8 +158,10 @@ export function ImageGenConfirmation({
158158
method: 'PUT',
159159
headers: { 'Content-Type': 'application/json' },
160160
body: JSON.stringify({
161-
message_id: messageId,
161+
message_id: messageId || '',
162162
content: '```image-gen-result\n' + resultBlock + '\n```',
163+
session_id: sessionId,
164+
prompt_hint: initialPrompt,
163165
}),
164166
}).catch(() => {});
165167
}

src/lib/db.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -565,9 +565,32 @@ export function addMessage(
565565
return db.prepare('SELECT * FROM messages WHERE id = ?').get(id) as Message;
566566
}
567567

568-
export function updateMessageContent(messageId: string, content: string): void {
568+
export function updateMessageContent(messageId: string, content: string): number {
569569
const db = getDb();
570-
db.prepare('UPDATE messages SET content = ? WHERE id = ?').run(content, messageId);
570+
const result = db.prepare('UPDATE messages SET content = ? WHERE id = ?').run(content, messageId);
571+
return result.changes;
572+
}
573+
574+
/**
575+
* Find the most recent assistant message in a session that contains a given text snippet,
576+
* update its content, and return the real message ID. Used as fallback when the frontend
577+
* only has a temporary message ID.
578+
*/
579+
export function updateMessageBySessionAndHint(
580+
sessionId: string,
581+
promptHint: string,
582+
content: string,
583+
): { changes: number; messageId?: string } {
584+
const db = getDb();
585+
// Find the latest assistant message containing the prompt hint within an image-gen-request block
586+
const row = db.prepare(
587+
"SELECT id FROM messages WHERE session_id = ? AND role = 'assistant' AND content LIKE '%image-gen-request%' AND content LIKE ? ORDER BY created_at DESC LIMIT 1"
588+
).get(sessionId, `%${promptHint.slice(0, 60)}%`) as { id: string } | undefined;
589+
590+
if (!row) return { changes: 0 };
591+
592+
const result = db.prepare('UPDATE messages SET content = ? WHERE id = ?').run(content, row.id);
593+
return { changes: result.changes, messageId: row.id };
571594
}
572595

573596
export function clearSessionMessages(sessionId: string): void {

0 commit comments

Comments
 (0)