feat(frontend): add React chat UI with SSE parsing and source accordion
This commit is contained in:
1 parent
7a0396422f
commit
7f4d4bde66
21 files changed
+7220
No files matched your search
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user