Overview
Raily sits between your content and AI systems, ensuring every access is authorized, tracked, and monetized. This guide shows how to integrate Raily with popular LLM providers.OpenAI
GPT-4, GPT-3.5
Anthropic
Claude 3
Gemini
Custom
Any LLM
OpenAI Integration
Basic RAG Pattern
Copy
import OpenAI from 'openai';
import Raily from '@raily/sdk';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const raily = new Raily({ apiKey: process.env.RAILY_API_KEY });
async function ragQuery(userQuestion, relevantContentIds) {
// Step 1: Check access and fetch authorized content
const contextParts = [];
for (const contentId of relevantContentIds) {
const access = await raily.access.check({
contentId,
requesterId: process.env.APP_ID,
context: {
purpose: "rag",
model: "gpt-4"
}
});
if (access.allowed) {
const response = await fetch(access.contentUrl, {
headers: { Authorization: `Bearer ${access.token}` }
});
const content = await response.text();
contextParts.push(content);
}
}
if (contextParts.length === 0) {
return "I don't have access to relevant information to answer your question.";
}
// Step 2: Query OpenAI with authorized content
const completion = await openai.chat.completions.create({
model: "gpt-4",
messages: [
{
role: "system",
content: `You are a helpful assistant. Use the following context to answer questions:\n\n${contextParts.join('\n\n---\n\n')}`
},
{
role: "user",
content: userQuestion
}
]
});
return completion.choices[0].message.content;
}
// Usage
const answer = await ragQuery(
"What are the key trends in AI for 2024?",
["cnt_report_2024", "cnt_industry_analysis"]
);
With Embeddings
Copy
import OpenAI from 'openai';
import Raily from '@raily/sdk';
const openai = new OpenAI();
const raily = new Raily({ apiKey: process.env.RAILY_API_KEY });
async function semanticSearch(query, contentIds) {
// Get query embedding
const queryEmbedding = await openai.embeddings.create({
model: "text-embedding-ada-002",
input: query
});
// Search and filter by access
const results = [];
for (const contentId of contentIds) {
const access = await raily.access.check({
contentId,
requesterId: process.env.APP_ID,
context: { purpose: "inference" }
});
if (access.allowed) {
// Fetch content and its embedding
const content = await fetchContent(access.contentUrl, access.token);
const similarity = cosineSimilarity(
queryEmbedding.data[0].embedding,
content.embedding
);
results.push({
contentId,
content: content.text,
similarity
});
}
}
// Return top results
return results
.sort((a, b) => b.similarity - a.similarity)
.slice(0, 5);
}
Assistants API
Copy
import OpenAI from 'openai';
import Raily from '@raily/sdk';
const openai = new OpenAI();
const raily = new Raily({ apiKey: process.env.RAILY_API_KEY });
// Create an assistant with Raily-protected knowledge
async function createAssistantWithProtectedKnowledge(contentIds) {
// Verify access to all content
const authorizedContent = [];
for (const id of contentIds) {
const access = await raily.access.check({
contentId: id,
requesterId: "openai_assistant",
context: { purpose: "assistant_knowledge" }
});
if (access.allowed) {
const response = await fetch(access.contentUrl);
const content = await response.text();
authorizedContent.push({ id, content });
}
}
// Upload authorized content as files
const fileIds = [];
for (const item of authorizedContent) {
const file = await openai.files.create({
file: new Blob([item.content], { type: 'text/plain' }),
purpose: 'assistants'
});
fileIds.push(file.id);
}
// Create assistant with files
const assistant = await openai.beta.assistants.create({
name: "Knowledge Assistant",
model: "gpt-4-turbo-preview",
tools: [{ type: "retrieval" }],
file_ids: fileIds
});
return assistant;
}
Anthropic Integration
Basic Chat with Context
Copy
import Anthropic from '@anthropic-ai/sdk';
import Raily from '@raily/sdk';
const anthropic = new Anthropic();
const raily = new Raily({ apiKey: process.env.RAILY_API_KEY });
async function chatWithContext(userMessage, contentId) {
// Check access
const access = await raily.access.check({
contentId,
requesterId: "anthropic_app",
context: {
purpose: "inference",
model: "claude-3-opus"
}
});
if (!access.allowed) {
return {
success: false,
error: `Access denied: ${access.reason}`
};
}
// Fetch authorized content
const response = await fetch(access.contentUrl, {
headers: { Authorization: `Bearer ${access.token}` }
});
const contextContent = await response.text();
// Query Claude
const message = await anthropic.messages.create({
model: "claude-3-opus-20240229",
max_tokens: 4096,
system: `You have access to the following reference material. Use it to provide accurate, well-informed responses:\n\n${contextContent}`,
messages: [
{ role: "user", content: userMessage }
]
});
return {
success: true,
response: message.content[0].text,
usage: {
inputTokens: message.usage.input_tokens,
outputTokens: message.usage.output_tokens
}
};
}
Streaming Responses
Copy
async function streamingChatWithContent(userMessage, contentId) {
const access = await raily.access.check({
contentId,
requesterId: "anthropic_streaming_app",
context: { purpose: "inference" }
});
if (!access.allowed) {
throw new Error(access.reason);
}
const contextResponse = await fetch(access.contentUrl);
const context = await contextResponse.text();
const stream = await anthropic.messages.create({
model: "claude-3-sonnet-20240229",
max_tokens: 1024,
stream: true,
system: `Context: ${context}`,
messages: [{ role: "user", content: userMessage }]
});
// Return async iterator
return stream;
}
// Usage with streaming
const stream = await streamingChatWithContent(
"Summarize the key findings",
"cnt_research_paper"
);
for await (const event of stream) {
if (event.type === 'content_block_delta') {
process.stdout.write(event.delta.text);
}
}
Google Gemini Integration
Copy
import { GoogleGenerativeAI } from '@google/generative-ai';
import Raily from '@raily/sdk';
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY);
const raily = new Raily({ apiKey: process.env.RAILY_API_KEY });
async function geminiWithRaily(prompt, contentIds) {
// Gather authorized content
const contextParts = [];
for (const contentId of contentIds) {
const access = await raily.access.check({
contentId,
requesterId: "gemini_app",
context: { purpose: "inference", model: "gemini-pro" }
});
if (access.allowed) {
const response = await fetch(access.contentUrl);
contextParts.push(await response.text());
}
}
// Query Gemini
const model = genAI.getGenerativeModel({ model: "gemini-pro" });
const result = await model.generateContent({
contents: [{
role: "user",
parts: [{
text: `Context:\n${contextParts.join('\n\n---\n\n')}\n\nQuestion: ${prompt}`
}]
}]
});
return result.response.text();
}
LangChain Integration
Custom Retriever
Copy
from langchain.schema import BaseRetriever, Document
from raily import Raily
import requests
class RailyRetriever(BaseRetriever):
"""Custom retriever that uses Raily for access control."""
def __init__(self, content_ids: list[str], requester_id: str):
self.content_ids = content_ids
self.requester_id = requester_id
self.raily = Raily(api_key=os.environ["RAILY_API_KEY"])
def get_relevant_documents(self, query: str) -> list[Document]:
documents = []
for content_id in self.content_ids:
access = self.raily.access.check(
content_id=content_id,
requester_id=self.requester_id,
context={"purpose": "rag", "query": query}
)
if access.allowed:
response = requests.get(
access.content_url,
headers={"Authorization": f"Bearer {access.token}"}
)
documents.append(Document(
page_content=response.text,
metadata={"content_id": content_id}
))
return documents
async def aget_relevant_documents(self, query: str) -> list[Document]:
# Async implementation
return self.get_relevant_documents(query)
RAG Chain
Copy
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
# Create Raily-powered retriever
retriever = RailyRetriever(
content_ids=["cnt_doc1", "cnt_doc2", "cnt_doc3"],
requester_id="langchain_app"
)
# Build RAG chain
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(temperature=0),
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
# Query
result = qa_chain({"query": "What are the main conclusions?"})
print(result["result"])
print(f"Sources: {[doc.metadata['content_id'] for doc in result['source_documents']]}")
LlamaIndex Integration
Copy
from llama_index import VectorStoreIndex, Document
from llama_index.retrievers import BaseRetriever
from raily import Raily
class RailyDocumentLoader:
"""Load documents through Raily access control."""
def __init__(self, requester_id: str):
self.raily = Raily(api_key=os.environ["RAILY_API_KEY"])
self.requester_id = requester_id
def load(self, content_ids: list[str]) -> list[Document]:
documents = []
for content_id in content_ids:
access = self.raily.access.check(
content_id=content_id,
requester_id=self.requester_id,
context={"purpose": "indexing"}
)
if access.allowed:
response = requests.get(access.content_url)
documents.append(Document(
text=response.text,
metadata={"source": content_id}
))
return documents
# Usage
loader = RailyDocumentLoader(requester_id="llamaindex_app")
documents = loader.load(["cnt_1", "cnt_2", "cnt_3"])
# Create index
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("What are the key insights?")
print(response)
Best Practices
Cache Access Tokens
Access tokens are valid for a period. Cache and reuse them to reduce API calls.
Batch Requests
Check access for multiple content items in a single request when possible.
Handle Denials Gracefully
Always handle access denials gracefully with fallback responses.
Track Usage Context
Provide detailed context (model, purpose) for better analytics and policy matching.
Error Handling Pattern
Copy
async function safeContentAccess(contentId, requesterId) {
try {
const access = await raily.access.check({
contentId,
requesterId,
context: { purpose: "inference" }
});
if (!access.allowed) {
console.log(`Access denied: ${access.reason}`);
return {
success: false,
reason: access.reason,
retryAfter: access.retryAfter
};
}
const response = await fetch(access.contentUrl, {
headers: { Authorization: `Bearer ${access.token}` }
});
if (!response.ok) {
throw new Error(`Failed to fetch: ${response.status}`);
}
return {
success: true,
content: await response.text(),
expiresAt: access.expiresAt
};
} catch (error) {
console.error('Raily access error:', error);
return {
success: false,
reason: 'internal_error',
error: error.message
};
}
}