Files
pastor-online/frontend/src/api.ts
T
omp 2043d5ddcf refactor: troca PDF+OCR por corpus USFX em domínio público
- corpus/: Almeida (João Ferreira de Almeida) em USFX, domínio público,
  66 livros / 1189 capítulos / 31098 versículos, versionado com SHA-256
- app/corpus.py: parser USFX (texto no tail de <v/>), nomes canônicos dos
  livros, aliases (abreviaturas e numerais romanos), VerseChunk sem página
- indexer: descobre *.xml em CORPUS_DIR, progresso em versículos, lote
  configurável, manifesto incremental por SHA-256
- remove extraction.py, parallel.py, bible_parser.py, Tesseract e PyMuPDF
- campo page removido de Source/SearchResult/StatusResponse e do frontend
- embedder: propriedade name; status reporta o embedder em uso, não o do .env
- create_app constrói o embedder ANTES do store (o default HashEmbedder do
  VectorStore impedia o modelo configurado de ser carregado em produção)
- fallback do modelo agora aparece em /api/status.errors
- torch 2.6.0 + transformers 5.22.1 pinados (transformers 5.x exige torch>=2.6)
2026-11-02 11:16:07 -03:00

89 lines
2.1 KiB
TypeScript

import { parseSSE, type SSEEvent } from "./lib/sse";
export interface Source {
reference: string;
text: string;
book: string;
chapter: number;
verse: number;
file: string;
score: number;
}
export interface Status {
state: "ready" | "indexing" | "error";
sources_total: number;
sources_indexed: number;
chunks: number;
model: string;
last_indexed_at: string | null;
errors: string[];
verses_indexed: number;
verses_total: number;
current_file: string | null;
}
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);
}