feat(frontend): add React chat UI with SSE parsing and source accordion

This commit is contained in:
omp committed 2026-11-01 19:25:35 -03:00
1 parent 7a0396422f
commit 7f4d4bde66
21 files changed
+7220

No files matched your search

+86
View File
@@ -0,0 +1,86 @@
import { parseSSE, type SSEEvent } from "./lib/sse";
export interface Source {
reference: string;
text: string;
book: string;
chapter: number;
verse: number;
page: number;
file: string;
score: number;
}
export interface Status {
state: "ready" | "indexing" | "error";
pdfs_total: number;
pdfs_indexed: number;
chunks: number;
model: string;
last_indexed_at: string | null;
errors: string[];
}
export interface ChatMessage {
role: "user" | "assistant";
content: string;
}
const API_BASE = import.meta.env.VITE_API_BASE_URL ?? "";
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const response = await fetch(`${API_BASE}${path}`, {
headers: { "Content-Type": "application/json" },
...init,
});
if (!response.ok) {
let detail = `Erro ${response.status}`;
try {
const body = (await response.json()) as { detail?: string };
if (body.detail) {
detail = body.detail;
}
} catch {
/* resposta sem corpo JSON */
}
throw new Error(detail);
}
return (await response.json()) as T;
}
export function fetchStatus(): Promise<Status> {
return request<Status>("/api/status");
}
export function reindex(): Promise<{ state: string }> {
return request<{ state: string }>("/api/reindex", { method: "POST" });
}
/** Envia a mensagem e devolve os eventos do stream. */
export async function* sendMessage(
message: string,
history: ChatMessage[],
): AsyncGenerator<SSEEvent> {
const response = await fetch(`${API_BASE}/api/chat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, history }),
});
if (!response.ok || !response.body) {
let detail = `Erro ${response.status}`;
try {
const body = (await response.json()) as { detail?: string };
if (body.detail) {
detail = body.detail;
}
} catch {
/* resposta sem corpo JSON */
}
throw new Error(detail);
}
yield* parseSSE(response.body);
}