allow setting up an internal organization which accesses a separate llamacloud project

This commit is contained in:
Zhaoqi Li
2025-08-28 16:07:54 -07:00
parent 5cdc7f6933
commit 5823daf249
11 changed files with 78 additions and 21 deletions
+3
View File
@@ -80,6 +80,9 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key"
# AI Services
OPENAI_API_KEY="your-openai-api-key"
LLAMACLOUD_API_KEY="your-llamacloud-api-key"
# Optional: Internal API key and domain for internal users
# LLAMACLOUD_API_KEY_INTERNAL="your-internal-llamacloud-api-key"
# INTERNAL_EMAIL_DOMAIN="@yourdomain.com" # Defaults to @runllama.ai
# App
NEXT_PUBLIC_APP_URL="http://localhost:3000"
+5
View File
@@ -82,6 +82,9 @@ OPENAI_API_KEY="your-openai-api-key"
# LlamaCloud
LLAMACLOUD_API_KEY="your-llamacloud-api-key"
# Optional: Internal API key and domain for internal users
# LLAMACLOUD_API_KEY_INTERNAL="your-internal-llamacloud-api-key"
# INTERNAL_EMAIL_DOMAIN="@yourdomain.com" # Defaults to @runllama.ai
# App Configuration
NEXT_PUBLIC_APP_URL="http://localhost:3000"
@@ -217,6 +220,8 @@ NEXT_PUBLIC_SUPABASE_URL="your-supabase-url"
NEXT_PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key"
OPENAI_API_KEY="your-openai-api-key"
LLAMACLOUD_API_KEY="your-llamacloud-api-key"
# LLAMACLOUD_API_KEY_INTERNAL="your-internal-llamacloud-api-key" # Optional: for internal users
# INTERNAL_EMAIL_DOMAIN="@yourdomain.com" # Optional: defaults to @runllama.ai
NEXT_PUBLIC_APP_URL="https://your-domain.com"
```
+3 -1
View File
@@ -5,6 +5,7 @@ import { NextRequest } from 'next/server';
import { organizationService } from '@/lib/organization-service';
import { db } from '@/lib/db';
import { LlamaIndexService } from '@/lib/llama-index-service';
import { getLlamaCloudApiKey } from '@/lib/env';
export async function POST(request: NextRequest) {
console.log('🎯 Multi-step API route called');
@@ -117,8 +118,9 @@ export async function POST(request: NextRequest) {
} else {
console.log(`📋 Using index names: ${indexNames.join(', ')}`);
const apiKey = getLlamaCloudApiKey(currentUser.email);
const llamaIndexService = new LlamaIndexService({
apiKey: process.env.LLAMACLOUD_API_KEY!,
apiKey: apiKey,
projectName: project.organization.llamaCloudProjectName || 'Default',
indexNames: indexNames,
});
+8 -3
View File
@@ -1,6 +1,7 @@
import { NextRequest } from 'next/server';
import { apiHandler } from '@/lib/middleware/api-handler';
import { env, validateEnv } from '@/lib/env';
import { env, validateEnv, getLlamaCloudApiKey } from '@/lib/env';
import { organizationService } from '@/lib/organization-service';
export async function GET(request: NextRequest) {
return apiHandler(async () => {
@@ -13,19 +14,23 @@ export async function GET(request: NextRequest) {
}
try {
// Get current user to determine which API key to use
const currentUser = await organizationService.getCurrentUser();
const apiKey = getLlamaCloudApiKey(currentUser?.email);
// Fetch projects and organizations from LlamaCloud
const [projectsResponse, organizationsResponse] = await Promise.all([
fetch('https://api.cloud.llamaindex.ai/api/v1/projects', {
method: 'GET',
headers: {
'Authorization': `Bearer ${env.LLAMACLOUD_API_KEY}`,
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
}),
fetch('https://api.cloud.llamaindex.ai/api/v1/organizations', {
method: 'GET',
headers: {
'Authorization': `Bearer ${env.LLAMACLOUD_API_KEY}`,
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
})
+8 -5
View File
@@ -2,7 +2,7 @@ import { NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { organizationService } from '@/lib/organization-service';
import { llamaCloudConnectionService } from '@/lib/services/llamacloud-connection-service';
import { env, validateEnv } from '@/lib/env';
import { env, validateEnv, getLlamaCloudApiKey } from '@/lib/env';
export async function GET() {
@@ -76,24 +76,27 @@ export async function GET() {
}
// Helper function to fetch available LlamaCloud projects
async function fetchLlamaCloudProjects() {
async function fetchLlamaCloudProjects(userEmail?: string) {
try {
if (!validateEnv()) {
return [];
}
// Get the appropriate API key based on user's email
const apiKey = getLlamaCloudApiKey(userEmail);
const [projectsResponse, organizationsResponse] = await Promise.all([
fetch('https://api.cloud.llamaindex.ai/api/v1/projects', {
method: 'GET',
headers: {
'Authorization': `Bearer ${env.LLAMACLOUD_API_KEY}`,
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
}),
fetch('https://api.cloud.llamaindex.ai/api/v1/organizations', {
method: 'GET',
headers: {
'Authorization': `Bearer ${env.LLAMACLOUD_API_KEY}`,
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
})
@@ -164,7 +167,7 @@ export async function POST(request: Request) {
// Attempt automatic LlamaCloud connection if there's exactly one project
let llamaCloudConnectionResult = null;
try {
const availableProjects = await fetchLlamaCloudProjects();
const availableProjects = await fetchLlamaCloudProjects(currentUser.email);
if (availableProjects.length === 1) {
const project = availableProjects[0];
@@ -1,7 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { organizationService } from '@/lib/organization-service';
import { env } from '@/lib/env';
import { env, getLlamaCloudApiKey } from '@/lib/env';
// GET /api/projects/[projectId]/indexes - Get project indexes and available indexes
export async function GET(
@@ -71,11 +71,13 @@ export async function GET(
// Fetch available indexes from LlamaCloud
try {
// Get the appropriate API key based on user's email
const apiKey = getLlamaCloudApiKey(currentUser.email);
const pipelinesResponse = await fetch('https://api.cloud.llamaindex.ai/api/v1/pipelines', {
method: 'GET',
headers: {
'Authorization': `Bearer ${env.LLAMACLOUD_API_KEY}`,
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
});
@@ -240,10 +242,13 @@ export async function POST(
// Fetch index names from LlamaCloud to validate and get names
try {
// Get the appropriate API key based on user's email
const apiKey = getLlamaCloudApiKey(currentUser.email);
const pipelinesResponse = await fetch('https://api.cloud.llamaindex.ai/api/v1/pipelines', {
method: 'GET',
headers: {
'Authorization': `Bearer ${env.LLAMACLOUD_API_KEY}`,
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
});
+13
View File
@@ -1,6 +1,8 @@
// Environment variables configuration
export const env = {
LLAMACLOUD_API_KEY: process.env.LLAMACLOUD_API_KEY || '',
LLAMACLOUD_API_KEY_INTERNAL: process.env.LLAMACLOUD_API_KEY_INTERNAL || '',
INTERNAL_EMAIL_DOMAIN: process.env.INTERNAL_EMAIL_DOMAIN || '@runllama.ai',
};
// Function to validate required environment variables
@@ -22,4 +24,15 @@ export function validateEnv() {
}
return true;
}
// Helper function to get the appropriate LlamaCloud API key based on user email
export function getLlamaCloudApiKey(userEmail?: string | null): string {
// If user has internal email domain and internal key is configured, use internal key
if (userEmail?.endsWith(env.INTERNAL_EMAIL_DOMAIN) && env.LLAMACLOUD_API_KEY_INTERNAL) {
return env.LLAMACLOUD_API_KEY_INTERNAL;
}
// Otherwise, use the regular API key
return env.LLAMACLOUD_API_KEY;
}
+10 -3
View File
@@ -9,7 +9,7 @@ import { llamaCloudClient } from './llamacloud-client';
import { organizationAuth } from './organization-auth';
import { db } from '@/lib/db';
import { DatabaseError, LlamaCloudConnectionError } from '@/lib/errors/api-errors';
import { env, validateEnv } from '@/lib/env';
import { env, validateEnv, getLlamaCloudApiKey } from '@/lib/env';
/**
* Main LlamaCloud connection management service
@@ -28,9 +28,16 @@ export class LlamaCloudConnectionService implements ILlamaCloudConnectionService
throw new LlamaCloudConnectionError('LlamaCloud API key not configured in environment variables');
}
// Step 3: Verify project access using environment API key
// Get user's email to determine which API key to use
const user = await db.user.findUnique({
where: { id: userId },
select: { email: true }
});
// Step 3: Verify project access using appropriate API key based on user
const apiKey = getLlamaCloudApiKey(user?.email);
const verifiedProject = await llamaCloudClient.verifyProjectAccess(
env.LLAMACLOUD_API_KEY,
apiKey,
request.projectId
);
+10 -3
View File
@@ -8,7 +8,7 @@ import { llamaCloudClient } from './llamacloud-client';
import { organizationAuth } from './organization-auth';
import { db } from '@/lib/db';
import { DatabaseError, LlamaCloudConnectionError, NotFoundError } from '@/lib/errors/api-errors';
import { env, validateEnv } from '@/lib/env';
import { env, validateEnv, getLlamaCloudApiKey } from '@/lib/env';
/**
* LlamaCloud documents management service
@@ -27,12 +27,19 @@ export class LlamaCloudDocumentsService implements ILlamaCloudDocumentsService {
throw new LlamaCloudConnectionError('LlamaCloud API key not configured in environment variables');
}
// Get user's email to determine which API key to use
const user = await db.user.findUnique({
where: { id: userId },
select: { email: true }
});
const apiKey = getLlamaCloudApiKey(user?.email);
// Step 3: Get connected organization
const organization = await this.getConnectedOrganization(request.organizationId);
// Step 4: Fetch pipelines for the project
const pipelines = await llamaCloudClient.fetchPipelinesForProject(
env.LLAMACLOUD_API_KEY,
apiKey,
organization.llamaCloudProjectId!
);
@@ -41,7 +48,7 @@ export class LlamaCloudDocumentsService implements ILlamaCloudDocumentsService {
const documentFetchPromises = pipelines.map(async (pipeline) => {
try {
const documents = await llamaCloudClient.fetchFilesForPipeline(
env.LLAMACLOUD_API_KEY,
apiKey,
pipeline.id
);
return documents.map((doc: any) => ({
+6 -3
View File
@@ -1,6 +1,7 @@
import { db } from '@/lib/db';
import { organizationService } from '@/lib/organization-service';
import { LlamaIndexService } from '@/lib/llama-index-service';
import { getLlamaCloudApiKey } from '@/lib/env';
import {
GenerateResponseRequest,
GenerateResponseResponse,
@@ -44,7 +45,7 @@ export class ResponseGenerationService {
return this.generateDefaultResponse(request.question, 'No valid indexes found');
}
return this.generateLlamaIndexResponse(request, project, selectedIndexNames);
return this.generateLlamaIndexResponse(request, project, selectedIndexNames, currentUser.email);
}
private async getCurrentUser() {
@@ -136,10 +137,12 @@ export class ResponseGenerationService {
private async generateLlamaIndexResponse(
request: GenerateResponseRequest,
project: ProjectWithOrganization,
selectedIndexNames: string[]
selectedIndexNames: string[],
userEmail: string
): Promise<GenerateResponseResponse> {
const apiKey = getLlamaCloudApiKey(userEmail);
const llamaIndexService = new LlamaIndexService({
apiKey: process.env.LLAMACLOUD_API_KEY!,
apiKey: apiKey,
projectName: project.organization.llamaCloudProjectName || 'Default',
indexNames: request.useAllIndexes ? undefined : selectedIndexNames,
});
+4
View File
@@ -5,6 +5,10 @@ const nextConfig = {
// You will need to set LLAMACLOUD_API_KEY in your deployment environment
// or in a .env.local file that's not committed to version control
LLAMACLOUD_API_KEY: process.env.LLAMACLOUD_API_KEY,
// Internal API key for internal users
LLAMACLOUD_API_KEY_INTERNAL: process.env.LLAMACLOUD_API_KEY_INTERNAL,
// Internal email domain (defaults to @runllama.ai)
INTERNAL_EMAIL_DOMAIN: process.env.INTERNAL_EMAIL_DOMAIN,
},
// Other Next.js config options
reactStrictMode: true,