From openrag
Guides OpenRAG SDK integration into Python or JavaScript/TypeScript apps with installation via pip/npm/uv/yarn, env var/API key config, client setup, and code examples for chat/search.
npx claudepluginhub langflow-ai/openrag --plugin openragThis skill uses the workspace's default tool permissions.
When the user asks to integrate the OpenRAG SDK or use OpenRAG in their application, follow this workflow.
Implements retrieval-augmented generation (RAG) systems for knowledge-intensive apps, document search, Q&A, and grounding LLMs in external data. Covers embeddings, vector stores, retrieval pipelines, evaluation, with cost/prerequisite checks.
Plans and executes minimal OpenRAG installation: assesses existing setup, drafts requirements.md/todo.md, configures Docker/uvx, verifies localhost:3000/:5001/docs.
Provides production-ready patterns for LLM apps including RAG pipelines, chunking strategies, embeddings, vector DBs like Pinecone/pgvector, AI agents, LLMOps. Use for RAG implementation and agent design.
Share bugs, ideas, or general feedback.
When the user asks to integrate the OpenRAG SDK or use OpenRAG in their application, follow this workflow.
Before starting SDK integration:
http://localhost:3000, https://api.example.com)curl <base_url> or curl <base_url>/healthPackage: openrag-sdk
Installation:
pip install openrag-sdk
Or with uv:
uv add openrag-sdk
Package: openrag-sdk
Installation:
npm install openrag-sdk
Or with other package managers:
yarn add openrag-sdk
pnpm add openrag-sdk
bun add openrag-sdk
Package: openrag-mcp
For MCP integration (Model Context Protocol):
pip install openrag-mcp
Or with uvx:
uvx openrag-mcp
The SDK can be configured via environment variables or constructor arguments:
Environment Variables:
OPENRAG_API_KEY=your-api-key # Required if authentication is enabled
OPENRAG_URL=http://localhost:3000 # Optional, defaults to localhost:3000
Constructor Arguments:
from openrag_sdk import OpenRAGClient
# Using environment variables (auto-discovers OPENRAG_API_KEY and OPENRAG_URL)
client = OpenRAGClient()
# Using explicit arguments
client = OpenRAGClient(
api_key="orag_...",
base_url="https://api.example.com"
)
Similar configuration options for TypeScript:
import { OpenRAGClient } from 'openrag-sdk';
// Using environment variables
const client = new OpenRAGClient();
// Using explicit configuration
const client = new OpenRAGClient({
apiKey: 'orag_...',
baseUrl: 'https://api.example.com'
});
Python:
import asyncio
from openrag_sdk import OpenRAGClient
async def main():
# Client auto-discovers OPENRAG_API_KEY and OPENRAG_URL from environment
async with OpenRAGClient() as client:
# Simple chat
response = await client.chat.create(message="What is RAG?")
print(response.response)
print(f"Chat ID: {response.chat_id}")
# Continue conversation
followup = await client.chat.create(
message="Tell me more",
chat_id=response.chat_id
)
print(followup.response)
asyncio.run(main())
TypeScript:
import { OpenRAGClient } from 'openrag-sdk';
async function main() {
const client = new OpenRAGClient();
// Simple chat
const response = await client.chat.create({
message: "What is RAG?"
});
console.log(response.response);
console.log(`Chat ID: ${response.chatId}`);
// Continue conversation
const followup = await client.chat.create({
message: "Tell me more",
chatId: response.chatId
});
console.log(followup.response);
}
main();
Python:
async def streaming_chat():
chat_id = None
async with OpenRAGClient() as client:
# Stream responses
async for event in await client.chat.create(
message="Explain RAG",
stream=True
):
if event.type == "content":
print(event.delta, end="", flush=True)
elif event.type == "sources":
for source in event.sources:
print(f"\nSource: {source.filename}")
elif event.type == "done":
chat_id = event.chat_id
asyncio.run(streaming_chat())
Python with stream() context manager:
async def streaming_with_context():
async with OpenRAGClient() as client:
# Full event iteration
async with client.chat.stream(message="Explain RAG") as stream:
async for event in stream:
if event.type == "content":
print(event.delta, end="", flush=True)
# Access aggregated data after iteration
print(f"\nChat ID: {stream.chat_id}")
# Get final text directly
async with client.chat.stream(message="Explain RAG") as stream:
text = await stream.final_text()
print(text)
asyncio.run(streaming_with_context())
TypeScript:
async function streamingChat() {
const client = new OpenRAGClient();
const stream = await client.chat.create({
message: "Explain RAG",
stream: true
});
for await (const event of stream) {
if (event.type === 'content') {
process.stdout.write(event.delta);
} else if (event.type === 'sources') {
for (const source of event.sources) {
console.log(`\nSource: ${source.filename}`);
}
} else if (event.type === 'done') {
console.log(`\nChat ID: ${event.chatId}`);
}
}
}
Python:
async def manage_conversations():
async with OpenRAGClient() as client:
# List all conversations
conversations = await client.chat.list()
for conv in conversations.conversations:
print(f"{conv.chat_id}: {conv.title}")
if not conversations.conversations:
print("No conversations found")
return
chat_id = conversations.conversations[0].chat_id
# Get specific conversation with messages
conversation = await client.chat.get(chat_id)
for msg in conversation.messages:
print(f"{msg.role}: {msg.content}")
# Delete conversation
await client.chat.delete(chat_id)
asyncio.run(manage_conversations())
TypeScript:
async function manageConversations() {
const client = new OpenRAGClient();
// List all conversations
const conversations = await client.chat.list();
for (const conv of conversations.conversations) {
console.log(`${conv.chatId}: ${conv.title}`);
}
if (!conversations.conversations.length) {
console.log("No conversations found");
return;
}
const chatId = conversations.conversations[0].chatId;
// Get specific conversation
const conversation = await client.chat.get(chatId);
for (const msg of conversation.messages) {
console.log(`${msg.role}: ${msg.content}`);
}
// Delete conversation
await client.chat.delete(chatId);
}
Python:
async def search_knowledge():
async with OpenRAGClient() as client:
# Basic search
results = await client.search.query("document processing")
for result in results.results:
print(f"{result.filename} (score: {result.score})")
print(f"{result.text[:100]}...")
# Search with filters
from openrag_sdk import SearchFilters
results = await client.search.query(
"API documentation",
filters=SearchFilters(
data_sources=["api-docs.pdf"],
document_types=["application/pdf"]
),
limit=5,
score_threshold=0.5
)
asyncio.run(search_knowledge())
TypeScript:
async function searchKnowledge() {
const client = new OpenRAGClient();
// Basic search
const results = await client.search.query("document processing");
for (const result of results.results) {
console.log(`${result.filename} (score: ${result.score})`);
console.log(`${result.text.substring(0, 100)}...`);
}
// Search with filters
const filtered = await client.search.query("API documentation", {
filters: {
data_sources: ["api-docs.pdf"],
document_types: ["application/pdf"]
},
limit: 5,
scoreThreshold: 0.5
});
}
Python:
async def manage_documents():
async with OpenRAGClient() as client:
# Ingest a file (waits for completion by default)
result = await client.documents.ingest(file_path="./report.pdf")
print(f"Status: {result.status}")
# Ingest from file object
with open("./report.pdf", "rb") as f:
result = await client.documents.ingest(file=f, filename="report.pdf")
# Poll for completion manually
final_status = await client.documents.wait_for_task(result.task_id)
print(f"Status: {final_status.status}")
print(f"Successful files: {final_status.successful_files}")
# Delete a document
result = await client.documents.delete("report.pdf")
print(f"Success: {result.success}")
asyncio.run(manage_documents())
TypeScript:
async function manageDocuments() {
const client = new OpenRAGClient();
// Ingest a file
const result = await client.documents.ingest({
filePath: "./report.pdf"
});
console.log(`Status: ${result.status}`);
// Poll for completion
const finalStatus = await client.documents.waitForTask(result.task_id);
console.log(`Status: ${finalStatus.status}`);
console.log(`Successful files: ${finalStatus.successful_files}`);
// Delete a document
const deleteResult = await client.documents.delete("report.pdf");
console.log(`Success: ${deleteResult.success}`);
}
Python:
async def manage_settings():
async with OpenRAGClient() as client:
# Get settings
settings = await client.settings.get()
print(f"LLM Provider: {settings.agent.llm_provider}")
print(f"LLM Model: {settings.agent.llm_model}")
print(f"Embedding Model: {settings.knowledge.embedding_model}")
# Update settings
await client.settings.update({
"llm_provider": "openai",
"llm_model": "gpt-4o",
"embedding_provider": "openai",
"embedding_model": "text-embedding-3-small"
})
asyncio.run(manage_settings())
TypeScript:
async function manageSettings() {
const client = new OpenRAGClient();
// Get settings
const settings = await client.settings.get();
console.log(`LLM Provider: ${settings.agent.llmProvider}`);
console.log(`LLM Model: ${settings.agent.llmModel}`);
// Update settings
await client.settings.update({
llm_provider: "openai",
llm_model: "gpt-4o",
embedding_provider: "openai",
embedding_model: "text-embedding-3-small"
});
}
Python:
async def use_knowledge_filters():
async with OpenRAGClient() as client:
# Create a knowledge filter
result = await client.knowledge_filters.create({
"name": "Technical Docs",
"description": "Filter for technical documentation",
"queryData": {
"query": "technical",
"filters": {
"document_types": ["application/pdf"]
},
"limit": 10,
"scoreThreshold": 0.5
}
})
filter_id = result.id
# Search for filters
filters = await client.knowledge_filters.search("Technical")
for f in filters:
print(f"{f.name}: {f.description}")
# Update a filter
await client.knowledge_filters.update(filter_id, {
"description": "Updated description"
})
# Delete a filter
await client.knowledge_filters.delete(filter_id)
# Use filter in chat
response = await client.chat.create(
message="Explain the API",
filter_id=filter_id
)
# Use filter in search
results = await client.search.query(
"API endpoints",
filter_id=filter_id
)
asyncio.run(use_knowledge_filters())
TypeScript:
async function useKnowledgeFilters() {
const client = new OpenRAGClient();
// Create a knowledge filter
const result = await client.knowledgeFilters.create({
name: "Technical Docs",
description: "Filter for technical documentation",
queryData: {
query: "technical",
filters: {
documentTypes: ["application/pdf"]
},
limit: 10,
scoreThreshold: 0.5
}
});
const filterId = result.id;
// Use filter in chat
const response = await client.chat.create({
message: "Explain the API",
filterId: filterId
});
// Use filter in search
const results = await client.search.query({
query: "API endpoints",
filterId: filterId
});
}
from openrag_sdk import (
OpenRAGError,
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError
)
async def handle_errors():
try:
async with OpenRAGClient() as client:
response = await client.chat.create(message="Hello")
except AuthenticationError as e:
print(f"Invalid API key: {e.message}")
except NotFoundError as e:
print(f"Resource not found: {e.message}")
except ValidationError as e:
print(f"Invalid request: {e.message}")
except RateLimitError as e:
print(f"Rate limited: {e.message}")
except ServerError as e:
print(f"Server error: {e.message} (status: {e.status_code})")
except OpenRAGError as e:
print(f"API error: {e.message} (status: {e.status_code})")
import {
OpenRAGClient,
OpenRAGError,
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError
} from 'openrag-sdk';
async function handleErrors() {
try {
const client = new OpenRAGClient();
const response = await client.chat.create({ message: "Hello" });
} catch (error) {
if (error instanceof AuthenticationError) {
console.error(`Invalid API key: ${error.message}`);
} else if (error instanceof NotFoundError) {
console.error(`Resource not found: ${error.message}`);
} else if (error instanceof ValidationError) {
console.error(`Invalid request: ${error.message}`);
} else if (error instanceof RateLimitError) {
console.error(`Rate limited: ${error.message}`);
} else if (error instanceof ServerError) {
console.error(`Server error: ${error.message}`);
} else if (error instanceof OpenRAGError) {
console.error(`API error: ${error.message}`);
}
}
}
from fastapi import FastAPI, HTTPException
from openrag_sdk import OpenRAGClient
from pydantic import BaseModel
app = FastAPI()
client = OpenRAGClient()
class ChatRequest(BaseModel):
message: str
chat_id: str | None = None
@app.post("/api/chat")
async def chat(request: ChatRequest):
try:
response = await client.chat.create(
message=request.message,
chat_id=request.chat_id
)
return {
"answer": response.response,
"sources": [{"filename": s.filename, "score": s.score} for s in response.sources],
"chat_id": response.chat_id
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/search")
async def search(query: str, limit: int = 10):
try:
results = await client.search.query(query, limit=limit)
return {
"results": [
{
"filename": r.filename,
"text": r.text,
"score": r.score
}
for r in results.results
]
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
import express from 'express';
import { OpenRAGClient } from 'openrag-sdk';
const app = express();
const client = new OpenRAGClient();
app.use(express.json());
app.post('/api/chat', async (req, res) => {
try {
const { message, chatId } = req.body;
const response = await client.chat.create({
message,
chatId
});
res.json({
answer: response.response,
sources: response.sources.map(s => ({
filename: s.filename,
score: s.score
})),
chatId: response.chatId
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/api/search', async (req, res) => {
try {
const { query, limit = 10 } = req.query;
const results = await client.search.query({
query: query as string,
limit: Number(limit)
});
res.json({
results: results.results.map(r => ({
filename: r.filename,
text: r.text,
score: r.score
}))
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3001, () => {
console.log('Server running on port 3001');
});
import { useState } from 'react';
import { OpenRAGClient } from 'openrag-sdk';
// Note: In production, proxy API calls through your backend
// to avoid exposing API keys in the browser
const client = new OpenRAGClient({
baseUrl: process.env.REACT_APP_OPENRAG_URL
});
function ChatComponent() {
const [message, setMessage] = useState('');
const [chatId, setChatId] = useState<string | null>(null);
const [response, setResponse] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
const result = await client.chat.create({
message,
chatId,
limit: 5
});
setResponse(result.response);
setChatId(result.chatId);
setMessage('');
} catch (error) {
console.error('Chat error:', error);
} finally {
setLoading(false);
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Ask a question..."
disabled={loading}
/>
<button type="submit" disabled={loading}>
{loading ? 'Sending...' : 'Send'}
</button>
</form>
{response && (
<div className="response">
{response}
</div>
)}
</div>
);
}
import { useState } from 'react';
import { OpenRAGClient } from 'openrag-sdk';
function StreamingChat() {
const [message, setMessage] = useState('');
const [response, setResponse] = useState('');
const [streaming, setStreaming] = useState(false);
const client = new OpenRAGClient();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setStreaming(true);
setResponse('');
try {
const stream = await client.chat.create({
message,
stream: true
});
for await (const event of stream) {
if (event.type === 'content') {
setResponse(prev => prev + event.delta);
}
}
} catch (error) {
console.error('Streaming error:', error);
} finally {
setStreaming(false);
setMessage('');
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
disabled={streaming}
/>
<button type="submit" disabled={streaming}>
{streaming ? 'Streaming...' : 'Send'}
</button>
</form>
<div className="response">
{response}
{streaming && <span className="cursor">▊</span>}
</div>
</div>
);
}
Never expose API keys in client-side code
Use HTTPS in production
Validate and sanitize inputs
Implement proper error handling
Follow OWASP guidelines
import pytest
from openrag_sdk import OpenRAGClient
@pytest.fixture
async def client():
async with OpenRAGClient() as client:
yield client
@pytest.mark.asyncio
async def test_chat_basic(client):
response = await client.chat.create(message="Hello")
assert response.response is not None
assert isinstance(response.sources, list)
assert response.chat_id is not None
@pytest.mark.asyncio
async def test_search_with_filters(client):
results = await client.search.query(
"test",
filters={"document_types": ["application/pdf"]}
)
assert isinstance(results.results, list)
import { describe, it, expect } from 'vitest';
import { OpenRAGClient } from 'openrag-sdk';
describe('OpenRAG SDK', () => {
const client = new OpenRAGClient();
it('should chat successfully', async () => {
const response = await client.chat.create({
message: 'Hello'
});
expect(response.response).toBeDefined();
expect(response.sources).toBeInstanceOf(Array);
expect(response.chatId).toBeDefined();
});
it('should search with filters', async () => {
const results = await client.search.query({
query: 'test',
filters: {
documentTypes: ['application/pdf']
}
});
expect(results.results).toBeInstanceOf(Array);
});
});
http://localhost:3000 or https://api.example.com)curl <base_url> or curl <base_url>/healthlimit values (don't retrieve more sources than needed)score_threshold to filter low-quality resultsscore_threshold to filter irrelevant resultsEnvironment configuration
Health checks
Monitoring and logging
Fallback handling
Scaling
After integration, document:
Before considering integration complete: