import type { SendMessageResponse } from "@duoc-thu/shared-types"; import { buildMockResponse } from "./mockFixtures"; /** * Sends a question to the real RAG backend through the app's own route * handler, which holds the service URL and maps the response. * * The mock is retained but is now opt-in via `NEXT_PUBLIC_USE_MOCK_CHAT`, and * every mocked answer is labelled as such in its own text. Silently falling * back to a plausible-looking fake answer is the exact failure this project * cannot afford: a fabricated dose that looks like a real one. */ const USE_MOCK = process.env.NEXT_PUBLIC_USE_MOCK_CHAT === "true"; export class ChatUnavailableError extends Error { constructor(readonly status: number) { super(`Chat backend unavailable (${status})`); this.name = "ChatUnavailableError"; } } export async function sendChatMessage( content: string, conversationId?: string ): Promise { if (USE_MOCK) { await new Promise((resolve) => setTimeout(resolve, 400)); return buildMockResponse(content); } const response = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content, conversationId }), }); if (!response.ok) { // Surfaced to the user as an error state, never as an answer. throw new ChatUnavailableError(response.status); } return (await response.json()) as SendMessageResponse; }