{"_id":"@aialchemy/rag-sdk","_rev":"5-b6a120fc608cc0aee74624129cb15061","name":"@aialchemy/rag-sdk","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.0":{"name":"@aialchemy/rag-sdk","version":"0.1.0","author":{"name":"AI Alchemy Labs"},"license":"MIT","_id":"@aialchemy/rag-sdk@0.1.0","maintainers":[{"name":"srinivas-jay","email":"billing@aialchemy.au"}],"homepage":"https://github.com/aialchemy/rag-sdk#readme","bugs":{"url":"https://github.com/aialchemy/rag-sdk/issues"},"dist":{"shasum":"a38a2d4f1f480f4d361500b88db4d85f035071ff","tarball":"https://registry.npmjs.org/@aialchemy/rag-sdk/-/rag-sdk-0.1.0.tgz","fileCount":7,"integrity":"sha512-Zr9h+eOMc953a3vDAYnhaUQo6VoFRJrxf6OooqpLFtITgvDmh2q36YarzEz9Ki1xdQSbRQElCdnNdZAGNcLxLw==","signatures":[{"sig":"MEUCIQCMs+1Bd9X6aZnn5dKT3GvNDhmFLqDS15LI56ikgsmxEAIgRDW8gKy9AnRSqK4BFeFYAzLmNRxKmnKPaofqfDgXTwM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":363980},"main":"./dist/index.cjs","type":"module","_from":"file:aialchemy-rag-sdk-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsx watch src/index.ts","lint":"biome check src/","test":"vitest run","build":"tsup","format":"biome format --write .","lint:fix":"biome check --write src/","typecheck":"tsc --noEmit","test:watch":"vitest","typecheck:examples":"tsc -p examples/tsconfig.json --noEmit"},"_npmUser":{"name":"srinivas-jay","email":"billing@aialchemy.au"},"_resolved":"/tmp/f8a1b6b947b84d86b94cad804984e8b5/aialchemy-rag-sdk-0.1.0.tgz","_integrity":"sha512-Zr9h+eOMc953a3vDAYnhaUQo6VoFRJrxf6OooqpLFtITgvDmh2q36YarzEz9Ki1xdQSbRQElCdnNdZAGNcLxLw==","repository":{"url":"git+https://github.com/aialchemy/rag-sdk.git","type":"git"},"_npmVersion":"11.8.0","description":"TypeScript-first, production-oriented RAG SDK for document ingestion, OCR, chunking, embeddings, indexing, retrieval, and answer generation.","directories":{},"_nodeVersion":"24.13.1","dependencies":{"zod":"^4.3.6","uuid":"^13.0.0","openai":"^6.34.0","@mistralai/mistralai":"^2.2.0","@qdrant/js-client-rest":"^1.17.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"^8.5.1","ollama":"^0.6.3","vitest":"^4.1.4","typescript":"^6.0.2","@types/node":"^25.6.0","@types/uuid":"^11.0.0","@biomejs/biome":"^2.4.11","@anthropic-ai/sdk":"^0.88.0","@google/generative-ai":"^0.24.1","@huggingface/inference":"^4.13.15"},"peerDependencies":{"ollama":">=0.5.0","@anthropic-ai/sdk":">=0.30.0","@google/generative-ai":">=0.21.0","@huggingface/inference":">=3.0.0"},"peerDependenciesMeta":{"ollama":{"optional":true},"@anthropic-ai/sdk":{"optional":true},"@google/generative-ai":{"optional":true},"@huggingface/inference":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rag-sdk_0.1.0_1775906167057_0.319523474011735","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aialchemy/rag-sdk","version":"0.1.1","author":{"name":"AI Alchemy Labs"},"license":"MIT","_id":"@aialchemy/rag-sdk@0.1.1","maintainers":[{"name":"srinivas-jay","email":"billing@aialchemy.au"}],"homepage":"https://github.com/aialchemylabs/rag-sdk#readme","bugs":{"url":"https://github.com/aialchemylabs/rag-sdk/issues"},"dist":{"shasum":"0420cf641aff32190447aabbe3070ce09c89d488","tarball":"https://registry.npmjs.org/@aialchemy/rag-sdk/-/rag-sdk-0.1.1.tgz","fileCount":7,"integrity":"sha512-tJlRY6z+CevFGRRoOIZaq0AsuRoQlVxnRKSeDe6SmuLyqAlRqLcWLj29eczcwH7r6JTIm7SUjrpbU7VZSTXWAQ==","signatures":[{"sig":"MEYCIQC0IhxCv7mTOe/ei9QY7apGNyxmJAGa9VB25d/eMCpF9QIhANIIo4p/NDKHBF+QrZZnNrSIj1NCCWOebpncsvCUUFmr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":364110},"main":"./dist/index.cjs","type":"module","_from":"file:aialchemy-rag-sdk-0.1.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsx watch src/index.ts","lint":"biome check src/","test":"vitest run","build":"tsup","format":"biome format --write .","lint:fix":"biome check --write src/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit","test:watch":"vitest","typecheck:e2e":"tsc --noEmit -p e2e/tsconfig.json","typecheck:examples":"tsc -p examples/tsconfig.json --noEmit"},"_npmUser":{"name":"srinivas-jay","email":"billing@aialchemy.au"},"_resolved":"/tmp/81c7d7f4601adf44d0d7d6c0e0474682/aialchemy-rag-sdk-0.1.1.tgz","_integrity":"sha512-tJlRY6z+CevFGRRoOIZaq0AsuRoQlVxnRKSeDe6SmuLyqAlRqLcWLj29eczcwH7r6JTIm7SUjrpbU7VZSTXWAQ==","repository":{"url":"git+https://github.com/aialchemylabs/rag-sdk.git","type":"git"},"_npmVersion":"11.8.0","description":"TypeScript-first, production-oriented RAG SDK for document ingestion, OCR, chunking, embeddings, indexing, retrieval, and answer generation.","directories":{},"_nodeVersion":"24.13.1","dependencies":{"zod":"^4.3.6","uuid":"^13.0.0","openai":"^6.34.0","@mistralai/mistralai":"^2.2.0","@qdrant/js-client-rest":"^1.17.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"^8.5.1","ollama":"^0.6.3","vitest":"^4.1.4","typescript":"^6.0.2","@types/node":"^25.6.0","@types/uuid":"^11.0.0","@biomejs/biome":"^2.4.11","@anthropic-ai/sdk":"^0.88.0","@google/generative-ai":"^0.24.1","@huggingface/inference":"^4.13.15"},"peerDependencies":{"ollama":">=0.5.0","@anthropic-ai/sdk":">=0.30.0","@google/generative-ai":">=0.21.0","@huggingface/inference":">=3.0.0"},"peerDependenciesMeta":{"ollama":{"optional":true},"@anthropic-ai/sdk":{"optional":true},"@google/generative-ai":{"optional":true},"@huggingface/inference":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rag-sdk_0.1.1_1775956076689_0.2517205651175394","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aialchemy/rag-sdk","version":"0.1.2","author":{"name":"AI Alchemy Labs"},"license":"MIT","_id":"@aialchemy/rag-sdk@0.1.2","maintainers":[{"name":"srinivas-jay","email":"billing@aialchemy.au"}],"homepage":"https://github.com/aialchemylabs/rag-sdk#readme","bugs":{"url":"https://github.com/aialchemylabs/rag-sdk/issues"},"dist":{"shasum":"0595981523eb70be54e03c5a0837f018db69f82f","tarball":"https://registry.npmjs.org/@aialchemy/rag-sdk/-/rag-sdk-0.1.2.tgz","fileCount":7,"integrity":"sha512-QavjfIrifbS3jAJBlqqZbh5yjqoSpGh7aab3xYVjgIeF6WQveYTFRHRVXUaG73eEmGB7IugNC41kLQuctM9yRw==","signatures":[{"sig":"MEUCIF73TGQ59NcfEdirv/YqaimJpkiY+pcldbzmZDGscE9PAiEA696qz9KByJ3XfgAdeVAovJ+M3mMtqnD4SZBbz0NXzCg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":386754},"main":"./dist/index.cjs","type":"module","_from":"file:aialchemy-rag-sdk-0.1.2.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsx watch src/index.ts","lint":"biome check src/","test":"vitest run","build":"tsup","format":"biome format --write .","lint:fix":"biome check --write src/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit","test:watch":"vitest","typecheck:e2e":"tsc --noEmit -p e2e/tsconfig.json","typecheck:examples":"tsc -p examples/tsconfig.json --noEmit"},"_npmUser":{"name":"srinivas-jay","email":"billing@aialchemy.au"},"_resolved":"/tmp/82dabe1c3c28ac2e5c9264ce695626e7/aialchemy-rag-sdk-0.1.2.tgz","_integrity":"sha512-QavjfIrifbS3jAJBlqqZbh5yjqoSpGh7aab3xYVjgIeF6WQveYTFRHRVXUaG73eEmGB7IugNC41kLQuctM9yRw==","repository":{"url":"git+https://github.com/aialchemylabs/rag-sdk.git","type":"git"},"_npmVersion":"11.8.0","description":"TypeScript-first, production-oriented RAG SDK for document ingestion, OCR, chunking, embeddings, indexing, retrieval, and answer generation.","directories":{},"_nodeVersion":"24.13.1","dependencies":{"zod":"^4.3.6","uuid":"^13.0.0","openai":"^6.34.0","@mistralai/mistralai":"^2.2.0","@qdrant/js-client-rest":"^1.17.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"^8.5.1","ollama":"^0.6.3","vitest":"^4.1.4","typescript":"^6.0.2","@types/node":"^25.6.0","@types/uuid":"^11.0.0","@biomejs/biome":"^2.4.11","@anthropic-ai/sdk":"^0.88.0","@google/generative-ai":"^0.24.1","@huggingface/inference":"^4.13.15"},"peerDependencies":{"ollama":">=0.5.0","@anthropic-ai/sdk":">=0.30.0","@google/generative-ai":">=0.21.0","@huggingface/inference":">=3.0.0"},"peerDependenciesMeta":{"ollama":{"optional":true},"@anthropic-ai/sdk":{"optional":true},"@google/generative-ai":{"optional":true},"@huggingface/inference":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rag-sdk_0.1.2_1776154699184_0.8623242134429432","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@aialchemy/rag-sdk","version":"0.1.3","author":{"name":"AI Alchemy Labs"},"license":"MIT","_id":"@aialchemy/rag-sdk@0.1.3","maintainers":[{"name":"srinivas-jay","email":"billing@aialchemy.au"}],"homepage":"https://github.com/aialchemylabs/rag-sdk#readme","bugs":{"url":"https://github.com/aialchemylabs/rag-sdk/issues"},"dist":{"shasum":"335aa7e5fff6a819ff6682da81ae3acc47bfdfcf","tarball":"https://registry.npmjs.org/@aialchemy/rag-sdk/-/rag-sdk-0.1.3.tgz","fileCount":7,"integrity":"sha512-4+0AW5hjG8rpivVg++IjPirhGX0irzdvCE4R6nPbaO17qj0sFLTzwd1tR1nwIN44c1FKxI/XVkItnuFvQKTbsg==","signatures":[{"sig":"MEQCIGsGq3kdNlUfEXBYm0Qbc6K13mjPhDXcZWcruLBlQYroAiAzWEW7jBiBkN3ZG6hIPRyREgWxuD+ADi9sMOM8aWq6oA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":406386},"main":"./dist/index.cjs","type":"module","_from":"file:aialchemy-rag-sdk-0.1.3.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsx watch src/index.ts","lint":"biome check src/","test":"vitest run","build":"tsup","format":"biome format --write .","lint:fix":"biome check --write src/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit","test:watch":"vitest","typecheck:e2e":"tsc --noEmit -p e2e/tsconfig.json","typecheck:examples":"tsc -p examples/tsconfig.json --noEmit"},"_npmUser":{"name":"srinivas-jay","email":"billing@aialchemy.au"},"_resolved":"/tmp/b950dfc12937aa347c55cd7aaf15dd4d/aialchemy-rag-sdk-0.1.3.tgz","_integrity":"sha512-4+0AW5hjG8rpivVg++IjPirhGX0irzdvCE4R6nPbaO17qj0sFLTzwd1tR1nwIN44c1FKxI/XVkItnuFvQKTbsg==","repository":{"url":"git+https://github.com/aialchemylabs/rag-sdk.git","type":"git"},"_npmVersion":"11.8.0","description":"TypeScript-first, production-oriented RAG SDK for document ingestion, OCR, chunking, embeddings, indexing, retrieval, and answer generation.","directories":{},"_nodeVersion":"24.13.1","dependencies":{"zod":"^4.3.6","uuid":"^13.0.0","openai":"^6.34.0","@mistralai/mistralai":"^2.2.0","@qdrant/js-client-rest":"^1.17.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"^8.5.1","ollama":"^0.6.3","vitest":"^4.1.4","typescript":"^6.0.2","@types/node":"^25.6.0","@types/uuid":"^11.0.0","@biomejs/biome":"^2.4.11","@anthropic-ai/sdk":"^0.88.0","@google/generative-ai":"^0.24.1","@huggingface/inference":"^4.13.15"},"peerDependencies":{"ollama":">=0.5.0","@anthropic-ai/sdk":">=0.30.0","@google/generative-ai":">=0.21.0","@huggingface/inference":">=3.0.0"},"peerDependenciesMeta":{"ollama":{"optional":true},"@anthropic-ai/sdk":{"optional":true},"@google/generative-ai":{"optional":true},"@huggingface/inference":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rag-sdk_0.1.3_1776676917228_0.9795024375688879","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@aialchemy/rag-sdk","version":"0.1.4","description":"TypeScript-first, production-oriented RAG SDK for document ingestion, OCR, chunking, embeddings, indexing, retrieval, and answer generation.","homepage":"https://github.com/aialchemylabs/rag-sdk#readme","bugs":{"url":"https://github.com/aialchemylabs/rag-sdk/issues"},"repository":{"type":"git","url":"git+https://github.com/aialchemylabs/rag-sdk.git"},"license":"MIT","author":{"name":"AI Alchemy Labs"},"type":"module","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","dependencies":{"@mistralai/mistralai":"^2.2.0","@qdrant/js-client-rest":"^1.17.0","openai":"^6.34.0","uuid":"^13.0.0","zod":"^4.3.6"},"peerDependencies":{"@anthropic-ai/sdk":">=0.30.0","@google/generative-ai":">=0.21.0","@huggingface/inference":">=3.0.0","ollama":">=0.5.0"},"peerDependenciesMeta":{"@anthropic-ai/sdk":{"optional":true},"@google/generative-ai":{"optional":true},"@huggingface/inference":{"optional":true},"ollama":{"optional":true}},"devDependencies":{"@anthropic-ai/sdk":"^0.88.0","@biomejs/biome":"^2.4.11","@google/generative-ai":"^0.24.1","@huggingface/inference":"^4.13.15","@types/node":"^25.6.0","@types/uuid":"^11.0.0","ollama":"^0.6.3","tsup":"^8.5.1","tsx":"^4.21.0","typescript":"^6.0.2","vitest":"^4.1.4"},"engines":{"node":">=22"},"scripts":{"build":"tsup","dev":"tsx watch src/index.ts","typecheck":"tsc --noEmit","typecheck:examples":"tsc -p examples/tsconfig.json --noEmit","lint":"biome check src/","lint:fix":"biome check --write src/","format":"biome format --write .","test":"vitest run","test:e2e":"vitest run --config vitest.e2e.config.ts","test:watch":"vitest","typecheck:e2e":"tsc --noEmit -p e2e/tsconfig.json"},"_id":"@aialchemy/rag-sdk@0.1.4","_integrity":"sha512-g81XMqmYPSv8PGDNhBsdWTEJTKxboFlC3P5DhbCTtNso7MFZuCNqukOUS512zwj8KnwvpNV2ZIWWtHkBeZ/NNw==","_resolved":"/tmp/b992b6418410cd8728813787ba4f7e14/aialchemy-rag-sdk-0.1.4.tgz","_from":"file:aialchemy-rag-sdk-0.1.4.tgz","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-g81XMqmYPSv8PGDNhBsdWTEJTKxboFlC3P5DhbCTtNso7MFZuCNqukOUS512zwj8KnwvpNV2ZIWWtHkBeZ/NNw==","shasum":"c8935b984efff3c91ceaa327c0658a90454b383e","tarball":"https://registry.npmjs.org/@aialchemy/rag-sdk/-/rag-sdk-0.1.4.tgz","fileCount":7,"unpackedSize":411098,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDwCqWQqjtoH+SxI4koClQ/Z9tGmkCRDc2CgYfQMZgyIwIhAKI3Iia2uqfeQ6x42VFEdkXNMgZXtmPnuId0GpuEXYdH"}]},"_npmUser":{"name":"srinivas-jay","email":"billing@aialchemy.au"},"directories":{},"maintainers":[{"name":"srinivas-jay","email":"billing@aialchemy.au"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rag-sdk_0.1.4_1776678718268_0.5498242518971221"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-11T11:16:06.982Z","modified":"2026-04-20T09:51:58.561Z","0.1.0":"2026-04-11T11:16:07.220Z","0.1.1":"2026-04-12T01:07:56.857Z","0.1.2":"2026-04-14T08:18:19.426Z","0.1.3":"2026-04-20T09:21:57.394Z","0.1.4":"2026-04-20T09:51:58.428Z"},"bugs":{"url":"https://github.com/aialchemylabs/rag-sdk/issues"},"author":{"name":"AI Alchemy Labs"},"license":"MIT","homepage":"https://github.com/aialchemylabs/rag-sdk#readme","repository":{"type":"git","url":"git+https://github.com/aialchemylabs/rag-sdk.git"},"description":"TypeScript-first, production-oriented RAG SDK for document ingestion, OCR, chunking, embeddings, indexing, retrieval, and answer generation.","maintainers":[{"name":"srinivas-jay","email":"billing@aialchemy.au"}],"readme":"# @aialchemy/rag-sdk\n\nTypeScript-first, production-oriented RAG SDK for document ingestion, OCR, chunking, embeddings, indexing, retrieval, and answer generation.\n\n---\n\n## Features\n\n- **End-to-end document RAG pipeline** in a single package -- ingest, chunk, embed, index, retrieve, and answer\n- **Mistral OCR** for born-digital and scanned documents with page-aware extraction\n- **Qdrant vector storage** with metadata filtering, tenant isolation, and hybrid search\n- **Multi-provider LLM support** -- OpenAI, Anthropic, Google Gemini, HuggingFace, and Ollama for embeddings and chat, with optional peer dependencies\n- **Citation-first answering** -- every answer traces back to source documents and page ranges\n- **Multi-tenancy** -- per-tenant Qdrant collections with enforced metadata filters\n- **Sync and async ingestion** -- blocking mode for development, job-based mode for production\n- **Typed errors** with error codes, categories, retryable flags, and partial-success patterns\n- **Telemetry hooks** for events and metrics -- compatible with OpenTelemetry-style tracing\n- **Strong TypeScript types** for every config, input, output, and error surface\n- **Zod-validated configuration** with fail-fast initialization\n- **ESM-first** package with full type declarations\n\n### Supported File Types\n\nPDF, PNG, JPEG, TIFF, WEBP, GIF, BMP, AVIF, DOCX, PPTX, and plain text.\n\n---\n\n## Installation\n\n```bash\npnpm add @aialchemy/rag-sdk\n```\n\n**Requirements:** Node.js 22 or later.\n\n---\n\n## Supported LLM Providers\n\nThe SDK supports multiple LLM providers for embeddings and answer generation. Only install the packages you need.\n\n| Provider | Embeddings | Chat/Answering | Package | API Key Required |\n|----------|-----------|---------------|---------|-----------------|\n| OpenAI | Yes | Yes | `openai` (included) | Yes |\n| Anthropic | No | Yes | `@anthropic-ai/sdk` | Yes |\n| Google Gemini | Yes | Yes | `@google/generative-ai` | Yes |\n| HuggingFace | Yes | Yes | `@huggingface/inference` | Yes |\n| Ollama | Yes | Yes | `ollama` | No (local) |\n\nInstall optional providers as needed:\n\n```bash\n# For Anthropic answering\npnpm add @anthropic-ai/sdk\n\n# For Gemini embeddings + answering\npnpm add @google/generative-ai\n\n# For HuggingFace\npnpm add @huggingface/inference\n\n# For Ollama (local models)\npnpm add ollama\n```\n\n### Example: Gemini embeddings with Anthropic answering\n\n```ts\nconst rag = await createRag({\n  mistral: { apiKey: process.env.MISTRAL_API_KEY! },\n  qdrant: { url: 'http://localhost:6333', collection: 'my-docs' },\n  embeddings: {\n    provider: 'gemini',\n    model: 'text-embedding-004',\n    apiKey: process.env.GEMINI_API_KEY!,\n  },\n  answering: {\n    provider: 'anthropic',\n    model: 'claude-sonnet-4-20250514',\n    apiKey: process.env.ANTHROPIC_API_KEY!,\n  },\n});\n```\n\n### Example: Fully local with Ollama\n\n```ts\nconst rag = await createRag({\n  mistral: { apiKey: process.env.MISTRAL_API_KEY! },\n  qdrant: { url: 'http://localhost:6333', collection: 'my-docs' },\n  embeddings: {\n    provider: 'ollama',\n    model: 'nomic-embed-text',\n  },\n  answering: {\n    provider: 'ollama',\n    model: 'llama3',\n  },\n});\n```\n\n---\n\n## Quick Start\n\n```ts\nimport { createRag } from '@aialchemy/rag-sdk';\n\nconst rag = await createRag({\n  mistral: {\n    apiKey: process.env.MISTRAL_API_KEY!,\n  },\n  qdrant: {\n    url: process.env.QDRANT_URL!,\n    apiKey: process.env.QDRANT_API_KEY,\n    collection: 'my-documents',\n  },\n  embeddings: {\n    provider: 'openai',\n    model: 'text-embedding-3-small',\n    apiKey: process.env.OPENAI_API_KEY!,\n  },\n});\n\n// Ingest a PDF\nconst result = await rag.ingest.file('./contracts/agreement.pdf', {\n  tags: ['legal', 'contracts'],\n});\n\nconsole.log(`Indexed ${result.chunksIndexed} chunks from ${result.sourceName}`);\n\n// Retrieve relevant chunks\nconst retrieval = await rag.retrieve('What are the termination clauses?', {\n  topK: 5,\n  scoreThreshold: 0.7,\n});\n\nfor (const match of retrieval.matches) {\n  console.log(`[${match.score.toFixed(3)}] ${match.citation.sourceName} p${match.citation.pageStart}`);\n  console.log(match.content);\n}\n```\n\n---\n\n## Configuration\n\n### `createRag(config: RagConfig): Promise<RagClient>`\n\nThe factory is async because LLM providers may use dynamic imports for optional peer dependencies.\n\nThe factory function validates all configuration at initialization and fails fast on invalid or missing values.\n\n### Required\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `mistral.apiKey` | `string` | Mistral API key for OCR |\n| `qdrant.url` | `string` | Qdrant instance URL |\n| `qdrant.collection` | `string` | Qdrant collection name (used as prefix for multi-tenant collections) |\n| `embeddings.provider` | `'openai' \\| 'gemini' \\| 'huggingface' \\| 'ollama'` | Embedding provider name |\n| `embeddings.model` | `string` | Embedding model identifier |\n| `embeddings.apiKey` | `string` | Embedding provider API key (optional for Ollama) |\n\n### Optional\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `mistral.model` | `string` | `'mistral-ocr-latest'` | Mistral OCR model |\n| `qdrant.apiKey` | `string` | -- | Qdrant API key (if instance is secured) |\n| `embeddings.baseUrl` | `string` | -- | Custom base URL for embedding provider |\n| `embeddings.vectorSize` | `number` | -- | Override vector dimensions |\n| `embeddings.distanceMetric` | `'cosine' \\| 'euclid' \\| 'dot'` | `'cosine'` | Distance metric for Qdrant |\n| `embeddings.versionLabel` | `string` | `'{provider}:{model}'` | Label stored with every chunk for migration safety |\n| `chunking.targetTokens` | `number` | `512` | Target tokens per chunk (50--8192) |\n| `chunking.maxTokens` | `number` | `1024` | Maximum tokens per chunk (100--16384) |\n| `chunking.overlapTokens` | `number` | `64` | Overlap tokens between chunks (0--512) |\n| `chunking.headingAware` | `boolean` | `true` | Split on heading boundaries |\n| `chunking.preservePageBoundaries` | `boolean` | `false` | Avoid splitting across pages |\n| `chunking.preserveTables` | `boolean` | `true` | Keep tables intact within chunks |\n| `retrieval.topK` | `number` | `10` | Default number of results (1--100) |\n| `retrieval.scoreThreshold` | `number` | `0.0` | Minimum similarity score (0--1) |\n| `retrieval.hybrid.enabled` | `boolean` | -- | Enable hybrid (dense + sparse) search |\n| `retrieval.hybrid.fusionAlpha` | `number` | `0.5` | Fusion weight between dense and sparse (0--1) |\n| `answering.provider` | `'openai' \\| 'anthropic' \\| 'gemini' \\| 'huggingface' \\| 'ollama'` | -- | Answer generation LLM provider |\n| `answering.model` | `string` | -- | Answer generation model |\n| `answering.apiKey` | `string` | -- | Answer generation API key (optional for Ollama) |\n| `documentStore` | `DocumentStore` | `InMemoryDocumentStore` | Custom document metadata store for persistence |\n| `answering.baseUrl` | `string` | -- | Custom base URL for answer provider |\n| `answering.maxTokens` | `number` | `2048` | Maximum tokens for generated answer |\n| `answering.temperature` | `number` | `0.1` | Sampling temperature (0--2) |\n| `answering.noCitationPolicy` | `'refuse' \\| 'warn' \\| 'allow'` | `'refuse'` | Behavior when evidence is insufficient |\n| `telemetry.enabled` | `boolean` | `true` | Enable telemetry events and metrics |\n| `telemetry.onEvent` | `(event) => void` | -- | Callback for telemetry events |\n| `telemetry.onMetric` | `(metric) => void` | -- | Callback for metrics |\n| `security.redactPii` | `boolean` | `false` | Enable PII redaction |\n| `security.preprocessor` | `(content: string) => string` | -- | Custom content preprocessor |\n| `defaults.processingMode` | `'text_first' \\| 'ocr_first' \\| 'hybrid'` | `'hybrid'` | Default OCR processing mode |\n| `defaults.tenantId` | `string` | -- | Default tenant ID applied to all operations |\n| `defaults.domainId` | `string` | -- | Default domain ID |\n| `defaults.tags` | `string[]` | -- | Default tags applied to ingested documents |\n| `jobs.concurrency` | `number` | `5` | Maximum concurrent async jobs (1--50) |\n| `jobs.timeoutMs` | `number` | `300000` | Job timeout in milliseconds |\n| `maxFileSizeBytes` | `number` | `52428800` | Maximum file size (default 50 MB) |\n\n---\n\n## Environment Variables\n\nAll required credentials can be provided via environment variables as a fallback when not passed in config. Explicit config always takes precedence.\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `MISTRAL_API_KEY` | Yes | Mistral OCR API key |\n| `QDRANT_URL` | Yes | Qdrant instance URL |\n| `QDRANT_API_KEY` | No | Qdrant API key (if instance is secured) |\n| `QDRANT_COLLECTION` | Yes | Default Qdrant collection name |\n| `OPENAI_API_KEY` | Conditional | Required when using OpenAI as your embedding or answer provider |\n| `ANTHROPIC_API_KEY` | Conditional | Required when using Anthropic as your answer provider |\n| `GEMINI_API_KEY` | Conditional | Required when using Gemini as your embedding or answer provider |\n| `HUGGINGFACE_API_KEY` | Conditional | Required when using HuggingFace as your embedding or answer provider |\n\n---\n\n## Ingestion\n\nThe ingestion pipeline handles: input detection, Mistral OCR extraction, document normalization, chunking, embedding generation, and Qdrant indexing.\n\n### File ingestion\n\n```ts\nconst result = await rag.ingest.file('./reports/quarterly-review.pdf', {\n  processingMode: ProcessingMode.Hybrid,\n  tags: ['finance', 'quarterly'],\n  domainId: 'reports',\n});\n```\n\n### Buffer ingestion\n\n```ts\nimport { readFile } from 'node:fs/promises';\n\nconst buffer = await readFile('./scanned-invoice.png');\n\nconst result = await rag.ingest.buffer(buffer, 'scanned-invoice.png', {\n  processingMode: ProcessingMode.OcrFirst,\n  tags: ['invoices'],\n});\n```\n\n### URL ingestion\n\n```ts\nconst result = await rag.ingest.url('https://example.com/documents/whitepaper.pdf', {\n  tags: ['research'],\n});\n```\n\n### Text ingestion\n\nFor pre-extracted or plain text content that does not need OCR:\n\n```ts\nconst result = await rag.ingest.text('This is the full document content...', {\n  tags: ['notes'],\n});\n```\n\n### Async ingestion\n\nFor large files or production pipelines, enable async mode to receive a job ID for tracking:\n\n```ts\nconst tenantId = 'tenant-acme';\n\nconst result = await rag.ingest.file('./large-report.pdf', {\n  async: true,\n  security: { tenantId },\n  tags: ['bulk'],\n});\n\nconsole.log(`Job started: ${result.jobId}`);\n\n// Poll for completion\nconst job = await rag.jobs.get(result.jobId!, tenantId);\nconsole.log(`Status: ${job?.status}, Progress: ${job?.progress}%`);\n```\n\n### Ingestion result\n\nEvery ingestion returns an `IngestResult`:\n\n```ts\ninterface IngestResult {\n  documentId: string;\n  sourceName: string;\n  status: 'completed' | 'partial' | 'failed' | 'pending';\n  normalizedDocument?: NormalizedDocument; // absent when async/pending\n  chunkingResult?: ChunkingResult;        // absent when async/pending\n  chunksIndexed: number;\n  processingTimeMs: number;\n  warnings: string[];\n  jobId?: string; // present when async: true\n}\n```\n\n---\n\n## Retrieval\n\n### Dense vector search\n\n```ts\nconst result = await rag.retrieve('What are the payment terms?', {\n  topK: 10,\n  scoreThreshold: 0.7,\n  filters: {\n    tags: ['contracts'],\n    domainId: 'legal',\n  },\n});\n\nfor (const match of result.matches) {\n  console.log(`Score: ${match.score}`);\n  console.log(`Source: ${match.citation.sourceName}, pages ${match.citation.pageStart}-${match.citation.pageEnd}`);\n  console.log(`Content: ${match.content}\\n`);\n}\n```\n\n### Hybrid search\n\nCombines dense vector search with sparse keyword matching. Requires `retrieval.hybrid.enabled: true` in config.\n\n```ts\nconst result = await rag.retrieve.hybrid('termination clause penalties', {\n  topK: 10,\n  fusionAlpha: 0.6, // 0 = dense only, 1 = sparse only\n  filters: {\n    documentIds: ['doc-abc-123'],\n  },\n});\n```\n\n### Retrieval result\n\n```ts\ninterface RetrieveResult {\n  query: string;\n  matches: RetrieveMatch[];\n  totalMatches: number;\n  searchTimeMs: number;\n  searchType: 'dense' | 'hybrid';\n}\n\ninterface RetrieveMatch {\n  chunkId: string;\n  documentId: string;\n  content: string;\n  score: number;\n  metadata: ChunkMetadata;\n  citation: CitationAnchor;\n}\n```\n\n---\n\n## Answer Generation\n\nGenerate citation-backed answers from retrieved evidence. Requires the `answering` config block.\n\n```ts\nconst rag = await createRag({\n  // ...required config...\n  answering: {\n    provider: 'openai',\n    model: 'gpt-4o',\n    apiKey: process.env.OPENAI_API_KEY!,\n    noCitationPolicy: 'refuse', // default -- will not generate unsupported claims\n  },\n});\n\nconst answer = await rag.answer('What is the liability cap?', {\n  topK: 5,\n  filters: { tags: ['contracts'] },\n});\n\nconsole.log(`Answer: ${answer.answer}`);\nconsole.log(`Confidence: ${answer.confidence}`);\nconsole.log(`Risk level: ${answer.riskLevel}`);\n\nfor (const citation of answer.citations) {\n  console.log(`  [${citation.citationIndex}] ${citation.anchor.sourceName} p${citation.anchor.pageStart}: \"${citation.text}\"`);\n}\n```\n\n### No-citation policy\n\nThe `noCitationPolicy` setting controls SDK behavior when retrieved evidence is insufficient:\n\n| Policy | Behavior |\n|--------|----------|\n| `'refuse'` | Returns a disclaimer instead of an unsupported answer (default) |\n| `'warn'` | Returns a notice that no evidence was found, sets `riskLevel` to `'no_evidence'` and includes a disclaimer |\n| `'allow'` | Returns an empty answer with `confidence: 'none'` and `riskLevel: 'no_evidence'`, letting the caller decide how to proceed |\n\n### Answer result\n\n```ts\ninterface AnswerResult {\n  answer: string;\n  citations: AnswerCitation[];\n  confidence: 'high' | 'medium' | 'low' | 'none';\n  riskLevel: 'safe' | 'low_evidence' | 'no_evidence';\n  disclaimer?: string;\n  sources: Array<{\n    documentId: string;\n    sourceName: string;\n    pageRange: string;\n  }>;\n  retrievalTimeMs: number;\n  generationTimeMs: number;\n  totalTimeMs: number;\n}\n```\n\n---\n\n## Documents Management\n\n### Get a document\n\n```ts\nconst doc = await rag.documents.get('doc-abc-123');\n\nif (doc) {\n  console.log(`${doc.sourceName} -- ${doc.chunkCount} chunks, ${doc.pageCount} pages`);\n}\n```\n\n### List documents\n\n```ts\nconst docs = await rag.documents.list({\n  tenantId: 'tenant-1',\n  tags: ['contracts'],\n  limit: 20,\n  offset: 0,\n});\n```\n\n### Delete a document\n\nRemoves the document record and all indexed chunks from Qdrant:\n\n```ts\nconst { deleted } = await rag.documents.delete('doc-abc-123');\nconsole.log(`Deleted ${deleted} chunks`);\n```\n\n### Reindex a document\n\nReplace all indexed chunks for a document:\n\n```ts\nconst updatedChunks = [\n  {\n    chunkId: 'chk-1',\n    content: 'Updated text for this chunk...',\n    metadata: {\n      sourceName: 'contract.pdf',\n      pageStart: 3,\n      pageEnd: 3,\n      processingMode: 'hybrid',\n      ocrProvider: 'mistral',\n    },\n  },\n];\n\nconst { reindexed } = await rag.documents.reindex('doc-abc-123', updatedChunks, 'tenant-acme');\nconsole.log(`Reindexed ${reindexed} chunks`);\n```\n\n### Update metadata\n\nPatch tags, domain, or custom metadata on an existing document:\n\n```ts\nawait rag.documents.updateMetadata('doc-abc-123', {\n  tags: ['contracts', 'reviewed'],\n  domainId: 'legal-reviewed',\n  metadata: { reviewedBy: 'jdoe', reviewedAt: new Date().toISOString() },\n});\n```\n\n### Custom document store\n\n> **Warning:** The default `InMemoryDocumentStore` and `InMemoryJobStore` are for **development and testing only**. All document metadata and job state is lost on process restart. For production deployments, implement the `DocumentStore` interface with a persistent backend (PostgreSQL, Redis, DynamoDB, etc.) and pass it via config. The SDK logs a warning at startup when using the in-memory default.\n\nFor persistence, implement the `DocumentStore` interface or pass a custom backend:\n\n```ts\nimport { createRag, InMemoryDocumentStore } from '@aialchemy/rag-sdk';\nimport type { DocumentStore } from '@aialchemy/rag-sdk';\n\n// Use the default in-memory store (no config needed)\nconst rag = await createRag({ /* ... */ });\n\n// Or pass a custom persistent store\nconst rag = await createRag({\n  // ...required config...\n  documentStore: myCustomStore, // implements DocumentStore interface\n});\n```\n\n---\n\n## Async Jobs\n\nAsync ingestion returns a job ID for tracking long-running operations.\n\n### Get job status\n\n```ts\nconst job = await rag.jobs.get('job-xyz-789', 'tenant-acme');\n\nif (job) {\n  console.log(`Status: ${job.status}`);  // 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n  console.log(`Progress: ${job.progress}%`);\n}\n```\n\n### List jobs\n\n```ts\nconst jobs = await rag.jobs.list({\n  status: 'running',\n  limit: 10,\n});\n```\n\n### Cancel a job\n\n```ts\nconst cancelled = await rag.jobs.cancel('job-xyz-789', 'tenant-acme');\nconsole.log(`Job ${cancelled.jobId} is now ${cancelled.status}`);\n```\n\n---\n\n## Multi-Tenancy\n\nThe SDK supports per-tenant collection isolation in Qdrant. Pass a `SecurityContext` with a `tenantId` to scope all operations.\n\n### Tenant-scoped ingestion\n\n```ts\nconst result = await rag.ingest.file('./tenant-doc.pdf', {\n  security: {\n    tenantId: 'tenant-acme',\n    userId: 'user-42',\n  },\n  tags: ['onboarding'],\n});\n```\n\n### Tenant-scoped retrieval\n\n```ts\nconst result = await rag.retrieve('renewal terms', {\n  security: {\n    tenantId: 'tenant-acme',\n  },\n  filters: {\n    tags: ['contracts'],\n  },\n});\n```\n\n### Default tenant\n\nSet a default tenant at initialization to avoid passing it on every call:\n\n```ts\nconst rag = await createRag({\n  // ...required config...\n  defaults: {\n    tenantId: 'tenant-acme',\n  },\n});\n```\n\nWhen `tenantId` is configured, the SDK enforces tenant metadata filters on all operations. Tenant filters are never silently skipped.\n\n---\n\n## Error Handling\n\nAll SDK errors are instances of `RagSdkError` with structured fields for programmatic handling.\n\n### Catching errors\n\n```ts\nimport { RagSdkError, RagErrorCode } from '@aialchemy/rag-sdk';\n\ntry {\n  await rag.ingest.file('./document.pdf');\n} catch (err) {\n  if (err instanceof RagSdkError) {\n    console.error(`[${err.code}] ${err.message}`);\n    console.error(`Category: ${err.category}`);\n    console.error(`Retryable: ${err.retryable}`);\n    console.error(`Provider: ${err.provider}`);\n    console.error(`Details:`, err.details);\n  }\n}\n```\n\n### Error shape\n\n```ts\nclass RagSdkError extends Error {\n  code: RagErrorCode;        // e.g. 'OCR_TOTAL_FAILURE'\n  category: RagErrorCategory; // e.g. 'ocr'\n  retryable: boolean;\n  provider?: string;\n  details?: RagErrorDetails;\n}\n```\n\n### Error categories\n\n| Category | Example codes |\n|----------|--------------|\n| `configuration` | `CONFIG_MISSING_REQUIRED`, `CONFIG_INVALID_URL`, `CONFIG_UNSUPPORTED_PROVIDER`, `CONFIG_INVALID_RANGE` |\n| `authentication` | `AUTH_INVALID_KEY`, `AUTH_EXPIRED_KEY`, `AUTH_PROVIDER_UNAUTHORIZED` |\n| `ocr` | `OCR_TOTAL_FAILURE`, `OCR_PARTIAL_FAILURE`, `OCR_PAGE_FAILURE`, `OCR_EMPTY_RESULT`, `OCR_UNSUPPORTED_FILE` |\n| `embedding` | `EMBEDDING_PROVIDER_ERROR`, `EMBEDDING_DIMENSION_MISMATCH`, `EMBEDDING_RATE_LIMIT` |\n| `vector_database` | `VECTOR_CONNECTION_ERROR`, `VECTOR_COLLECTION_NOT_FOUND`, `VECTOR_UPSERT_FAILED`, `VECTOR_SEARCH_FAILED` |\n| `validation` | `VALIDATION_INVALID_INPUT`, `VALIDATION_FILE_TOO_LARGE`, `VALIDATION_UNSUPPORTED_TYPE` |\n| `timeout` | `TIMEOUT_INGESTION`, `TIMEOUT_RETRIEVAL`, `TIMEOUT_ANSWER` |\n| `partial_processing` | `PARTIAL_OCR`, `PARTIAL_INDEXING` |\n| `answer` | `ANSWER_PROVIDER_ERROR`, `ANSWER_NO_EVIDENCE`, `ANSWER_LOW_CONFIDENCE` |\n\n### Partial success\n\nIngestion can return a `'partial'` status when non-fatal warnings are raised during extraction (e.g., low embedded-text content detected in `text_first` mode). OCR failures and indexing failures throw a `RagSdkError` instead of returning partial results. Always check `result.status` and `result.warnings`:\n\n```ts\nconst result = await rag.ingest.file('./mixed-quality-scan.pdf');\n\nif (result.status === 'partial') {\n  console.warn('Partial ingestion:', result.warnings);\n}\n```\n\n### Retry and resilience\n\nThe SDK does **not** include built-in retry logic. All errors carry a `retryable` flag that your application should use to decide whether to retry:\n\n```ts\nimport { RagSdkError } from '@aialchemy/rag-sdk';\n\nasync function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {\n  for (let attempt = 0; attempt <= maxRetries; attempt++) {\n    try {\n      return await fn();\n    } catch (err) {\n      if (err instanceof RagSdkError && err.retryable && attempt < maxRetries) {\n        const delay = Math.min(1000 * 2 ** attempt, 30000);\n        await new Promise((r) => setTimeout(r, delay));\n        continue;\n      }\n      throw err;\n    }\n  }\n  throw new Error('unreachable');\n}\n\n// Usage\nconst result = await withRetry(() => rag.ingest.file('./report.pdf'));\n```\n\nRetryable errors include transient conditions like Qdrant connection failures (`VECTOR_CONNECTION_ERROR`), embedding rate limits (`EMBEDDING_RATE_LIMIT`), and provider overload errors. Non-retryable errors (configuration, validation, authentication) should not be retried.\n\n### Token estimation\n\nToken counts reported in `ChunkMetadata.tokenCount` and `DocumentRecord.totalTokens` use a heuristic of approximately 4 characters per token. This is a fast estimate, not a provider-specific tokenizer. Actual token counts may differ by 20--30% depending on the model and content. The chunking system uses the same heuristic for target/max token boundaries.\n\n---\n\n## Telemetry\n\nHook into SDK events and metrics for observability. Compatible with OpenTelemetry-style tracing pipelines.\n\n### Event hooks\n\n```ts\nconst rag = await createRag({\n  // ...required config...\n  telemetry: {\n    enabled: true,\n    onEvent: (event) => {\n      // event.type: 'ingestion_started' | 'ingestion_completed' | 'ocr_completed' | ...\n      // event.timestamp, event.durationMs, event.documentId, event.tenantId\n      console.log(`[${event.type}] ${event.documentId} -- ${event.durationMs}ms`);\n    },\n    onMetric: (metric) => {\n      // metric.name, metric.value, metric.unit, metric.tags\n      myMetricsExporter.record(metric.name, metric.value, metric.unit);\n    },\n  },\n});\n```\n\n### Telemetry event types\n\n| Event | Emitted when |\n|-------|-------------|\n| `ingestion_started` | Ingestion pipeline begins |\n| `ingestion_completed` | Ingestion finishes successfully |\n| `ingestion_failed` | Ingestion fails |\n| `ocr_completed` | OCR extraction finishes |\n| `ocr_failed` | OCR extraction fails |\n| `embeddings_completed` | Embedding generation finishes |\n| `embeddings_failed` | Embedding generation fails |\n| `qdrant_upsert_completed` | Chunks are indexed in Qdrant |\n| `retrieval_executed` | A retrieval query completes |\n| `answer_generation_executed` | Answer generation completes |\n\n---\n\n## API Reference\n\n### Factory\n\n| Function | Returns | Description |\n|----------|---------|-------------|\n| `createRag(config)` | `Promise<RagClient>` | Create and validate a RAG client instance (async) |\n\n### Ingestion -- `rag.ingest`\n\n| Method | Signature |\n|--------|-----------|\n| `file` | `(filePath: string, options?: IngestOptions) => Promise<IngestResult>` |\n| `buffer` | `(buffer: Buffer, fileName: string, options?: IngestOptions) => Promise<IngestResult>` |\n| `url` | `(url: string, options?: IngestOptions) => Promise<IngestResult>` |\n| `text` | `(text: string, options?: IngestOptions) => Promise<IngestResult>` |\n\n### Retrieval -- `rag.retrieve`\n\n| Method | Signature |\n|--------|-----------|\n| `retrieve` | `(query: string, options?: RetrieveOptions) => Promise<RetrieveResult>` |\n| `retrieve.hybrid` | `(query: string, options?: HybridRetrieveOptions) => Promise<RetrieveResult>` |\n\n### Answers -- `rag.answer`\n\n| Method | Signature |\n|--------|-----------|\n| `answer` | `(query: string, options?: AnswerOptions) => Promise<AnswerResult>` |\n\n### Documents -- `rag.documents`\n\n| Method | Signature |\n|--------|-----------|\n| `get` | `(documentId: string, tenantId?: string) => Promise<DocumentRecord \\| null>` |\n| `list` | `(filters?: DocumentListFilters) => Promise<DocumentRecord[]>` |\n| `delete` | `(documentId: string, tenantId?: string) => Promise<{ deleted: number }>` |\n| `reindex` | `(documentId: string, chunks: Array<...>, tenantId?: string) => Promise<{ reindexed: number }>` |\n| `updateMetadata` | `(documentId: string, patch: DocumentMetadataPatch, tenantId?: string) => Promise<void>` |\n\n### Jobs -- `rag.jobs`\n\n| Method | Signature |\n|--------|-----------|\n| `get` | `(jobId: string, tenantId: string) => Promise<JobRecord \\| null>` |\n| `list` | `(filters?: JobListFilters) => Promise<JobRecord[]>` |\n| `cancel` | `(jobId: string, tenantId: string) => Promise<JobRecord>` |\n\n### Utilities\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `rag.healthcheck()` | `Promise<{ status, details }>` | Check Qdrant connectivity and service health |\n| `rag.validateConfig()` | `{ valid, errors? }` | Re-validate the current configuration |\n| `rag.version()` | `string` | Return the SDK version |\n\n### Exported Types\n\n```ts\n// Config\nRagConfig, MistralConfig, QdrantConfig, EmbeddingConfig, ChunkingConfig,\nRetrievalConfig, AnsweringConfig, TelemetryConfig, SecurityConfig,\nDefaultsConfig, JobsConfig\nEmbeddingProviderName, AnsweringProviderName\n\n// Documents\nNormalizedDocument, NormalizedPage, NormalizedTable, NormalizedLink,\nOcrWarning, OcrProviderMetadata\n\n// Chunks\nChunkMetadata, Chunk, ChunkingResult\n\n// Citations\nCitationAnchor, Citation\n\n// Ingestion\nIngestInput, IngestFileInput, IngestBufferInput, IngestUrlInput,\nIngestTextInput, IngestOptions, IngestResult\n\n// Retrieval\nRetrieveOptions, RetrieveResult, RetrieveMatch, HybridRetrieveOptions\n\n// Answers\nAnswerOptions, AnswerResult, AnswerCitation\n\n// Jobs\nJobRecord, JobStatus, JobListFilters\n\n// Documents Management\nDocumentRecord, DocumentListFilters, DocumentMetadataPatch\n\n// Document Store\nDocumentStore, StoredDocument, DocumentStoreListFilters, InMemoryDocumentStore\n\n// Security\nSecurityContext\n\n// Telemetry\nTelemetryEvent, TelemetryEventType, MetricEntry\n\n// Enums\nProcessingMode\n\n// Errors\nRagSdkError, RagErrorDetails, RagErrorCode, RagErrorCategory\n\n// LLM Providers\nEmbeddingProvider, ChatProvider, ProviderCapability\nPROVIDER_CAPABILITIES, SUPPORTED_PROVIDERS\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}