Client: chat
Asking questions and streaming answers, and the union both return.
chat.ask
import { createClient, citationDisplayLabel } from "@oprag/sdk";
const oprag = createClient({ apiUrl: "https://api.dev.oprag.ai", apiKey: embedKey });
const res = await oprag.chat.ask({ question: "What is your refund policy?" });
if (res.type === "answer") {
render(res.answer);
for (const source of res.sources ?? []) {
console.log(citationDisplayLabel(source));
}
} else {
// res.type === "lead_capture_prompt" — there is no `answer` on this branch.
renderLeadForm(res.promptMessage, res.fields);
} Multi-turn
Echo the ids back from the previous response and the next answer keeps its context. Omit them and every question is answered cold.
const first = await oprag.chat.ask({ question: "Do you ship to Canada?" });
if (first.type === "answer") {
const second = await oprag.chat.ask({
question: "How long does it take?",
sessionId: first.sessionId,
conversationId: first.conversationId,
visitorId: first.visitorId,
});
} chat.stream
const controller = new AbortController();
const final = await oprag.chat.stream(
{ question: "Summarize your shipping policy", sessionId },
{
// `accumulated` is the whole answer so far — render it directly.
onToken: (_token, accumulated) => setDraft(accumulated),
onSources: (sources) => setCitations(sources ?? []),
signal: controller.signal,
},
); stream resolves with the same union ask returns, so the branch check applies there too. A full UI example is in the streaming recipe.
Citations
Every field on a CitationSource is optional, which is why citationDisplayLabel exists — it picks the best available of title, document title, and location, and appends the page number when there is one.
Underneath, both methods call POST /v1/chat.
Ready to ship?
Get started free