Skip to content

Instantly share code, notes, and snippets.

@anand-mukul
Created October 8, 2023 06:33
Show Gist options
  • Save anand-mukul/1a813bd686f7faff2e1793b6580fa539 to your computer and use it in GitHub Desktop.
Save anand-mukul/1a813bd686f7faff2e1793b6580fa539 to your computer and use it in GitHub Desktop.
Remember to replace PINECONE INDEX NAME AND ENVIRONMENT, also check .env with app.pinecone.io
import { db } from '@/db';
import { getKindeServerSession } from '@kinde-oss/kinde-auth-nextjs/server';
import { createUploadthing, type FileRouter } from 'uploadthing/next';
import { PDFLoader } from 'langchain/document_loaders/fs/pdf';
import { OpenAIEmbeddings } from 'langchain/embeddings/openai';
import { PineconeStore } from 'langchain/vectorstores/pinecone';
import { pinecone } from '@/lib/pinecone';
import { getUserSubscriptionPlan } from '@/lib/stripe';
import { PLANS } from '@/config/stripe';
const f = createUploadthing();
const middleware = async () => {
const { getUser } = getKindeServerSession();
const user = getUser();
if (!user || !user.id) throw new Error('Unauthorized');
const subscriptionPlan = await getUserSubscriptionPlan();
return { subscriptionPlan, userId: user.id };
};
const onUploadComplete = async ({
metadata,
file,
}: {
metadata: Awaited<ReturnType<typeof middleware>>;
file: {
key: string;
name: string;
url: string;
};
}) => {
const isFileExist = await db.file.findFirst({
where: {
key: file.key,
},
});
if (isFileExist) return;
const createdFile = await db.file.create({
data: {
key: file.key,
name: file.name,
userId: metadata.userId,
url: `https://uploadthing-prod.s3.us-west-2.amazonaws.com/${file.key}`,
uploadStatus: 'PROCESSING',
},
});
try {
const response = await fetch(
`https://uploadthing-prod.s3.us-west-2.amazonaws.com/${file.key}`
);
const blob = await response.blob();
const loader = new PDFLoader(blob);
const pageLevelDocs = await loader.load();
const pagesAmt = pageLevelDocs.length;
const { subscriptionPlan } = metadata;
const { isSubscribed } = subscriptionPlan;
const allowedPages = isSubscribed
? PLANS.find((plan) => plan.name === 'Pro')!.pagesPerPdf
: PLANS.find((plan) => plan.name === 'Free')!.pagesPerPdf;
if (pagesAmt > allowedPages) {
await db.file.update({
data: {
uploadStatus: 'FAILED',
},
where: {
id: createdFile.id,
},
});
} else {
const pineconeIndex = pinecone.Index('INDEXNAME'); //check on app.pinecone.io
const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
});
await PineconeStore.fromDocuments(pageLevelDocs, embeddings, {
pineconeIndex,
namespace: createdFile.id,
});
await db.file.update({
data: {
uploadStatus: 'SUCCESS',
},
where: {
id: createdFile.id,
},
});
}
} catch (err) {
await db.file.update({
data: {
uploadStatus: 'FAILED',
},
where: {
id: createdFile.id,
},
});
}
};
export const ourFileRouter: FileRouter = {
freePlanUploader: f({ pdf: { maxFileSize: '4MB' } })
.middleware(middleware)
.onUploadComplete(onUploadComplete),
proPlanUploader: f({ pdf: { maxFileSize: '16MB' } })
.middleware(middleware)
.onUploadComplete(onUploadComplete),
};
export type OurFileRouter = typeof ourFileRouter;
import { Pinecone } from '@pinecone-database/pinecone';
export const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY!,
environment: 'ENVIRONMENTNAME', //check on app.pinecone.io
});
import { db } from '@/db';
import { openai } from '@/lib/openai';
import { pinecone } from '@/lib/pinecone';
import { SendMessageValidator } from '@/lib/validators/SendMessageValidator';
import { getKindeServerSession } from '@kinde-oss/kinde-auth-nextjs/server';
import { OpenAIEmbeddings } from 'langchain/embeddings/openai';
import { PineconeStore } from 'langchain/vectorstores/pinecone';
import { NextRequest } from 'next/server';
import { OpenAIStream, StreamingTextResponse } from 'ai';
export const POST = async (req: NextRequest) => {
const body = await req.json();
const { getUser } = getKindeServerSession();
const user = getUser();
const { id: userId } = user;
if (!userId) {
return new Response('Unauthorized', { status: 401 });
}
const { fileId, message } = SendMessageValidator.parse(body);
const file = await db.file.findFirst({
where: {
id: fileId,
userId,
},
});
if (!file) {
return new Response('Not found', { status: 404 });
}
await db.message.create({
data: {
text: message,
isUserMessage: true,
userId,
fileId,
},
});
const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
});
const pineconeIndex = pinecone.Index('INDEXNAME'); //check on app.pinecone.io
const vectorStore = await PineconeStore.fromExistingIndex(embeddings, {
//@ts-ignore
pineconeIndex,
namespace: file.id,
});
const results = await vectorStore.similaritySearch(message, 4);
const prevMessages = await db.message.findMany({
where: {
fileId,
},
orderBy: {
createdAt: 'asc',
},
take: 6,
});
const formattedPrevMessages = prevMessages.map((msg) => ({
role: msg.isUserMessage ? ('user' as const) : ('assistant' as const),
content: msg.text,
}));
const contextMessages = formattedPrevMessages.map((message) => {
if (message.role === 'user') {
return `User: ${message.content}\n`;
}
return `Assistant: ${message.content}\n`;
});
const context = `PREVIOUS CONVERSATION:\n${contextMessages.join('')}\n\nCONTEXT:\n${results.map((r) => r.pageContent).join('\n\n')}\nUSER INPUT: ${message}`;
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
temperature: 0,
stream: true,
messages: [
{
role: 'system',
content: 'Use the following pieces of context (or previous conversation if needed) to answer the user\'s question in markdown format.',
},
{
role: 'user',
content: context,
},
],
});
const stream = OpenAIStream(response, {
async onCompletion(completion) {
await db.message.create({
data: {
text: completion,
isUserMessage: false,
fileId,
userId,
},
});
},
});
return new StreamingTextResponse(stream);
};
@Ben-121
Copy link

Ben-121 commented Nov 14, 2023

it still gives me the same error too many pages in pdf

@Omsoni06
Copy link

same

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment