npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@opensaas/stack-rag

v0.18.2

Published

RAG and AI embeddings integration for OpenSaas Stack

Readme

@opensaas/stack-rag

RAG (Retrieval-Augmented Generation) and AI embeddings integration for OpenSaas Stack.

Turn your OpenSaas app into a knowledge base with semantic search capabilities powered by vector embeddings.

Features

  • 🤖 Multiple Embedding Providers: OpenAI, Ollama (local), or bring your own
  • 🗄️ Flexible Storage: pgvector, SQLite VSS, or JSON-based (for development)
  • 🔍 Semantic Search: Natural language queries with relevance scoring
  • 🔐 Access Control: All searches respect your existing access control rules
  • Automatic Embeddings: Auto-generate embeddings when content changes
  • 🛠️ MCP Integration: Semantic search tools for AI assistants
  • 📊 Multiple Abstraction Levels: From automatic "magic" to low-level control
  • ✂️ Text Chunking: Multiple strategies for splitting long documents
  • 🚀 Batch Processing: Rate-limited batch embedding generation with progress tracking

Installation

pnpm add @opensaas/stack-rag

# Install your chosen embedding provider
pnpm add openai  # For OpenAI embeddings
# OR use Ollama (no package needed - just run Ollama locally)

Quick Start

1. Configure RAG in your OpenSaas app

// opensaas.config.ts
import { config, list } from '@opensaas/stack-core'
import { text } from '@opensaas/stack-core/fields'
import { ragPlugin, openaiEmbeddings, pgvectorStorage } from '@opensaas/stack-rag'
import { searchable } from '@opensaas/stack-rag/fields'

export default config({
  plugins: [
    ragPlugin({
      provider: openaiEmbeddings({
        apiKey: process.env.OPENAI_API_KEY!,
        model: 'text-embedding-3-small',
      }),
      storage: pgvectorStorage(),
    }),
  ],
  db: {
    provider: 'postgresql',
    url: process.env.DATABASE_URL!,
  },
  lists: {
    Article: list({
      fields: {
        title: text({ validation: { isRequired: true } }),
        // Use searchable() wrapper for automatic embedding generation
        content: searchable(text({ validation: { isRequired: true } }), {
          provider: 'openai',
          dimensions: 1536,
        }),
      },
    }),
  },
})

What's happening:

  • The searchable() wrapper automatically creates a contentEmbedding field
  • Embeddings are auto-generated whenever content changes
  • The embedding field respects all your existing access control rules

2. Generate schema and push to database

pnpm generate
pnpm db:push

3. Create content (embeddings generated automatically)

import { getContext } from '@/.opensaas/context'

const context = await getContext()

// Embedding is automatically generated from content
await context.db.article.create({
  data: {
    title: 'Introduction to AI',
    content: 'Artificial intelligence is...',
    // No need to manually create embedding - it's automatic!
  },
})

4. Perform semantic search

import { createEmbeddingProvider, createVectorStorage } from '@opensaas/stack-rag'

export async function searchArticles(query: string) {
  const context = await getContext()

  // Generate embedding for search query
  const provider = createEmbeddingProvider({
    type: 'openai',
    apiKey: process.env.OPENAI_API_KEY!,
  })
  const queryVector = await provider.embed(query)

  // Search for similar articles
  const storage = createVectorStorage({ type: 'json' })
  const results = await storage.search('Article', 'contentEmbedding', queryVector, {
    limit: 10,
    minScore: 0.7,
    context, // Access control enforced
  })

  return results
}

Local Development with Ollama

For local development without API costs:

import { config, list } from '@opensaas/stack-core'
import { ragPlugin, ollamaEmbeddings, jsonStorage } from '@opensaas/stack-rag'

export default config({
  plugins: [
    ragPlugin({
      provider: ollamaEmbeddings({
        baseURL: 'http://localhost:11434',
        model: 'nomic-embed-text',
      }),
      storage: jsonStorage(), // No database extensions needed
    }),
  ],
  // ... rest of config
})

First, install and run Ollama:

# Install Ollama from https://ollama.ai
ollama pull nomic-embed-text
ollama serve

Storage Backends

JSON Storage (Development)

Good for development and small datasets. No database extensions needed.

storage: jsonStorage()

pgvector (Production PostgreSQL)

Best for production apps using PostgreSQL. Requires pgvector extension.

storage: pgvectorStorage({ distanceFunction: 'cosine' })

Setup:

CREATE EXTENSION vector;

SQLite VSS (SQLite)

Good for SQLite-based apps. Requires sqlite-vss extension.

storage: sqliteVssStorage({ distanceFunction: 'cosine' })

Field Configuration Patterns

High-Level: searchable() Wrapper (Recommended)

The easiest way to add semantic search to any field:

import { searchable } from '@opensaas/stack-rag/fields'

fields: {
  content: searchable(text({ validation: { isRequired: true } }), {
    provider: 'openai',
    dimensions: 1536,
  })
}

What it does:

  • Automatically creates a companion contentEmbedding field
  • Links it to the source field (content)
  • Auto-generates embeddings when content changes
  • Clean, concise syntax

Options:

type SearchableOptions = {
  provider?: string // Embedding provider (e.g., 'openai', 'ollama')
  dimensions?: number // Vector dimensions (default: 1536)
  chunking?: ChunkingConfig // Text chunking configuration
  embeddingFieldName?: string // Custom embedding field name (default: `${fieldName}Embedding`)
}

Custom embedding field name:

fields: {
  body: searchable(text(), {
    provider: 'openai',
    embeddingFieldName: 'bodyVector', // Instead of 'bodyEmbedding'
  })
}

Low-Level: Manual embedding() Field

For advanced use cases where you need more control:

import { embedding } from '@opensaas/stack-rag/fields'

fields: {
  content: text({ validation: { isRequired: true } }),
  contentEmbedding: embedding({
    sourceField: 'content',
    provider: 'openai',
    dimensions: 1536,
    autoGenerate: true,
  })
}

When to use manual pattern:

  • Need access to the embedding field in your schema
  • Want to store embeddings without a source field
  • Building custom embedding pipelines
  • Need field-level hooks on the embedding field

Both patterns are fully supported and can be used interchangeably.

MCP Integration

Automatic semantic search tools for AI assistants:

import { config, list } from '@opensaas/stack-core'
import { text } from '@opensaas/stack-core/fields'
import { ragPlugin, openaiEmbeddings } from '@opensaas/stack-rag'
import { embedding } from '@opensaas/stack-rag/fields'

export default config({
  plugins: [
    ragPlugin({
      provider: openaiEmbeddings({ apiKey: process.env.OPENAI_API_KEY! }),
      enableMcpTools: true, // Enables semantic_search_article tool
    }),
  ],
  mcp: {
    enabled: true,
    auth: { type: 'better-auth', loginPage: '/sign-in' },
  },
  lists: {
    Article: list({
      fields: {
        content: text(),
        contentEmbedding: embedding({
          sourceField: 'content',
          autoGenerate: true,
        }),
      },
    }),
  },
})

Runtime Utilities

The @opensaas/stack-rag/runtime package provides high-level utilities for common RAG operations.

Semantic Search

Simplified API that handles embedding generation and search in one call:

import { semanticSearch } from '@opensaas/stack-rag/runtime'
import { createEmbeddingProvider, createVectorStorage } from '@opensaas/stack-rag'
import { getContext } from '@/.opensaas/context'

const results = await semanticSearch({
  listKey: 'Article',
  fieldName: 'contentEmbedding',
  query: 'articles about machine learning',
  provider: createEmbeddingProvider({ type: 'openai', apiKey: process.env.OPENAI_API_KEY! }),
  storage: createVectorStorage({ type: 'pgvector' }),
  context: await getContext(),
  limit: 10,
  minScore: 0.7,
})

Find Similar Items

Find items similar to a given item by ID:

import { findSimilar } from '@opensaas/stack-rag/runtime'

const similar = await findSimilar({
  listKey: 'Article',
  fieldName: 'contentEmbedding',
  itemId: 'article-123',
  storage: createVectorStorage({ type: 'pgvector' }),
  context: await getContext(),
  limit: 5,
  excludeSelf: true, // Don't include the source article
})

Text Chunking

Split long documents into smaller chunks for embedding:

import { chunkText } from '@opensaas/stack-rag/runtime'

// Recursive chunking (respects paragraph/sentence boundaries)
const chunks = chunkText(longDocument, {
  strategy: 'recursive',
  chunkSize: 1000,
  chunkOverlap: 200,
})

// Sentence-based chunking (preserves sentences)
const chunks = chunkText(document, {
  strategy: 'sentence',
  chunkSize: 500,
  chunkOverlap: 100,
})

// Token-aware chunking (for token limits)
const chunks = chunkText(document, {
  strategy: 'token-aware',
  tokenLimit: 500, // ~500 tokens per chunk
  chunkOverlap: 50,
})

Batch Processing with Rate Limiting

Process large batches of texts with automatic rate limiting:

import { batchProcess } from '@opensaas/stack-rag/runtime'

const result = await batchProcess({
  provider: createEmbeddingProvider({ type: 'openai', apiKey: process.env.OPENAI_API_KEY! }),
  texts: largeArrayOfTexts,
  batchSize: 10,
  rateLimit: 60, // 60 requests per minute
  onProgress: (progress) => {
    console.log(`Progress: ${progress.percentage}% (${progress.processed}/${progress.total})`)
  },
})

console.log(`Successfully processed: ${result.stats.successful}`)
console.log(`Failed: ${result.stats.failed}`)

Generate Embeddings with Chunking

Generate embeddings for long texts with automatic chunking:

import { generateEmbedding } from '@opensaas/stack-rag/runtime'

// Single embedding
const embedding = await generateEmbedding({
  provider: createEmbeddingProvider({ type: 'openai', apiKey: process.env.OPENAI_API_KEY! }),
  text: 'Short text',
})

// Chunked embeddings for long documents
const chunkedEmbeddings = await generateEmbedding({
  provider: createEmbeddingProvider({ type: 'openai', apiKey: process.env.OPENAI_API_KEY! }),
  text: veryLongDocument,
  enableChunking: true,
  chunking: { chunkSize: 1000, chunkOverlap: 200 },
})

// Each chunk has its embedding
for (const { chunk, embedding } of chunkedEmbeddings) {
  console.log(`Chunk ${chunk.index}: ${chunk.text.substring(0, 50)}...`)
  await saveChunkEmbedding(chunk, embedding)
}

API Reference

Main Exports (@opensaas/stack-rag)

  • ragPlugin(config) - RAG plugin for OpenSaas Stack (v0.2.0+)
  • openaiEmbeddings(config) - OpenAI embedding provider helper
  • ollamaEmbeddings(config) - Ollama embedding provider helper
  • pgvectorStorage(config) - pgvector storage helper
  • sqliteVssStorage(config) - SQLite VSS storage helper
  • jsonStorage() - JSON-based storage helper

Field Types (@opensaas/stack-rag/fields)

  • embedding(options) - Vector embedding field type

Providers (@opensaas/stack-rag/providers)

  • createEmbeddingProvider(config) - Factory for creating embedding providers
  • registerEmbeddingProvider(type, factory) - Register custom providers

Storage (@opensaas/stack-rag/storage)

  • createVectorStorage(config) - Factory for creating storage backends
  • registerVectorStorage(type, factory) - Register custom storage backends

Runtime Utilities (@opensaas/stack-rag/runtime)

  • semanticSearch(options) - High-level semantic search
  • findSimilar(options) - Find similar items by ID
  • chunkText(text, options) - Text chunking utilities
  • generateEmbedding(options) - Generate embeddings with chunking support
  • generateEmbeddings(options) - Batch embedding generation
  • batchProcess(options) - Batch processing with rate limiting
  • RateLimiter - Rate limiting utility class
  • ProcessingQueue - Concurrent processing queue

Documentation

See CLAUDE.md for comprehensive documentation including:

  • All abstraction levels (high-level to low-level)
  • Custom embedding providers
  • Custom storage backends
  • Text chunking strategies
  • Performance optimization
  • Testing patterns
  • Migration guides

Examples

See examples/rag-demo for a complete working example with:

  • Document search
  • Chatbot with knowledge base
  • MCP integration
  • Multiple embedding providers

Repository

  • GitHub: https://github.com/OpenSaasAU/stack
  • Docs: https://stack.opensaas.au/
  • Issues: https://github.com/OpenSaasAU/stack/issues

License

MIT