RAG
Chunking and semantic retrieval
git clone https://github.com/bijectionhq/rag.git
cd rag
git checkout f2395882af1c729f0e6e4aeba13b44d823caeceeBijection repository access is required. Request access.
Description
Chunking and semantic retrieval
Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.
This package has a pinned source revision in Bijection’s workspace. Source availability does not establish npm publication or runtime qualification.
Getting started
- Get the source at the revision above and read its package documentation.
- Check the required Bijection SDK version, dependencies, and external service credentials.
- Follow the package setup instructions and the guide to using components.
- Exercise the component in your deployment according to your project’s verification rules.
Open source
Maintained source: bijectionhq/rag. Based on convex/rag. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.
From the upstream publisher
Convex component for RAG: chunk and embed documents, then run semantic search to build LLM context.
Demo video
Upstream overview
From the upstream package documentation.
A semantic search component that implements Retrieval-Augmented Generation (RAG) for AI applications. It automatically chunks text content, generates embeddings using AI SDK models, and provides vector-based similarity search across namespaced content. Includes features like custom filtering, importance weighting, chunk context expansion, and graceful content migration for production RAG workflows.
Use cases
From the upstream package documentation.
- Building AI chatbots that need to search through documentation, knowledge bases, or user-specific content to provide contextual responses
- Creating document Q&A systems where users upload files and ask questions that require semantic search across the content with proper context
- Implementing multi-tenant RAG where different users or organizations need isolated search namespaces with custom filtering by document type, category, or metadata
- Adding semantic search to existing apps that need to surface relevant content based on user queries rather than exact keyword matches
- Building AI agents that need to retrieve and synthesize information from large content repositories before generating responses
How it works
From the upstream package documentation.
The component integrates with your Convex app through the standard component pattern, requiring a convex.config.ts setup and creating a RAG instance with your chosen embedding model from AI SDK. Content is added via the add() method which automatically chunks text and generates embeddings, organizing everything into namespaces for isolation.
Search operates through the search() method which performs vector similarity matching and returns results with configurable score thresholds, chunk context expansion, and custom filtering. The component handles the complexity of embedding generation, vector storage, and result formatting, providing both raw results and formatted text ready for LLM consumption.
For full RAG workflows, the generateText() method combines search and LLM generation in one call, automatically retrieving relevant context and formatting it for the language model. The component supports graceful content replacement using keys, allowing you to update documents without disrupting active searches.
Upstream package FAQ
What embedding models does the Convex RAG component support?
The @convex-dev/rag component supports OpenAI's text embedding models and other popular embedding providers through configurable adapters. You can specify which model to use when initializing the RAG system in your Convex functions.
How does RAG handle document updates and re-indexing?
The Convex RAG component automatically handles incremental updates through Convex's reactive data layer. When documents change, only the affected embeddings are regenerated, and the vector index updates in real-time without full re-indexing.
Can I customize the document chunking strategy for RAG?
Yes, the @convex-dev/rag component provides configurable chunking strategies including fixed-size, semantic, and custom splitting methods. You can adjust chunk size, overlap, and splitting logic based on your document types and use case requirements.
Does the RAG component work with streaming LLM responses?
The Convex RAG component integrates seamlessly with streaming LLM APIs by providing the retrieved context upfront. You can use the search results to augment your prompts before streaming responses through Convex actions or HTTP endpoints.
Use with agents and CLI
Give your coding agent the component’s source identity and setup guidance.
Help me integrate RAG with Bijection.
Read: https://bijection.com/components/rag/SKILL.md
Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.Upstream README.md
View repository ↗Documentation from convex/rag, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.
A component for semantic search, usually used to look up context for LLMs. Use with an Agent for Retrieval-Augmented Generation (RAG).
✨ Key Features
- Add Content: Add or replace content with text chunks and embeddings.
- Semantic Search: Vector-based search using configurable embedding models
- Namespaces: Organize content into namespaces for per-user search.
- Custom Filtering: Filter content with custom indexed fields.
- Importance Weighting: Weight content by providing a 0 to 1 "importance".
- Chunk Context: Get surrounding chunks for better context.
- Graceful Migrations: Migrate content or whole namespaces without disruption.
Found a bug? Feature request? File it here.
Installation
npm install @convex-dev/rag ai
The AI SDK (ai@^7) is a peer dependency, so your app and
the component always share one copy.
Create a convex.config.ts file in your app's convex/ folder and install the
component by calling use:
// convex/convex.config.ts
import { defineApp } from "convex/server";
import rag from "@convex-dev/rag/convex.config.js";
const app = defineApp();
app.use(rag);
export default app;
Run npx convex codegen if npx convex dev isn't already running.
Basic Setup
// convex/example.ts
import { components } from "./_generated/api";
import { RAG } from "@convex-dev/rag";
// Any AI SDK model that supports embeddings will work.
import { openai } from "@ai-sdk/openai";
const rag = new RAG(components.rag, {
textEmbeddingModel: openai.embedding("text-embedding-3-small"),
embeddingDimension: 1536, // Needs to match your embedding model
});
Add context to RAG
Add content with text chunks. Each call to add will create a new entry. It
will embed the chunks automatically if you don't provide them.
export const add = action({
args: { text: v.string() },
handler: async (ctx, { text }) => {
// Add the text to a namespace shared by all users.
await rag.add(ctx, {
namespace: "all-users",
text,
});
},
});
See below for how to chunk the text yourself or add content asynchronously, e.g. to handle large files.
Semantic Search
Search across content with vector similarity
textis a string with the full content of the results, for convenience. It is in order of the entries, with titles at each entry boundary, and separators between non-sequential chunks. See below for more details.resultsis an array of matching chunks with scores and more metadata.entriesis an array of the entries that matched the query. Each result has aentryIdreferencing one of these source entries.usagecontains embedding token usage information. Will be{ tokens: 0 }if no embedding was performed (e.g. when passing pre-computed embeddings).
export const search = action({
args: {
query: v.string(),
},
handler: async (ctx, args) => {
const { results, text, entries, usage } = await rag.search(ctx, {
namespace: "global",
query: args.query,
limit: 10,
vectorScoreThreshold: 0.5, // Only return results with a score >= 0.5
});
return { results, text, entries, usage };
},
});
Generate a response based on RAG context
Once you have searched for the context, you can use it with an LLM.
Generally you'll already be using something to make LLM requests, e.g. the Agent Component, which tracks the message history for you. See the Agent Component docs for more details on doing RAG with the Agent Component.
However, if you just want a one-off response, you can use the generateText
function as a convenience.
This will automatically search for relevant entries and use them as context for the LLM, using default formatting.
The arguments to generateText are compatible with all arguments to
generateText from the AI SDK.
export const askQuestion = action({
args: {
prompt: v.string(),
},
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
const { text, context } = await rag.generateText(ctx, {
search: { namespace: userId, limit: 10 },
prompt: args.prompt,
model: openai.chat("gpt-4o-mini"),
});
return { answer: text, context };
},
Note: You can specify any of the search options available on rag.search.
Filtered Search
You can provide filters when adding content and use them to search. To do this, you'll need to give the RAG component a list of the filter names. You can optionally provide a type parameter for type safety (no runtime validation).
Note: these filters can be OR'd together when searching. In order to get an AND,
you provide a filter with a more complex value, such as categoryAndType below.
// convex/example.ts
import { components } from "./_generated/api";
import { RAG } from "@convex-dev/rag";
// Any AI SDK model that supports embeddings will work.
import { openai } from "@ai-sdk/openai";
// Optional: Add type safety to your filters.
type FilterTypes = {
category: string;
contentType: string;
categoryAndType: { category: string; contentType: string };
};
const rag = new RAG<FilterTypes>(components.rag, {
textEmbeddingModel: openai.embedding("text-embedding-3-small"),
embeddingDimension: 1536, // Needs to match your embedding model
filterNames: ["category", "contentType", "categoryAndType"],
});
Adding content with filters:
await rag.add(ctx, {
namespace: "global",
text,
filterValues: [
{ name: "category", value: "news" },
{ name: "contentType", value: "article" },
{
name: "categoryAndType",
value: { category: "news", contentType: "article" },
},
],
});
Search with metadata filters:
export const searchForNewsOrSports = action({
args: {
query: v.string(),
},
handler: async (ctx, args) => {
const userId = await getUserId(ctx);
if (!userId) throw new Error("Unauthorized");
const results = await rag.search(ctx, {
namespace: userId,
query: args.query,
filters: [
{ name: "category", value: "news" },
{ name: "category", value: "sports" },
],
limit: 10,
});
return results;
},
});
Add surrounding chunks to results for context
Instead of getting just the single matching chunk, you can request surrounding chunks so there's more context to the result.
Note: If there are results that have overlapping ranges, it will not return duplicate chunks, but instead give priority to adding the "before" context to each chunk. For example if you requested 2 before and 1 after, and your results were for the same entryId indexes 1, 4, and 7, the results would be:
[
// Only one before chunk available, and leaves chunk2 for the next result.
{ order: 1, content: [chunk0, chunk1], startOrder: 0, ... },
// 2 before chunks available, but leaves chunk5 for the next result.
{ order: 4, content: [chunk2, chunk3, chunk4], startOrder: 2, ... },
// 2 before chunks available, and includes one after chunk.
{ order: 7, content: [chunk5, chunk6, chunk7, chunk8], startOrder: 5, ... },
]
export const searchWithContext = action({
args: {
query: v.string(),
userId: v.string(),
},
handler: async (ctx, args) => {
const { results, text, entries, usage } = await rag.search(ctx, {
namespace: args.userId,
query: args.query,
chunkContext: { before: 2, after: 1 }, // Include 2 chunks before, 1 after
limit: 5,
});
return { results, text, entries, usage };
},
});