RAGfly TypeScript/JavaScript SDK
The TypeScript SDK is the fastest way to connect a Node, browser, or edge agent to RAGfly. It wraps the REST API and SSE stream protocol into the same surface as the Python SDK. Zero dependencies — native
fetch.
Installation
npm install @ragfly/sdk
Runs on Node 18+, the browser, Vercel Edge and Cloudflare Workers (anywhere fetch exists). No runtime dependencies.
Quick start
import { RAGfly } from "@ragfly/sdk";
const client = new RAGfly({ apiKey: "slm_live_..." });
// End-to-end RAG: retrieves documents and generates a response
const resp = await client.ask("What are the Q1 sales figures?");
console.log(resp.answer);
// Token-by-token streaming
for await (const chunk of client.ask("Summarize the active contracts", { stream: true })) {
process.stdout.write(chunk.delta);
}
// Semantic retrieval only, without going through the LLM
const results = await client.search("maintenance contracts", { limit: 5 });
for (const doc of results.documents) {
console.log(doc.nombre, `rrfScore=${doc.rrfScore?.toFixed(3)}`);
for (const chunk of doc.chunks.slice(0, 2)) {
console.log(` "${chunk.texto.slice(0, 100)}…"`);
}
}
Method reference
client.ask(question, { stream?, conversationId? })
Natural language question over the corpus. Internally: creates a temporary conversation → sends the message → consumes the SSE stream → returns the response.
| Parameter | Type | Description |
|---|---|---|
question |
string |
The natural language question |
stream |
boolean |
true → returns AsyncGenerator<AskChunk>; omitted/false → Promise<AskResponse> |
conversationId |
number |
Reuse an existing conversation to maintain history |
Without streaming:
const resp = await client.ask("What does the Acme contract say?");
console.log(resp.answer); // string — full response
console.log(resp.conversationId); // number — id of the created conversation
With streaming:
for await (const chunk of client.ask("What does the Acme contract say?", { stream: true })) {
process.stdout.write(chunk.delta); // string — text fragment
}
// equivalent explicit form: client.askStream("...")
Maintain history in a conversation:
const r1 = await client.ask("Who signed the contract?");
const r2 = await client.ask("And when does it expire?", { conversationId: r1.conversationId });
client.search(query, { limit?, minSimilitud?, codigoEntidad?, idEspacio? })
Hybrid semantic search (vector + lexical + Cohere rerank) without LLM generation. Returns the most relevant chunks from the corpus with their scores.
| Parameter | Type | Description |
|---|---|---|
query |
string |
Search text |
limit |
number |
Maximum documents to return (default 10) |
minSimilitud |
number |
Minimum similarity threshold 0–1 (default 0.0) |
codigoEntidad |
string |
Filter by specific entity |
idEspacio |
number |
Search only within a Workspace |
const results = await client.search("maintenance contracts", { limit: 5 });
console.log(`${results.totalDocumentos} documents, ${results.totalChunks} chunks`);
console.log(`Time: ${results.duracionMs?.toFixed(0)}ms`);
for (const doc of results.documents) {
console.log(`· ${doc.nombre} (rrf=${doc.rrfScore?.toFixed(3)})`);
for (const chunk of doc.chunks) {
console.log(` similitud=${chunk.similitud?.toFixed(3)}: ${chunk.texto.slice(0, 80)}…`);
}
}
client.listDocuments({ page?, pageSize?, estado? })
Paginated list of the active group's corpus.
const page = await client.listDocuments({ page: 1, pageSize: 50 });
// → object with keys: items, total, page, limit
Opening a document on disk. Each document returned by listDocuments (and
by the document-detail response) carries an fs field describing where the file
lives, so an agent running on the same machine as the documents can open it.
The fs field of the response holds: ruta_archivo, ruta_es_absoluta,
carpeta_relativa, nombre_archivo, and como_abrir (a plain-language
instruction). The single rule:
ruta_es_absolutaistrue(loaded via RAGfly Desktop) → openruta_archivodirectly.ruta_es_absolutaisfalse(web upload via browser) → open$RAGFLY_ROOT + ruta_archivo, whereRAGFLY_ROOTis the parent folder of the root folder the user picked when uploading. Example: the user uploaded/Users/ana/Dropbox/MisDocumentos→ setRAGFLY_ROOT=/Users/ana/Dropbox, and/MisDocumentos/letras/cancion.txtresolves to/Users/ana/Dropbox/MisDocumentos/letras/cancion.txt.
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
const page = await client.listDocuments({ page: 1, pageSize: 50 });
for (const doc of page.items) {
const fs = doc.fs;
const path = fs.ruta_es_absoluta
? fs.ruta_archivo
: join(process.env.RAGFLY_ROOT!, fs.ruta_archivo);
if (existsSync(path)) {
const text = readFileSync(path, "utf8");
}
}
The
fsfield comes straight from the API insnake_case(it is not part of the mappedDocumentmodel above).
Set RAGFLY_ROOT once per machine — e.g.
export RAGFLY_ROOT="/Users/ana/Dropbox" in your ~/.zshrc; the cloud never
reads it nor stores your absolute root. Always check existsSync before
reading. Step-by-step walkthrough:
MCP.md § Setting up RAGFLY_ROOT.
Data models
TypeScript exposes the fields in camelCase; the backend speaks snake_case. The SDK maps between them.
| Interface | Key fields |
|---|---|
AskResponse |
answer: string, conversationId: number, messageId?: number |
AskChunk |
delta: string |
SearchResult |
query, totalDocumentos, totalChunks, duracionMs, documents: Document[] |
Document |
codigo, nombre, resumen, url, rrfScore, similitudMax, chunks: Chunk[] |
Chunk |
texto, similitud, scoreRerank, pagina, extra |
Mapping vs the Python SDK:
score_rerank→scoreRerank,rrf_score→rrfScore,similitud_max→similitudMax,total_documentos→totalDocumentos,duracion_ms→duracionMs.
Authentication
The SDK accepts API Keys (format slm_live_...) generated from app.ragfly.ai/api-keys or via the POST /auth/api-key endpoint.
const client = new RAGfly({ apiKey: process.env.RAGFLY_API_KEY! });
The Key inherits the group, entity, and role of the user who issued it. See INTEGRATION.md § Credentials for role and PROFILE details.
Options & errors
new RAGfly({
apiKey: "slm_live_...",
baseUrl: "https://api.ragfly.ai", // default
timeoutMs: 60000, // default
fetch: customFetch, // optional, defaults to globalThis.fetch
});
All non-2xx responses throw RAGflyError (with .statusCode):
import { RAGflyError } from "@ragfly/sdk";
try {
await client.ask("…");
} catch (err) {
if (err instanceof RAGflyError) console.error(err.statusCode, err.message);
}
