feat: simplified document upload handling

- Introduced a new endpoint for batch document status retrieval, allowing users to check the status of multiple documents in a search space.
- Enhanced the document upload process to return duplicate document IDs and improved response structure.
- Updated schemas to include new response models for document status.
- Removed unused attachment processing code from chat routes and UI components to streamline functionality.
This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-02-09 16:46:54 -08:00
parent d11e76aaa1
commit c979609041
15 changed files with 475 additions and 1090 deletions

View file

@ -1,46 +1,9 @@
import type { ThreadMessageLike } from "@assistant-ui/react";
import { z } from "zod";
import type { MessageRecord } from "./thread-persistence";
/**
* Zod schema for persisted attachment info
*/
const PersistedAttachmentSchema = z.object({
id: z.string(),
name: z.string(),
type: z.string(),
contentType: z.string().optional(),
imageDataUrl: z.string().optional(),
extractedContent: z.string().optional(),
});
const AttachmentsPartSchema = z.object({
type: z.literal("attachments"),
items: z.array(PersistedAttachmentSchema),
});
type PersistedAttachment = z.infer<typeof PersistedAttachmentSchema>;
/**
* Extract persisted attachments from message content (type-safe with Zod)
*/
function extractPersistedAttachments(content: unknown): PersistedAttachment[] {
if (!Array.isArray(content)) return [];
for (const part of content) {
const result = AttachmentsPartSchema.safeParse(part);
if (result.success) {
return result.data.items;
}
}
return [];
}
/**
* Convert backend message to assistant-ui ThreadMessageLike format
* Filters out 'thinking-steps' part as it's handled separately via messageThinkingSteps
* Restores attachments for user messages from persisted data
*/
export function convertToThreadMessage(msg: MessageRecord): ThreadMessageLike {
let content: ThreadMessageLike["content"];
@ -52,7 +15,7 @@ export function convertToThreadMessage(msg: MessageRecord): ThreadMessageLike {
const filteredContent = msg.content.filter((part: unknown) => {
if (typeof part !== "object" || part === null || !("type" in part)) return true;
const partType = (part as { type: string }).type;
// Filter out thinking-steps, mentioned-documents, and attachments
// Filter out metadata parts not directly renderable by assistant-ui
return (
partType !== "thinking-steps" &&
partType !== "mentioned-documents" &&
@ -67,25 +30,6 @@ export function convertToThreadMessage(msg: MessageRecord): ThreadMessageLike {
content = [{ type: "text", text: String(msg.content) }];
}
// Restore attachments for user messages
let attachments: ThreadMessageLike["attachments"];
if (msg.role === "user") {
const persistedAttachments = extractPersistedAttachments(msg.content);
if (persistedAttachments.length > 0) {
attachments = persistedAttachments.map((att) => ({
id: att.id,
name: att.name,
type: att.type as "document" | "image" | "file",
contentType: att.contentType || "application/octet-stream",
status: { type: "complete" as const },
content: [],
// Custom fields for our ChatAttachment interface
imageDataUrl: att.imageDataUrl,
extractedContent: att.extractedContent,
}));
}
}
// Build metadata.custom for author display in shared chats
const metadata = msg.author_id
? {
@ -103,7 +47,6 @@ export function convertToThreadMessage(msg: MessageRecord): ThreadMessageLike {
role: msg.role,
content,
createdAt: new Date(msg.created_at),
attachments,
metadata,
};
}