├── .eslintrc.json
├── .gitignore
├── README.md
├── components.json
├── drizzle.config.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── next.svg
└── vercel.svg
├── src
├── app
│ ├── api
│ │ ├── chat
│ │ │ └── route.ts
│ │ ├── create-chat
│ │ │ └── route.ts
│ │ ├── get-messages
│ │ │ └── route.ts
│ │ └── uploadthing
│ │ │ ├── core.ts
│ │ │ └── route.ts
│ ├── chat
│ │ └── [chatId]
│ │ │ └── page.tsx
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ ├── page.tsx
│ ├── sign-in
│ │ └── [[...sign-in]]
│ │ │ └── page.tsx
│ └── sign-up
│ │ └── [[...sign-up]]
│ │ └── page.tsx
├── components
│ ├── ChatComponent.tsx
│ ├── ChatSideBar.tsx
│ ├── FileUpload.tsx
│ ├── MessageList.tsx
│ ├── PDFViewer.tsx
│ ├── Providers.tsx
│ └── ui
│ │ ├── Sheet.tsx
│ │ ├── button.tsx
│ │ ├── input.tsx
│ │ ├── toast.tsx
│ │ ├── toaster.tsx
│ │ └── use-toast.ts
├── db
│ ├── dbConfig.ts
│ ├── helper
│ │ └── index.ts
│ └── models
│ │ ├── chatModel.ts
│ │ └── messageModel.ts
├── lib
│ ├── context.ts
│ ├── db
│ │ ├── index.ts
│ │ └── schema.ts
│ ├── downloadFile.ts
│ ├── embeddings.ts
│ ├── huggingEmbedding.ts
│ ├── pinecone.ts
│ ├── transformers.ts
│ ├── uploadThing.ts
│ └── utils.ts
└── middleware.ts
├── tailwind.config.js
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | ```
14 |
15 | #playing with models
16 | openai challenges => cant perform embedding because of free tier limit 3/min. to tackle this im gonna make the chunksize high and limit to 3 not good at all but to test its enough.
17 |
18 | chatgpt model using cosine and transformer im trying dot product and cosine for similarites.
19 | vector dimension of text-ada open ai model is 1536, so need to update pinecone db;
20 |
21 | By using transformers i can use the "Xenova/e5-large-v2" model and its dimensions is 1024 so need to change the pinecone config.
22 |
23 | pinecone migrated from 0 to 1v so things changes including namespace not available for free tier so need to use some filter across the meta data.
24 |
25 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
26 |
27 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
28 |
29 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
30 |
31 | ## Learn More
32 |
33 | To learn more about Next.js, take a look at the following resources:
34 |
35 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
36 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
37 |
38 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
39 |
40 | ## Deploy on Vercel
41 |
42 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
43 |
44 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
45 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "src/app/global.css",
9 | "baseColor": "slate",
10 | "cssVariables": true
11 | },
12 | "aliases": {
13 | "components": "@/components",
14 | "utils": "@/lib/utils"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/drizzle.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "drizzle-kit";
2 | import * as dotenv from "dotenv";
3 | dotenv.config({ path: ".env.local" });
4 |
5 | export default {
6 | driver: "pg",
7 | schema: "./src/lib/db/schema.ts",
8 | dbCredentials: {
9 | connectionString: process.env.NEON_DATABASE_URL!,
10 | },
11 | } satisfies Config;
12 |
13 | // npx drizzle-kit push:pg
14 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | output: "standalone",
4 | experimental: {
5 | serverComponentsExternalPackages: ["sharp", "onnxruntime-node"],
6 | },
7 | };
8 |
9 | module.exports = nextConfig;
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-with-pdf",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "set NODE_OPTIONS=--max-old-space-size=8192 && next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@clerk/nextjs": "^4.23.5",
13 | "@neondatabase/serverless": "^0.6.0",
14 | "@pinecone-database/doc-splitter": "^0.0.1",
15 | "@pinecone-database/pinecone": "0.1.6",
16 | "@radix-ui/react-dialog": "^1.0.4",
17 | "@radix-ui/react-slot": "^1.0.2",
18 | "@radix-ui/react-toast": "^1.1.4",
19 | "@tanstack/react-query": "^4.35.0",
20 | "@types/md5": "^2.3.2",
21 | "@types/node": "20.6.0",
22 | "@types/react": "18.2.21",
23 | "@types/react-dom": "18.2.7",
24 | "@uploadthing/react": "^5.5.0",
25 | "@xenova/transformers": "^2.6.0",
26 | "ai": "^2.2.12",
27 | "autoprefixer": "10.4.15",
28 | "axios": "^1.5.0",
29 | "class-variance-authority": "^0.7.0",
30 | "clsx": "^2.0.0",
31 | "dotenv": "^16.3.1",
32 | "drizzle-kit": "^0.19.13",
33 | "drizzle-orm": "^0.28.6",
34 | "encoding": "^0.1.13",
35 | "eslint": "8.49.0",
36 | "eslint-config-next": "13.4.19",
37 | "langchain": "^0.0.146",
38 | "lucide-react": "^0.276.0",
39 | "md5": "^2.3.0",
40 | "mongoose": "^7.5.0",
41 | "next": "13.4.19",
42 | "openai-edge": "^1.2.2",
43 | "pdf-parse": "^1.1.1",
44 | "pg": "^8.11.3",
45 | "postcss": "8.4.29",
46 | "react": "18.2.0",
47 | "react-dom": "18.2.0",
48 | "react-dropzone": "^14.2.3",
49 | "tailwind-merge": "^1.14.0",
50 | "tailwind-scrollbar": "^3.0.5",
51 | "tailwindcss": "3.3.3",
52 | "tailwindcss-animate": "^1.0.7",
53 | "typescript": "5.2.2",
54 | "uploadthing": "^5.5.3"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/api/chat/route.ts:
--------------------------------------------------------------------------------
1 | import { Configuration, OpenAIApi } from "openai-edge";
2 | import { OpenAIStream, StreamingTextResponse, Message } from "ai";
3 | import { getContext } from "@/lib/context";
4 | import { db } from "@/lib/db";
5 | import { chats, messages as _messages } from "@/lib/db/schema";
6 | import { eq } from "drizzle-orm";
7 | import { NextResponse } from "next/server";
8 | import _message from "@/db/models/messageModel";
9 |
10 | export const runtime = "edge";
11 |
12 | const config = new Configuration({
13 | apiKey: process.env.OPENAI_API_KEY,
14 | });
15 | const openai = new OpenAIApi(config);
16 |
17 | export async function POST(req: Request) {
18 | try {
19 | const { messages, chatId } = await req.json();
20 |
21 | const _chats = await db.select().from(chats).where(eq(chats.id, chatId));
22 | if (_chats.length != 1) {
23 | return NextResponse.json({ error: "chat not found" }, { status: 404 });
24 | }
25 | const fileKey = _chats[0].fileKey;
26 | const lastMessage = messages[messages.length - 1];
27 | const context = await getContext(lastMessage.content, fileKey);
28 | console.log("lol context", context);
29 | const prompt = {
30 | role: "system",
31 | content: `AI assistant is a brand new, powerful, human-like artificial intelligence.
32 | The traits of AI include expert knowledge, helpfulness, cleverness, and articulateness.
33 | AI is a well-behaved and well-mannered individual.
34 | AI is always friendly, kind, and inspiring, and he is eager to provide vivid and thoughtful responses to the user.
35 | AI has the sum of all knowledge in their brain, and is able to accurately answer nearly any question about any topic in conversation.
36 | AI assistant is a big fan of Pinecone and Vercel.
37 | START CONTEXT BLOCK
38 | ${context}
39 | END OF CONTEXT BLOCK
40 | AI assistant will take into account any CONTEXT BLOCK that is provided in a conversation.
41 | If the context does not provide the answer to question, the AI assistant will say, "I'm sorry, but I don't know the answer to that question".
42 | AI assistant will not apologize for previous responses, but instead will indicated new information was gained.
43 | AI assistant will not invent anything that is not drawn directly from the context.
44 | `,
45 | };
46 |
47 | const response = await openai.createChatCompletion({
48 | model: "gpt-3.5-turbo",
49 | messages: [
50 | prompt,
51 | ...messages.filter((message: Message) => message.role === "user"),
52 | ],
53 | stream: true,
54 | });
55 | const stream = OpenAIStream(response, {
56 | onStart: async () => {
57 | // save user message into db
58 | await db.insert(_messages).values({
59 | chatId,
60 | content: lastMessage.content,
61 | role: "user",
62 | });
63 | },
64 | onCompletion: async (completion) => {
65 | // save ai message into db
66 | await db.insert(_messages).values({
67 | chatId,
68 | content: completion,
69 | role: "system",
70 | });
71 | },
72 | });
73 | return new StreamingTextResponse(stream);
74 | } catch (error) {
75 | console.log("some error happended in chatCompletion", error);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/app/api/create-chat/route.ts:
--------------------------------------------------------------------------------
1 | import { db } from "@/lib/db";
2 | import { chats } from "@/lib/db/schema";
3 | import { loadPdfIntoPinecone } from "@/lib/pinecone";
4 | // import { loadS3IntoPinecone } from "@/lib/pinecone";
5 | // import { getS3Url } from "@/lib/s3";
6 | import { auth } from "@clerk/nextjs";
7 | import { NextResponse } from "next/server";
8 | import { utapi } from "uploadthing/server";
9 |
10 | // /api/create-chat
11 | export async function POST(req: Request, res: Response) {
12 | const { userId } = await auth();
13 | console.log("userID from server", userId);
14 | if (!userId) {
15 | return NextResponse.json({ error: "unauthorized" }, { status: 401 });
16 | }
17 | try {
18 | const body = await req.json();
19 | const { file_key, file_name } = body;
20 | console.log(file_key, file_name);
21 | const pdf = await utapi.getFileUrls(file_key);
22 | console.log("pdf from server==>", pdf);
23 | const file_url = pdf[0].url;
24 | let doc = await loadPdfIntoPinecone(file_key, file_url);
25 | console.log("doc===>", doc);
26 | const chat_id = await db
27 | .insert(chats)
28 | .values({
29 | fileKey: file_key,
30 | pdfName: file_name,
31 | pdfUrl: file_url,
32 | userId,
33 | })
34 | .returning({
35 | insertedId: chats?.id,
36 | });
37 | console.log("chat==?", chat_id);
38 | return NextResponse.json(
39 | {
40 | chat_id: chat_id[0]?.insertedId,
41 | chat_name: file_name,
42 | },
43 | { status: 200 }
44 | );
45 | } catch (error) {
46 | console.error(error);
47 | return NextResponse.json(
48 | { error: "internal server error", msg: error },
49 | { status: 500 }
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/app/api/get-messages/route.ts:
--------------------------------------------------------------------------------
1 | import { db } from "@/lib/db";
2 | import { messages } from "@/lib/db/schema";
3 | import { eq } from "drizzle-orm";
4 | import { NextResponse } from "next/server";
5 |
6 | export const runtime = "edge";
7 |
8 | export const POST = async (req: Request) => {
9 | const { chatId } = await req.json();
10 | const _messages = await db
11 | .select()
12 | .from(messages)
13 | .where(eq(messages.chatId, chatId));
14 | return NextResponse.json(_messages);
15 | };
16 |
--------------------------------------------------------------------------------
/src/app/api/uploadthing/core.ts:
--------------------------------------------------------------------------------
1 | import { createUploadthing, type FileRouter } from "uploadthing/next";
2 | import { currentUser } from "@clerk/nextjs";
3 |
4 | const f = createUploadthing();
5 |
6 | // const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function
7 | const getUser = async () => await currentUser();
8 |
9 | // FileRouter for your app, can contain multiple FileRoutes
10 | export const ourFileRouter = {
11 | // Define as many FileRoutes as you like, each with a unique routeSlug
12 | pdfUploader: f({
13 | pdf: { maxFileCount: 1, maxFileSize: "8MB" },
14 | })
15 | // Set permissions and file types for this FileRoute
16 | .middleware(async ({ req }) => {
17 | // This code runs on your server before upload
18 | const user = await getUser();
19 |
20 | // If you throw, the user will not be able to upload
21 | if (!user) throw new Error("Unauthorized");
22 |
23 | // Whatever is returned here is accessible in onUploadComplete as `metadata`
24 | return { userId: user.id };
25 | })
26 | .onUploadComplete(async ({ metadata, file }) => {
27 | // This code RUNS ON YOUR SERVER after upload
28 | console.log("Upload complete for userId:", metadata.userId);
29 |
30 | console.log("file url", file.url);
31 | }),
32 | } satisfies FileRouter;
33 |
34 | export type OurFileRouter = typeof ourFileRouter;
35 |
--------------------------------------------------------------------------------
/src/app/api/uploadthing/route.ts:
--------------------------------------------------------------------------------
1 | import { createNextRouteHandler } from "uploadthing/next";
2 |
3 | import { ourFileRouter } from "./core";
4 |
5 | // Export routes for Next App Router
6 | export const { GET, POST } = createNextRouteHandler({
7 | router: ourFileRouter,
8 | });
9 |
--------------------------------------------------------------------------------
/src/app/chat/[chatId]/page.tsx:
--------------------------------------------------------------------------------
1 | import ChatComponent from "@/components/ChatComponent";
2 | import ChatSideBar from "@/components/ChatSideBar";
3 | import { PanelRightOpen } from "lucide-react";
4 | import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/Sheet";
5 | import { Button } from "@/components/ui/button";
6 | import PDFViewer from "@/components/PDFViewer";
7 | import { eq } from "drizzle-orm";
8 | import { db } from "@/lib/db";
9 | import { chats } from "@/lib/db/schema";
10 | // import { checkSubscription } from "@/lib/subscription";
11 | import { auth } from "@clerk/nextjs";
12 | // import { eq } from "drizzle-orm";
13 | import { redirect } from "next/navigation";
14 | import React from "react";
15 |
16 | type Props = {
17 | params: {
18 | chatId: string;
19 | };
20 | };
21 |
22 | const ChatPage = async ({ params: { chatId } }: Props) => {
23 | const { userId } = await auth();
24 | if (!userId) {
25 | return redirect("/sign-in");
26 | }
27 |
28 | const _chats = await db.select().from(chats).where(eq(chats.userId, userId));
29 | if (!_chats) {
30 | return redirect("/");
31 | }
32 | if (!_chats.find((chat) => chat.id === parseInt(chatId))) {
33 | return redirect("/");
34 | }
35 |
36 | const currentChat = _chats.find((chat) => chat.id === parseInt(chatId));
37 |
38 | // if (!_chats.find((chat) => chat._id === chatId)) {
39 | // return redirect("/");
40 | // }
41 |
42 | return (
43 |
44 |
45 | {/* chat sidebar */}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
Chat
69 |
70 | {currentChat?.pdfName}
71 |
72 |
73 |
74 |
75 | {/* pdf viewer */}
76 |
79 | {/* chat component */}
80 |
81 |
82 |
83 |
84 |
85 | );
86 | };
87 |
88 | export default ChatPage;
89 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kiranism/chat-with-pdf/624664c211d4b6025f872ca8f36c7111e69ad44b/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 222.2 84% 4.9%;
9 |
10 | --card: 0 0% 100%;
11 | --card-foreground: 222.2 84% 4.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 222.2 84% 4.9%;
15 |
16 | --primary: 222.2 47.4% 11.2%;
17 | --primary-foreground: 210 40% 98%;
18 |
19 | --secondary: 210 40% 96.1%;
20 | --secondary-foreground: 222.2 47.4% 11.2%;
21 |
22 | --muted: 210 40% 96.1%;
23 | --muted-foreground: 215.4 16.3% 46.9%;
24 |
25 | --accent: 210 40% 96.1%;
26 | --accent-foreground: 222.2 47.4% 11.2%;
27 |
28 | --destructive: 0 84.2% 60.2%;
29 | --destructive-foreground: 210 40% 98%;
30 |
31 | --border: 214.3 31.8% 91.4%;
32 | --input: 214.3 31.8% 91.4%;
33 | --ring: 222.2 84% 4.9%;
34 |
35 | --radius: 0.5rem;
36 | }
37 |
38 | .dark {
39 | --background: 222.2 84% 4.9%;
40 | --foreground: 210 40% 98%;
41 |
42 | --card: 222.2 84% 4.9%;
43 | --card-foreground: 210 40% 98%;
44 |
45 | --popover: 222.2 84% 4.9%;
46 | --popover-foreground: 210 40% 98%;
47 |
48 | --primary: 210 40% 98%;
49 | --primary-foreground: 222.2 47.4% 11.2%;
50 |
51 | --secondary: 217.2 32.6% 17.5%;
52 | --secondary-foreground: 210 40% 98%;
53 |
54 | --muted: 217.2 32.6% 17.5%;
55 | --muted-foreground: 215 20.2% 65.1%;
56 |
57 | --accent: 217.2 32.6% 17.5%;
58 | --accent-foreground: 210 40% 98%;
59 |
60 | --destructive: 0 62.8% 30.6%;
61 | --destructive-foreground: 210 40% 98%;
62 |
63 | --border: 217.2 32.6% 17.5%;
64 | --input: 217.2 32.6% 17.5%;
65 | --ring: 212.7 26.8% 83.9%;
66 | }
67 | }
68 |
69 | @layer base {
70 | * {
71 | @apply border-border;
72 | }
73 | body {
74 | @apply bg-background text-foreground;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { ClerkProvider } from "@clerk/nextjs";
2 | import "./globals.css";
3 | import type { Metadata } from "next";
4 | import { Inter } from "next/font/google";
5 | import "@uploadthing/react/styles.css";
6 | import Providers from "@/components/Providers";
7 | import { Toaster } from "@/components/ui/toaster";
8 | const inter = Inter({ subsets: ["latin"] });
9 |
10 | export const metadata: Metadata = {
11 | title: "Create Next App",
12 | description: "Generated by create next app",
13 | };
14 |
15 | export default function RootLayout({
16 | children,
17 | }: {
18 | children: React.ReactNode;
19 | }) {
20 | return (
21 |
22 |
23 |
24 |
25 | {children}
26 |
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { FileUpload } from "@/components/FileUpload";
2 | import { Button } from "@/components/ui/button";
3 | import { utapi } from "uploadthing/server";
4 | import { UserButton, auth } from "@clerk/nextjs";
5 | import { ArrowRight, LogIn } from "lucide-react";
6 | import Link from "next/link";
7 | import { db } from "@/lib/db";
8 | import { chats } from "@/lib/db/schema";
9 | import { eq } from "drizzle-orm";
10 |
11 | export default async function Home() {
12 | const { userId } = await auth();
13 | const isAuth = !!userId;
14 |
15 | let firstChat;
16 | if (userId) {
17 | firstChat = await db.select().from(chats).where(eq(chats.userId, userId));
18 | if (firstChat) {
19 | firstChat = firstChat[0];
20 | }
21 | }
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |
29 | Chat With PDF
30 |
31 |
32 |
33 |
34 | {isAuth && firstChat && (
35 |
36 |
37 | Go to Chats
38 |
39 |
40 | )}
41 |
42 |
43 | Chat with any PDF. Join millions of students, researchers and
44 | professionals to instantly answer questions and understand research
45 | with AI.
46 |
47 |
48 | {isAuth ? (
49 | <>
50 |
51 | >
52 | ) : (
53 |
54 |
55 | Login to get Started
56 |
57 |
58 |
59 | )}
60 |
61 |
62 |
63 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/src/app/sign-in/[[...sign-in]]/page.tsx:
--------------------------------------------------------------------------------
1 | import { SignIn } from "@clerk/nextjs";
2 |
3 | export default function Page() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/sign-up/[[...sign-up]]/page.tsx:
--------------------------------------------------------------------------------
1 | import { SignUp } from "@clerk/nextjs";
2 |
3 | export default function Page() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/ChatComponent.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useRef } from "react";
3 | import { Input } from "./ui/input";
4 | import { useChat } from "ai/react";
5 | import { Button } from "./ui/button";
6 | import { Send } from "lucide-react";
7 | import MessageList from "./MessageList";
8 | import { useQuery } from "@tanstack/react-query";
9 | import axios from "axios";
10 | import { Message } from "ai";
11 |
12 | type Props = { chatId: number };
13 |
14 | const ChatComponent = ({ chatId }: Props) => {
15 | const { data, isLoading } = useQuery({
16 | queryKey: ["chat", chatId],
17 | queryFn: async () => {
18 | const response = await axios.post("/api/get-messages", {
19 | chatId,
20 | });
21 | return response.data;
22 | },
23 | });
24 |
25 | const { input, handleInputChange, handleSubmit, messages } = useChat({
26 | api: "/api/chat",
27 | body: {
28 | chatId,
29 | },
30 | initialMessages: data || [],
31 | });
32 | const messageRef = React.useRef(null);
33 | React.useEffect(() => {
34 | if (messageRef.current) {
35 | messageRef.current.scrollTop = messageRef.current.scrollHeight;
36 | }
37 | }, [messages]);
38 |
39 | return (
40 |
41 | {/* header */}
42 |
43 |
Chat
44 |
45 |
46 | {/* message list */}
47 |
52 |
53 |
69 |
70 | );
71 | };
72 |
73 | export default ChatComponent;
74 |
--------------------------------------------------------------------------------
/src/components/ChatSideBar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Link from "next/link";
3 | import React from "react";
4 | import { Button } from "./ui/button";
5 | import { MessageCircle, PlusCircle } from "lucide-react";
6 | import { cn } from "@/lib/utils";
7 | import axios from "axios";
8 | import { DrizzleChat } from "@/lib/db/schema";
9 | // import SubscriptionButton from "./SubscriptionButton";
10 |
11 | type Props = {
12 | chats: DrizzleChat[];
13 | chatId: number;
14 | isPro?: boolean;
15 | };
16 |
17 | const ChatSideBar = ({ chats, chatId, isPro }: Props) => {
18 | const [loading, setLoading] = React.useState(false);
19 |
20 | return (
21 |
22 |
23 |
24 |
25 | New Chat
26 |
27 |
28 |
29 |
30 | {chats.map((chat) => (
31 |
32 |
38 |
39 |
40 | {chat.pdfName}
41 |
42 |
43 |
44 | ))}
45 |
46 |
47 | {/*
48 |
49 | Home
50 | Source
51 |
52 |
53 |
*/}
54 |
55 | );
56 | };
57 |
58 | export default ChatSideBar;
59 |
--------------------------------------------------------------------------------
/src/components/FileUpload.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { UploadDropzone } from "@uploadthing/react";
3 |
4 | import { OurFileRouter } from "@/app/api/uploadthing/core";
5 | import axios from "axios";
6 | import { useRouter } from "next/navigation";
7 | import { useState } from "react";
8 | import { useMutation } from "@tanstack/react-query";
9 | import { useToast } from "./ui/use-toast";
10 | import { Loader2 } from "lucide-react";
11 | export const FileUpload = () => {
12 | const router = useRouter();
13 | const { toast } = useToast();
14 | const [uploading, setUploading] = useState(false);
15 | const { mutate, isLoading } = useMutation({
16 | mutationFn: async ({
17 | file_key,
18 | file_name,
19 | }: {
20 | file_key: string;
21 | file_name: string;
22 | }) => {
23 | const response = await axios.post("/api/create-chat", {
24 | file_key,
25 | file_name,
26 | });
27 | return response.data;
28 | },
29 | });
30 |
31 | return (
32 |
33 | className="bg-white ut-label:text-lg ut-allowed-content:ut-uploading:text-red-300"
34 | endpoint="pdfUploader"
35 | content={{
36 | allowedContent({ isUploading }) {
37 | if (isUploading)
38 | return (
39 | <>
40 |
41 | Spilling Tea to GPT...
42 |
43 | >
44 | );
45 |
46 | if (!isUploading && isLoading)
47 | return (
48 | <>
49 |
50 | Loading... Almost there!
51 |
52 | >
53 | );
54 | },
55 | }}
56 | onClientUploadComplete={(res) => {
57 | // Do something with the response
58 | console.log("Files: ", res);
59 | const data = {
60 | file_key: res![0].key,
61 | file_name: res![0].name,
62 | };
63 | mutate(data, {
64 | onSuccess: ({ chat_id, chat_name }) => {
65 | toast({
66 | title: "Chat created!",
67 | description: `Chat session created for ${chat_name}`,
68 | });
69 | router.push(`/chat/${chat_id}`);
70 | },
71 | onError: (err) => {
72 | toast({
73 | variant: "destructive",
74 | title: "Error creating chat!",
75 | });
76 | console.error(err);
77 | },
78 | });
79 | }}
80 | onUploadError={(error: Error) => {
81 | toast({
82 | variant: "destructive",
83 | title: `ERROR! ${error.message}`,
84 | });
85 | }}
86 | onUploadBegin={(name) => {
87 | // Do something once upload begins
88 | console.log("Uploading: ", name);
89 | }}
90 | />
91 | );
92 | };
93 |
--------------------------------------------------------------------------------
/src/components/MessageList.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 | import { Message } from "ai/react";
3 | import { Loader2 } from "lucide-react";
4 | import React, { RefObject } from "react";
5 |
6 | type Props = {
7 | isLoading: boolean;
8 | messages: Message[];
9 | messageRef: RefObject;
10 | };
11 |
12 | const MessageList = ({ messages, isLoading, messageRef }: Props) => {
13 | if (isLoading) {
14 | return (
15 |
16 |
17 |
18 | );
19 | }
20 | if (!messages) return <>>;
21 | return (
22 |
26 | {messages.length > 0 &&
27 | messages.map((message) => {
28 | return (
29 |
36 |
44 |
{message.content}
45 |
46 |
47 | );
48 | })}
49 |
50 | );
51 | };
52 |
53 | export default MessageList;
54 |
--------------------------------------------------------------------------------
/src/components/PDFViewer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | type Props = { file_url: string | "" };
4 |
5 | const PDFViewer = ({ file_url }: Props) => {
6 | console.log("fileUrl", file_url);
7 | return (
8 | <>
9 | {file_url && (
10 |
14 | )}
15 | >
16 | );
17 | };
18 |
19 | export default PDFViewer;
20 |
--------------------------------------------------------------------------------
/src/components/Providers.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
4 |
5 | type Props = {
6 | children: React.ReactNode;
7 | };
8 |
9 | const queryClient = new QueryClient();
10 |
11 | const Providers = ({ children }: Props) => {
12 | return (
13 | {children}
14 | );
15 | };
16 |
17 | export default Providers;
18 |
--------------------------------------------------------------------------------
/src/components/ui/Sheet.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as SheetPrimitive from "@radix-ui/react-dialog";
5 | import { cva, type VariantProps } from "class-variance-authority";
6 | import { X } from "lucide-react";
7 |
8 | import { cn } from "@/lib/utils";
9 |
10 | const Sheet = SheetPrimitive.Root;
11 |
12 | const SheetTrigger = SheetPrimitive.Trigger;
13 |
14 | const SheetClose = SheetPrimitive.Close;
15 |
16 | const SheetPortal = ({
17 | className,
18 | ...props
19 | }: SheetPrimitive.DialogPortalProps) => (
20 |
21 | );
22 | SheetPortal.displayName = SheetPrimitive.Portal.displayName;
23 |
24 | const SheetOverlay = React.forwardRef<
25 | React.ElementRef,
26 | React.ComponentPropsWithoutRef
27 | >(({ className, ...props }, ref) => (
28 |
36 | ));
37 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
38 |
39 | const sheetVariants = cva(
40 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
41 | {
42 | variants: {
43 | side: {
44 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
45 | bottom:
46 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
47 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
48 | right:
49 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
50 | },
51 | },
52 | defaultVariants: {
53 | side: "right",
54 | },
55 | }
56 | );
57 |
58 | interface SheetContentProps
59 | extends React.ComponentPropsWithoutRef,
60 | VariantProps {}
61 |
62 | const SheetContent = React.forwardRef<
63 | React.ElementRef,
64 | SheetContentProps
65 | >(({ side = "right", className, children, ...props }, ref) => (
66 |
67 |
68 |
73 | {children}
74 |
75 |
76 | Close
77 |
78 |
79 |
80 | ));
81 | SheetContent.displayName = SheetPrimitive.Content.displayName;
82 |
83 | const SheetHeader = ({
84 | className,
85 | ...props
86 | }: React.HTMLAttributes) => (
87 |
94 | );
95 | SheetHeader.displayName = "SheetHeader";
96 |
97 | const SheetFooter = ({
98 | className,
99 | ...props
100 | }: React.HTMLAttributes) => (
101 |
108 | );
109 | SheetFooter.displayName = "SheetFooter";
110 |
111 | const SheetTitle = React.forwardRef<
112 | React.ElementRef,
113 | React.ComponentPropsWithoutRef
114 | >(({ className, ...props }, ref) => (
115 |
120 | ));
121 | SheetTitle.displayName = SheetPrimitive.Title.displayName;
122 |
123 | const SheetDescription = React.forwardRef<
124 | React.ElementRef,
125 | React.ComponentPropsWithoutRef
126 | >(({ className, ...props }, ref) => (
127 |
132 | ));
133 | SheetDescription.displayName = SheetPrimitive.Description.displayName;
134 |
135 | export {
136 | Sheet,
137 | SheetTrigger,
138 | SheetClose,
139 | SheetContent,
140 | SheetHeader,
141 | SheetFooter,
142 | SheetTitle,
143 | SheetDescription,
144 | };
145 |
--------------------------------------------------------------------------------
/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15 | outline:
16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost: "hover:bg-accent hover:text-accent-foreground",
20 | link: "text-primary underline-offset-4 hover:underline",
21 | },
22 | size: {
23 | default: "h-10 px-4 py-2",
24 | sm: "h-9 rounded-md px-3",
25 | lg: "h-11 rounded-md px-8",
26 | icon: "h-10 w-10",
27 | },
28 | },
29 | defaultVariants: {
30 | variant: "default",
31 | size: "default",
32 | },
33 | }
34 | )
35 |
36 | export interface ButtonProps
37 | extends React.ButtonHTMLAttributes,
38 | VariantProps {
39 | asChild?: boolean
40 | }
41 |
42 | const Button = React.forwardRef(
43 | ({ className, variant, size, asChild = false, ...props }, ref) => {
44 | const Comp = asChild ? Slot : "button"
45 | return (
46 |
51 | )
52 | }
53 | )
54 | Button.displayName = "Button"
55 |
56 | export { Button, buttonVariants }
57 |
--------------------------------------------------------------------------------
/src/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | export interface InputProps
6 | extends React.InputHTMLAttributes {}
7 |
8 | const Input = React.forwardRef(
9 | ({ className, type, ...props }, ref) => {
10 | return (
11 |
20 | );
21 | }
22 | );
23 | Input.displayName = "Input";
24 |
25 | export { Input };
26 |
--------------------------------------------------------------------------------
/src/components/ui/toast.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as ToastPrimitives from "@radix-ui/react-toast";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 | import { X } from "lucide-react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | const ToastProvider = ToastPrimitives.Provider;
9 |
10 | const ToastViewport = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ));
23 | ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
24 |
25 | const toastVariants = cva(
26 | "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
27 | {
28 | variants: {
29 | variant: {
30 | default: "border bg-background text-foreground",
31 | destructive:
32 | "destructive group border-destructive bg-destructive text-destructive-foreground",
33 | },
34 | },
35 | defaultVariants: {
36 | variant: "default",
37 | },
38 | }
39 | );
40 |
41 | const Toast = React.forwardRef<
42 | React.ElementRef,
43 | React.ComponentPropsWithoutRef &
44 | VariantProps
45 | >(({ className, variant, ...props }, ref) => {
46 | return (
47 |
52 | );
53 | });
54 | Toast.displayName = ToastPrimitives.Root.displayName;
55 |
56 | const ToastAction = React.forwardRef<
57 | React.ElementRef,
58 | React.ComponentPropsWithoutRef
59 | >(({ className, ...props }, ref) => (
60 |
68 | ));
69 | ToastAction.displayName = ToastPrimitives.Action.displayName;
70 |
71 | const ToastClose = React.forwardRef<
72 | React.ElementRef,
73 | React.ComponentPropsWithoutRef
74 | >(({ className, ...props }, ref) => (
75 |
84 |
85 |
86 | ));
87 | ToastClose.displayName = ToastPrimitives.Close.displayName;
88 |
89 | const ToastTitle = React.forwardRef<
90 | React.ElementRef,
91 | React.ComponentPropsWithoutRef
92 | >(({ className, ...props }, ref) => (
93 |
98 | ));
99 | ToastTitle.displayName = ToastPrimitives.Title.displayName;
100 |
101 | const ToastDescription = React.forwardRef<
102 | React.ElementRef,
103 | React.ComponentPropsWithoutRef
104 | >(({ className, ...props }, ref) => (
105 |
110 | ));
111 | ToastDescription.displayName = ToastPrimitives.Description.displayName;
112 |
113 | type ToastProps = React.ComponentPropsWithoutRef;
114 |
115 | type ToastActionElement = React.ReactElement;
116 |
117 | export {
118 | type ToastProps,
119 | type ToastActionElement,
120 | ToastProvider,
121 | ToastViewport,
122 | Toast,
123 | ToastTitle,
124 | ToastDescription,
125 | ToastClose,
126 | ToastAction,
127 | };
128 |
--------------------------------------------------------------------------------
/src/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import {
4 | Toast,
5 | ToastClose,
6 | ToastDescription,
7 | ToastProvider,
8 | ToastTitle,
9 | ToastViewport,
10 | } from "@/components/ui/toast"
11 | import { useToast } from "@/components/ui/use-toast"
12 |
13 | export function Toaster() {
14 | const { toasts } = useToast()
15 |
16 | return (
17 |
18 | {toasts.map(function ({ id, title, description, action, ...props }) {
19 | return (
20 |
21 |
22 | {title && {title} }
23 | {description && (
24 | {description}
25 | )}
26 |
27 | {action}
28 |
29 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/ui/use-toast.ts:
--------------------------------------------------------------------------------
1 | // Inspired by react-hot-toast library
2 | import * as React from "react"
3 |
4 | import type {
5 | ToastActionElement,
6 | ToastProps,
7 | } from "@/components/ui/toast"
8 |
9 | const TOAST_LIMIT = 1
10 | const TOAST_REMOVE_DELAY = 1000000
11 |
12 | type ToasterToast = ToastProps & {
13 | id: string
14 | title?: React.ReactNode
15 | description?: React.ReactNode
16 | action?: ToastActionElement
17 | }
18 |
19 | const actionTypes = {
20 | ADD_TOAST: "ADD_TOAST",
21 | UPDATE_TOAST: "UPDATE_TOAST",
22 | DISMISS_TOAST: "DISMISS_TOAST",
23 | REMOVE_TOAST: "REMOVE_TOAST",
24 | } as const
25 |
26 | let count = 0
27 |
28 | function genId() {
29 | count = (count + 1) % Number.MAX_VALUE
30 | return count.toString()
31 | }
32 |
33 | type ActionType = typeof actionTypes
34 |
35 | type Action =
36 | | {
37 | type: ActionType["ADD_TOAST"]
38 | toast: ToasterToast
39 | }
40 | | {
41 | type: ActionType["UPDATE_TOAST"]
42 | toast: Partial
43 | }
44 | | {
45 | type: ActionType["DISMISS_TOAST"]
46 | toastId?: ToasterToast["id"]
47 | }
48 | | {
49 | type: ActionType["REMOVE_TOAST"]
50 | toastId?: ToasterToast["id"]
51 | }
52 |
53 | interface State {
54 | toasts: ToasterToast[]
55 | }
56 |
57 | const toastTimeouts = new Map>()
58 |
59 | const addToRemoveQueue = (toastId: string) => {
60 | if (toastTimeouts.has(toastId)) {
61 | return
62 | }
63 |
64 | const timeout = setTimeout(() => {
65 | toastTimeouts.delete(toastId)
66 | dispatch({
67 | type: "REMOVE_TOAST",
68 | toastId: toastId,
69 | })
70 | }, TOAST_REMOVE_DELAY)
71 |
72 | toastTimeouts.set(toastId, timeout)
73 | }
74 |
75 | export const reducer = (state: State, action: Action): State => {
76 | switch (action.type) {
77 | case "ADD_TOAST":
78 | return {
79 | ...state,
80 | toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
81 | }
82 |
83 | case "UPDATE_TOAST":
84 | return {
85 | ...state,
86 | toasts: state.toasts.map((t) =>
87 | t.id === action.toast.id ? { ...t, ...action.toast } : t
88 | ),
89 | }
90 |
91 | case "DISMISS_TOAST": {
92 | const { toastId } = action
93 |
94 | // ! Side effects ! - This could be extracted into a dismissToast() action,
95 | // but I'll keep it here for simplicity
96 | if (toastId) {
97 | addToRemoveQueue(toastId)
98 | } else {
99 | state.toasts.forEach((toast) => {
100 | addToRemoveQueue(toast.id)
101 | })
102 | }
103 |
104 | return {
105 | ...state,
106 | toasts: state.toasts.map((t) =>
107 | t.id === toastId || toastId === undefined
108 | ? {
109 | ...t,
110 | open: false,
111 | }
112 | : t
113 | ),
114 | }
115 | }
116 | case "REMOVE_TOAST":
117 | if (action.toastId === undefined) {
118 | return {
119 | ...state,
120 | toasts: [],
121 | }
122 | }
123 | return {
124 | ...state,
125 | toasts: state.toasts.filter((t) => t.id !== action.toastId),
126 | }
127 | }
128 | }
129 |
130 | const listeners: Array<(state: State) => void> = []
131 |
132 | let memoryState: State = { toasts: [] }
133 |
134 | function dispatch(action: Action) {
135 | memoryState = reducer(memoryState, action)
136 | listeners.forEach((listener) => {
137 | listener(memoryState)
138 | })
139 | }
140 |
141 | type Toast = Omit
142 |
143 | function toast({ ...props }: Toast) {
144 | const id = genId()
145 |
146 | const update = (props: ToasterToast) =>
147 | dispatch({
148 | type: "UPDATE_TOAST",
149 | toast: { ...props, id },
150 | })
151 | const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
152 |
153 | dispatch({
154 | type: "ADD_TOAST",
155 | toast: {
156 | ...props,
157 | id,
158 | open: true,
159 | onOpenChange: (open) => {
160 | if (!open) dismiss()
161 | },
162 | },
163 | })
164 |
165 | return {
166 | id: id,
167 | dismiss,
168 | update,
169 | }
170 | }
171 |
172 | function useToast() {
173 | const [state, setState] = React.useState(memoryState)
174 |
175 | React.useEffect(() => {
176 | listeners.push(setState)
177 | return () => {
178 | const index = listeners.indexOf(setState)
179 | if (index > -1) {
180 | listeners.splice(index, 1)
181 | }
182 | }
183 | }, [state])
184 |
185 | return {
186 | ...state,
187 | toast,
188 | dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
189 | }
190 | }
191 |
192 | export { useToast, toast }
193 |
--------------------------------------------------------------------------------
/src/db/dbConfig.ts:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | export async function connect() {
4 | try {
5 | mongoose.connect(process.env.MONGO_URI!);
6 | const connection = mongoose.connection;
7 |
8 | connection.on("connected", () => {
9 | console.log("MongoDB connected successfully");
10 | });
11 |
12 | connection.on("error", (err) => {
13 | console.log(
14 | "MongoDB connection error. Please make sure MongoDB is running. " + err
15 | );
16 | process.exit();
17 | });
18 | } catch (error) {
19 | console.log("Something goes wrong!");
20 | console.log(error);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/db/helper/index.ts:
--------------------------------------------------------------------------------
1 | import Chat from "../models/chatModel";
2 | export interface ChatData {
3 | file_key: string;
4 | file_name: string;
5 | file_url: string;
6 | userId: string;
7 | _id?: string | undefined;
8 | }
9 | export const createChat = async (data: ChatData) => {
10 | return await Chat.create(data);
11 | };
12 |
--------------------------------------------------------------------------------
/src/db/models/chatModel.ts:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const userSystemEnumValues = ["system", "user"];
4 |
5 | const chatSchema = new mongoose.Schema({
6 | file_name: {
7 | type: String,
8 | required: true,
9 | },
10 | file_url: {
11 | type: String,
12 | required: true,
13 | },
14 | createdAt: {
15 | type: Date,
16 | required: true,
17 | default: Date.now,
18 | },
19 | userId: {
20 | type: String,
21 | required: true,
22 | },
23 | file_key: {
24 | type: String,
25 | required: true,
26 | },
27 | });
28 |
29 | const userSubscriptionSchema = new mongoose.Schema({
30 | userId: {
31 | type: String,
32 | required: true,
33 | unique: true,
34 | },
35 | stripeCustomerId: {
36 | type: String,
37 | required: true,
38 | unique: true,
39 | },
40 | stripeSubscriptionId: {
41 | type: String,
42 | unique: true,
43 | },
44 | stripePriceId: {
45 | type: String,
46 | },
47 | stripeCurrentPeriodEnd: {
48 | type: Date,
49 | },
50 | });
51 |
52 |
53 | const Chat = mongoose.models.chats || mongoose.model("chats", chatSchema);
54 |
55 | export default Chat;
56 |
--------------------------------------------------------------------------------
/src/db/models/messageModel.ts:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 | const userSystemEnumValues = ["system", "user"];
3 |
4 | const messageSchema = new mongoose.Schema({
5 | chatId: {
6 | type: mongoose.Schema.Types.ObjectId,
7 | ref: "Chat",
8 | required: true,
9 | },
10 | content: {
11 | type: String,
12 | required: true,
13 | },
14 | createdAt: {
15 | type: Date,
16 | required: true,
17 | default: Date.now,
18 | },
19 | role: {
20 | type: String,
21 | enum: userSystemEnumValues,
22 | required: true,
23 | },
24 | });
25 |
26 | const Message =
27 | mongoose.models.messages || mongoose.model("messages", messageSchema);
28 |
29 | export default Message;
30 |
--------------------------------------------------------------------------------
/src/lib/context.ts:
--------------------------------------------------------------------------------
1 | import { PineconeClient } from "@pinecone-database/pinecone";
2 | import { convertToAscii } from "./utils";
3 | import { getEmbeddings } from "./embeddings";
4 | // import { embeddingTransformer } from "./transformers";
5 |
6 | export async function getMatchesFromEmbeddings(
7 | embeddings: number[],
8 | fileKey: string
9 | ) {
10 | const pinecone = new PineconeClient();
11 | await pinecone.init({
12 | apiKey: process.env.PINECONE_API_KEY!,
13 | environment: process.env.PINECONE_ENVIRONMENT!,
14 | });
15 | const index = await pinecone.Index("chat-with-pdf");
16 | try {
17 | const fileKeyWithoutAsci = convertToAscii(fileKey);
18 | // const namespace = index.namespace(namespaceWithoutAscii);
19 | const namespace = convertToAscii(fileKey);
20 | const filter = { fileKey: { $eq: fileKeyWithoutAsci } };
21 | // const queryResult = await index.query({
22 | // topK: 5,
23 | // vector: embeddings,
24 | // filter: filter,
25 | // includeMetadata: true,
26 | // });
27 |
28 | const queryResult = await index.query({
29 | queryRequest: {
30 | topK: 5,
31 | vector: embeddings,
32 | filter: filter,
33 | includeMetadata: true,
34 | },
35 | });
36 | console.log("queryRes=>", queryResult);
37 | return queryResult.matches || [];
38 | } catch (error) {
39 | console.log("error querying embeddings", error);
40 | throw error;
41 | }
42 | }
43 |
44 | export async function getContext(query: string, fileKey: string) {
45 | const queryEmbeddings = await getEmbeddings(query);
46 | console.log("queryEmbeddings", queryEmbeddings);
47 | const matches = await getMatchesFromEmbeddings(queryEmbeddings, fileKey);
48 | console.log("matched", matches);
49 | const qualifyingDocs = matches.filter(
50 | (match) => match.score && match.score > 0.7
51 | );
52 |
53 | type Metadata = {
54 | text: string;
55 | pageNumber: number;
56 | };
57 | let docs = qualifyingDocs.map((match) => (match.metadata as Metadata).text);
58 |
59 | console.log("matching docs=>", docs);
60 | return docs.join("\n").substring(0, 3000);
61 | }
62 |
--------------------------------------------------------------------------------
/src/lib/db/index.ts:
--------------------------------------------------------------------------------
1 | import { neon, neonConfig } from "@neondatabase/serverless";
2 | import { drizzle } from "drizzle-orm/neon-http";
3 |
4 | neonConfig.fetchConnectionCache = true;
5 |
6 | if (!process.env.NEON_DATABASE_URL) {
7 | throw new Error("database url not found");
8 | }
9 |
10 | const sql = neon(process.env.NEON_DATABASE_URL);
11 |
12 | export const db = drizzle(sql);
13 |
--------------------------------------------------------------------------------
/src/lib/db/schema.ts:
--------------------------------------------------------------------------------
1 | import {
2 | integer,
3 | pgEnum,
4 | pgTable,
5 | serial,
6 | text,
7 | timestamp,
8 | varchar,
9 | } from "drizzle-orm/pg-core";
10 |
11 | export const userSystemEnum = pgEnum("user_system_enum", ["system", "user"]);
12 |
13 | export const chats = pgTable("chats", {
14 | id: serial("id").primaryKey(),
15 | pdfName: text("pdf_name").notNull(),
16 | pdfUrl: text("pdf_url").notNull(),
17 | createdAt: timestamp("created_at").notNull().defaultNow(),
18 | userId: varchar("user_id", { length: 256 }).notNull(),
19 | fileKey: text("file_key").notNull(),
20 | });
21 |
22 | export type DrizzleChat = typeof chats.$inferSelect;
23 |
24 | export const messages = pgTable("messages", {
25 | id: serial("id").primaryKey(),
26 | chatId: integer("chat_id")
27 | .references(() => chats.id)
28 | .notNull(),
29 | content: text("content").notNull(),
30 | createdAt: timestamp("created_at").notNull().defaultNow(),
31 | role: userSystemEnum("role").notNull(),
32 | });
33 |
34 | export const userSubscriptions = pgTable("user_subscriptions", {
35 | id: serial("id").primaryKey(),
36 | userId: varchar("user_id", { length: 256 }).notNull().unique(),
37 | stripeCustomerId: varchar("stripe_customer_id", { length: 256 })
38 | .notNull()
39 | .unique(),
40 | stripeSubscriptionId: varchar("stripe_subscription_id", {
41 | length: 256,
42 | }).unique(),
43 | stripePriceId: varchar("stripe_price_id", { length: 256 }),
44 | stripeCurrentPeriodEnd: timestamp("stripe_current_period_end"),
45 | });
46 |
47 | // drizzle-orm
48 | // drizzle-kit
49 |
--------------------------------------------------------------------------------
/src/lib/downloadFile.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import fs from "fs";
3 | import os from "os";
4 | import path from "path";
5 |
6 | export async function downloadFromURL(file_url: string) {
7 | try {
8 | // Use axios to download the PDF content
9 | const response = await axios.get(file_url, {
10 | responseType: "arraybuffer", // Ensure it's treated as an array buffer
11 | });
12 |
13 | // The PDF content is stored in response.data as an array buffer
14 | const pdfContent = response.data;
15 |
16 | // Create a temporary directory using the OS's tmpdir and store the PDF there
17 | // const tempDirectory = process.env.TEMP! || process.env.TMP!;
18 | const tempDirectory = os.tmpdir();
19 |
20 | const file_name = path.join(tempDirectory, `pdf-${Date.now()}.pdf`);
21 | // const file_name = `/temp/pdf-${Date.now()}.pdf`;
22 | // fs.writeFileSync(file_name, obj.Body as Buffer);
23 | fs.writeFileSync(file_name, pdfContent);
24 | console.log(
25 | "File successfully written to the temporary directory:",
26 | file_name
27 | );
28 | return file_name;
29 | } catch (error) {
30 | console.error(error);
31 | return null;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/embeddings.ts:
--------------------------------------------------------------------------------
1 | import { OpenAIApi, Configuration } from "openai-edge";
2 |
3 | const config = new Configuration({
4 | apiKey: process.env.OPENAI_API_KEY,
5 | });
6 |
7 | const openai = new OpenAIApi(config);
8 |
9 | export async function getEmbeddings(text: string) {
10 | try {
11 | const response = await openai.createEmbedding({
12 | model: "text-embedding-ada-002",
13 | input: text.replace(/\n/g, " "),
14 | });
15 | const result = await response.json();
16 | console.log("getEmb result-=>", result);
17 | return result?.data[0]?.embedding as number[];
18 | } catch (error) {
19 | console.log("error calling openai embeddings ai", error);
20 | throw error;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/lib/huggingEmbedding.ts:
--------------------------------------------------------------------------------
1 | // import { HfInference } from "@huggingface/inference";
2 |
3 | // const hf = new HfInference(process.env.HF_TOKEN);
4 |
5 | // export async function hfEmbedding(text: string) {
6 | // try {
7 | // const response = await hf.featureExtraction({
8 | // model: "BAAI/bge-base-en",
9 | // inputs: {
10 | // inputs: text.replace(/\n/g, " "),
11 | // },
12 | // });
13 | // console.log("getEmb result-=>", response);
14 | // return response;
15 | // } catch (error) {
16 | // console.log("error calling openai embeddings ai", error);
17 | // throw error;
18 | // }
19 | // }
20 |
--------------------------------------------------------------------------------
/src/lib/pinecone.ts:
--------------------------------------------------------------------------------
1 | import {
2 | PineconeClient,
3 | Vector,
4 | utils as PineconeUtils,
5 | } from "@pinecone-database/pinecone";
6 | import { PDFLoader } from "langchain/document_loaders/fs/pdf";
7 | import {
8 | Document,
9 | RecursiveCharacterTextSplitter,
10 | } from "@pinecone-database/doc-splitter";
11 | import { downloadFromURL } from "./downloadFile";
12 | import { getEmbeddings } from "./embeddings";
13 | import md5 from "md5";
14 | import { convertToAscii } from "./utils";
15 | // import { embeddingTransformer } from "./transformers";
16 |
17 | let pinecone: PineconeClient | null = null;
18 |
19 | export const getPineconeClient = async () => {
20 | if (!pinecone) {
21 | pinecone = new PineconeClient();
22 | await pinecone.init({
23 | environment: process.env.PINECONE_ENVIRONMENT!,
24 | apiKey: process.env.PINECONE_API_KEY!,
25 | });
26 | }
27 | return pinecone;
28 | };
29 |
30 | type PDFPage = {
31 | pageContent: string;
32 | metadata: {
33 | loc: { pageNumber: number };
34 | };
35 | };
36 |
37 | export async function loadPdfIntoPinecone(file_key: string, file_url: string) {
38 | console.log("downloading file from uploadthing into filesystem");
39 | const file_name = await downloadFromURL(file_url);
40 | if (!file_name) {
41 | throw new Error("could not download from uploadthing");
42 | }
43 | const loader = new PDFLoader(file_name);
44 | const pages = (await loader.load()) as PDFPage[];
45 | console.log("pagees=>", pages);
46 | // split and segment the pdf
47 | const documents = await Promise.all(pages.map(prepareDoc));
48 | console.log("documents", documents);
49 | const fileKeyWithoutAsci = convertToAscii(file_key);
50 | // vectorise and embed individual docs
51 | const vectors = await Promise.all(
52 | documents
53 | .flat()
54 | .slice(0, 3)
55 | .map((doc) => embedDocument(doc, fileKeyWithoutAsci))
56 | );
57 | console.log("vectors", vectors);
58 |
59 | // upload the vector to pinecone
60 | const client = await getPineconeClient();
61 | const pineconeIndex = client.Index("chat-with-pdf");
62 |
63 | // const namespace = pineconeIndex.namespace(namespaceWithoutAsci);
64 | console.log("inserting vectors into pinecone");
65 | const namespace = convertToAscii(file_key);
66 | await pineconeIndex.upsert({
67 | upsertRequest: {
68 | vectors: vectors,
69 | },
70 | });
71 | // PineconeUtils.chunkedUpsert(pineconeIndex, vectors, namespace, 10);
72 | // let res = await pineconeIndex.upsert(vectors);
73 | console.log("res from pine==>");
74 | return documents[0];
75 | }
76 |
77 | async function embedDocument(doc: Document, file_key: string) {
78 | try {
79 | const embeddings = await getEmbeddings(doc.pageContent);
80 | console.log("embeddings=>", embeddings);
81 | const hash = md5(doc.pageContent);
82 | return {
83 | id: hash,
84 | values: embeddings,
85 | metadata: {
86 | text: doc.metadata.text,
87 | fileKey: file_key,
88 | pageNumber: doc.metadata.pageNumber,
89 | },
90 | } as Vector;
91 | } catch (error) {
92 | console.log("error embedding document", error);
93 | throw error;
94 | }
95 | }
96 |
97 | export const truncateStringByBytes = (str: string, bytes: number) => {
98 | const enc = new TextEncoder();
99 | return new TextDecoder("utf-8").decode(enc.encode(str).slice(0, bytes));
100 | };
101 |
102 | async function prepareDoc(page: PDFPage) {
103 | let { metadata, pageContent } = page;
104 | pageContent = pageContent.replace(/\n/g, "");
105 | // split the docs
106 | const splitter = new RecursiveCharacterTextSplitter({
107 | chunkSize: 36000,
108 | chunkOverlap: 0,
109 | });
110 | const docs = await splitter.splitDocuments([
111 | new Document({
112 | pageContent,
113 | metadata: {
114 | pageNumber: metadata.loc.pageNumber,
115 | text: truncateStringByBytes(pageContent, 36000),
116 | },
117 | }),
118 | ]);
119 | return docs;
120 | }
121 |
122 | /*
123 | [
124 | [
125 | Document {
126 | pageContent:
127 | 'Kiran.SFRONTEND DEVELOPER · BSC COMPUTER SCIENCE · UNIVERSITY OF CALICUT7306405336|imkir4n@gmail.com|kiranfolio.vercel.app|Kiranism|kiranismTechnical SkillsFrontend DevelopmentReact, Next.js, Javascript (ES6+), Typescript, HTML, CSS, Redux, React Router, Material UI, Tailwind CSSBuild Tools and othersJest, Vite, Webpack, Babel, Node, NPM, Express, MongoDB, Git, Github, RESTful APIsWork ExperienceSTACKROOTS TECHNOLOGY SOLUTIONSCyberpark Kozhikode, KeralaFULLSTACK DEVELOPER - MERN [1 YR 3 MOS]Jan. 2022 - Apr. 2023•Collaborated with a team of developers and designers to design, develop, and launch dynamic web applications usingReact js, Nextj s,Node js, meeting project requirements.•Successfullydeployed 3+ projectsto the cloud using Git, ensuringscalability and maintainabilityof the codebase.•Led comprehensive code reviews with the team lead, yielding20% fewer bugsand elevating application stability.•Enhanced user experience with dynamic JavaScript and CSS animations, powered',
128 | metadata: {
129 | pageNumber: 1,
130 | text:
131 | 'Kiran.SFRONTEND DEVELOPER · BSC COMPUTER SCIENCE · UNIVERSITY OF CALICUT7306405336|imkir4n@gmail.com|kiranfolio.vercel.app|Kiranism|kiranismTechnical SkillsFrontend DevelopmentReact, Next.js, Javascript (ES6+), Typescript, HTML, CSS, Redux, React Router, Material UI, Tailwind CSSBuild Tools and othersJest, Vite, Webpack, Babel, Node, NPM, Express, MongoDB, Git, Github, RESTful APIsWork ExperienceSTACKROOTS TECHNOLOGY SOLUTIONSCyberpark Kozhikode, KeralaFULLSTACK DEVELOPER - MERN [1 YR 3 MOS]Jan. 2022 - Apr. 2023•Collaborated with a team of developers and designers to design, develop, and launch dynamic web applications usingReact js, Nextj s,Node js, meeting project requirements.•Successfullydeployed 3+ projectsto the cloud using Git, ensuringscalability and maintainabilityof the codebase.•Led comprehensive code reviews with the team lead, yielding20% fewer bugsand elevating application stability.•Enhanced user experience with dynamic JavaScript and CSS animations, powered byFramer Motion, driving a40% increase in user inter-actionand heightened application engagement.ProjectsXarchView ProjectSAAS-BASED ARCHITECTURAL PROJECT MANAGEMENT ERP SOLUTION, STREAMLINING PROJECT TASKS AND COMMUNICATION.•Implemented secure role-based authenticationfor controlled access, along withConditional UI Renderingbased on user roles.•UtilizedFormik and Yup for seamless form handling, validation, and included file upload usingReact FilePond. Increased form submissionefficiency by20%.•Managed application state efficiently usingRedux Toolkit. Resulting inoptimized rendering and enhanced data flow.•Enabledlive chat functionalityusingSocket.IOfor real-time user interaction. Boosted user engagement by30%.•Applied advancedReact optimization techniques, includingcode splitting, debouncing, for enhanced performance.REACT.JSREDUX TOOLKITMUIREST APIREACT ROUTERNODEEXPRESSGITE-commerceView ProjectONLINE MERCHANDISE STORE•Optimized performance by incorporating lazy loading of product images and content, resulting in a significant33% reduction in initial pageload time.•Leveraged React, Redux, Zustand, and Material UI to architect dynamic frontend components, achieving over70% component reusabilitythrough strategicutilization of MUI components.•ImplementedRazorpay for secure online payments, offering seamless UPI transactions for effortless and convenient user experience.•Maximized the potential of Formik byimplementing comprehensive form validations, resulting inerror-free data submission.REACT.JSREDUXZUSTANDMUIREST APIRAZORPAYNODEEXPRESSGITWallpaperNextView ProjectA WALLPAPER WEBSITE USING NEXT.JS 12 AS A PERSONAL PROJECT•Prepared GROQ queries to retrieve and display data fromSanity.io CMSin web application.•RevampedSEO techniquesand applied keyword optimization strategies, resulting in a 40% increase in website visibility and a 60% boost inorganic traffic within 6 months.•Enhanced website performance through the application ofoptimization techniques, resulting in significantly improvedPageSpeed scoresand faster page load times.NEXT.JSSANITY CMSGROQTAILWIND CSSDAISY UIGITEducationUniversity of CalicutKozhikode,KeralaBSC IN COMPUTER SCIENCE2018 - 2021Certifications2021Certificate of Achievement,Completed Full stack Mern BootcampLearnCodeOnlineAUGUST 9, 2023KIRAN · RÉSUMÉ1',
132 | loc: { lines: { from: 1, to: 1 } }
133 | }
134 | },
135 | Document {
136 | pageContent:
137 | 'byFramer Motion, driving a40% increase in user inter-actionand heightened application engagement.ProjectsXarchView ProjectSAAS-BASED ARCHITECTURAL PROJECT MANAGEMENT ERP SOLUTION, STREAMLINING PROJECT TASKS AND COMMUNICATION.•Implemented secure role-based authenticationfor controlled access, along withConditional UI Renderingbased on user roles.•UtilizedFormik and Yup for seamless form handling, validation, and included file upload usingReact FilePond. Increased form submissionefficiency by20%.•Managed application state efficiently usingRedux Toolkit. Resulting inoptimized rendering and enhanced data flow.•Enabledlive chat functionalityusingSocket.IOfor real-time user interaction. Boosted user engagement by30%.•Applied advancedReact optimization techniques, includingcode splitting, debouncing, for enhanced performance.REACT.JSREDUX TOOLKITMUIREST APIREACT ROUTERNODEEXPRESSGITE-commerceView ProjectONLINE MERCHANDISE STORE•Optimized performance by incorporating lazy loading of product',
138 | metadata: {
139 | pageNumber: 1,
140 | text:
141 | 'Kiran.SFRONTEND DEVELOPER · BSC COMPUTER SCIENCE · UNIVERSITY OF CALICUT7306405336|imkir4n@gmail.com|kiranfolio.vercel.app|Kiranism|kiranismTechnical SkillsFrontend DevelopmentReact, Next.js, Javascript (ES6+), Typescript, HTML, CSS, Redux, React Router, Material UI, Tailwind CSSBuild Tools and othersJest, Vite, Webpack, Babel, Node, NPM, Express, MongoDB, Git, Github, RESTful APIsWork ExperienceSTACKROOTS TECHNOLOGY SOLUTIONSCyberpark Kozhikode, KeralaFULLSTACK DEVELOPER - MERN [1 YR 3 MOS]Jan. 2022 - Apr. 2023•Collaborated with a team of developers and designers to design, develop, and launch dynamic web applications usingReact js, Nextj s,Node js, meeting project requirements.•Successfullydeployed 3+ projectsto the cloud using Git, ensuringscalability and maintainabilityof the codebase.•Led comprehensive code reviews with the team lead, yielding20% fewer bugsand elevating application stability.•Enhanced user experience with dynamic JavaScript and CSS animations, powered byFramer Motion, driving a40% increase in user inter-actionand heightened application engagement.ProjectsXarchView ProjectSAAS-BASED ARCHITECTURAL PROJECT MANAGEMENT ERP SOLUTION, STREAMLINING PROJECT TASKS AND COMMUNICATION.•Implemented secure role-based authenticationfor controlled access, along withConditional UI Renderingbased on user roles.•UtilizedFormik and Yup for seamless form handling, validation, and included file upload usingReact FilePond. Increased form submissionefficiency by20%.•Managed application state efficiently usingRedux Toolkit. Resulting inoptimized rendering and enhanced data flow.•Enabledlive chat functionalityusingSocket.IOfor real-time user interaction. Boosted user engagement by30%.•Applied advancedReact optimization techniques, includingcode splitting, debouncing, for enhanced performance.REACT.JSREDUX TOOLKITMUIREST APIREACT ROUTERNODEEXPRESSGITE-commerceView ProjectONLINE MERCHANDISE STORE•Optimized performance by incorporating lazy loading of product images and content, resulting in a significant33% reduction in initial pageload time.•Leveraged React, Redux, Zustand, and Material UI to architect dynamic frontend components, achieving over70% component reusabilitythrough strategicutilization of MUI components.•ImplementedRazorpay for secure online payments, offering seamless UPI transactions for effortless and convenient user experience.•Maximized the potential of Formik byimplementing comprehensive form validations, resulting inerror-free data submission.REACT.JSREDUXZUSTANDMUIREST APIRAZORPAYNODEEXPRESSGITWallpaperNextView ProjectA WALLPAPER WEBSITE USING NEXT.JS 12 AS A PERSONAL PROJECT•Prepared GROQ queries to retrieve and display data fromSanity.io CMSin web application.•RevampedSEO techniquesand applied keyword optimization strategies, resulting in a 40% increase in website visibility and a 60% boost inorganic traffic within 6 months.•Enhanced website performance through the application ofoptimization techniques, resulting in significantly improvedPageSpeed scoresand faster page load times.NEXT.JSSANITY CMSGROQTAILWIND CSSDAISY UIGITEducationUniversity of CalicutKozhikode,KeralaBSC IN COMPUTER SCIENCE2018 - 2021Certifications2021Certificate of Achievement,Completed Full stack Mern BootcampLearnCodeOnlineAUGUST 9, 2023KIRAN · RÉSUMÉ1',
142 | loc: { lines: { from: 1, to: 1 } }
143 | }
144 | },
145 | Document {
146 | pageContent:
147 | 'images and content, resulting in a significant33% reduction in initial pageload time.•Leveraged React, Redux, Zustand, and Material UI to architect dynamic frontend components, achieving over70% component reusabilitythrough strategicutilization of MUI components.•ImplementedRazorpay for secure online payments, offering seamless UPI transactions for effortless and convenient user experience.•Maximized the potential of Formik byimplementing comprehensive form validations, resulting inerror-free data submission.REACT.JSREDUXZUSTANDMUIREST APIRAZORPAYNODEEXPRESSGITWallpaperNextView ProjectA WALLPAPER WEBSITE USING NEXT.JS 12 AS A PERSONAL PROJECT•Prepared GROQ queries to retrieve and display data fromSanity.io CMSin web application.•RevampedSEO techniquesand applied keyword optimization strategies, resulting in a 40% increase in website visibility and a 60% boost inorganic traffic within 6 months.•Enhanced website performance through the application ofoptimization techniques, resulting in',
148 | metadata: {
149 | pageNumber: 1,
150 | text:
151 | 'Kiran.SFRONTEND DEVELOPER · BSC COMPUTER SCIENCE · UNIVERSITY OF CALICUT7306405336|imkir4n@gmail.com|kiranfolio.vercel.app|Kiranism|kiranismTechnical SkillsFrontend DevelopmentReact, Next.js, Javascript (ES6+), Typescript, HTML, CSS, Redux, React Router, Material UI, Tailwind CSSBuild Tools and othersJest, Vite, Webpack, Babel, Node, NPM, Express, MongoDB, Git, Github, RESTful APIsWork ExperienceSTACKROOTS TECHNOLOGY SOLUTIONSCyberpark Kozhikode, KeralaFULLSTACK DEVELOPER - MERN [1 YR 3 MOS]Jan. 2022 - Apr. 2023•Collaborated with a team of developers and designers to design, develop, and launch dynamic web applications usingReact js, Nextj s,Node js, meeting project requirements.•Successfullydeployed 3+ projectsto the cloud using Git, ensuringscalability and maintainabilityof the codebase.•Led comprehensive code reviews with the team lead, yielding20% fewer bugsand elevating application stability.•Enhanced user experience with dynamic JavaScript and CSS animations, powered byFramer Motion, driving a40% increase in user inter-actionand heightened application engagement.ProjectsXarchView ProjectSAAS-BASED ARCHITECTURAL PROJECT MANAGEMENT ERP SOLUTION, STREAMLINING PROJECT TASKS AND COMMUNICATION.•Implemented secure role-based authenticationfor controlled access, along withConditional UI Renderingbased on user roles.•UtilizedFormik and Yup for seamless form handling, validation, and included file upload usingReact FilePond. Increased form submissionefficiency by20%.•Managed application state efficiently usingRedux Toolkit. Resulting inoptimized rendering and enhanced data flow.•Enabledlive chat functionalityusingSocket.IOfor real-time user interaction. Boosted user engagement by30%.•Applied advancedReact optimization techniques, includingcode splitting, debouncing, for enhanced performance.REACT.JSREDUX TOOLKITMUIREST APIREACT ROUTERNODEEXPRESSGITE-commerceView ProjectONLINE MERCHANDISE STORE•Optimized performance by incorporating lazy loading of product images and content, resulting in a significant33% reduction in initial pageload time.•Leveraged React, Redux, Zustand, and Material UI to architect dynamic frontend components, achieving over70% component reusabilitythrough strategicutilization of MUI components.•ImplementedRazorpay for secure online payments, offering seamless UPI transactions for effortless and convenient user experience.•Maximized the potential of Formik byimplementing comprehensive form validations, resulting inerror-free data submission.REACT.JSREDUXZUSTANDMUIREST APIRAZORPAYNODEEXPRESSGITWallpaperNextView ProjectA WALLPAPER WEBSITE USING NEXT.JS 12 AS A PERSONAL PROJECT•Prepared GROQ queries to retrieve and display data fromSanity.io CMSin web application.•RevampedSEO techniquesand applied keyword optimization strategies, resulting in a 40% increase in website visibility and a 60% boost inorganic traffic within 6 months.•Enhanced website performance through the application ofoptimization techniques, resulting in significantly improvedPageSpeed scoresand faster page load times.NEXT.JSSANITY CMSGROQTAILWIND CSSDAISY UIGITEducationUniversity of CalicutKozhikode,KeralaBSC IN COMPUTER SCIENCE2018 - 2021Certifications2021Certificate of Achievement,Completed Full stack Mern BootcampLearnCodeOnlineAUGUST 9, 2023KIRAN · RÉSUMÉ1',
152 | loc: { lines: { from: 1, to: 1 } }
153 | }
154 | },
155 | Document {
156 | pageContent:
157 | 'significantly improvedPageSpeed scoresand faster page load times.NEXT.JSSANITY CMSGROQTAILWIND CSSDAISY UIGITEducationUniversity of CalicutKozhikode,KeralaBSC IN COMPUTER SCIENCE2018 - 2021Certifications2021Certificate of Achievement,Completed Full stack Mern BootcampLearnCodeOnlineAUGUST 9, 2023KIRAN · RÉSUMÉ1',
158 | metadata: {
159 | pageNumber: 1,
160 | text:
161 | 'Kiran.SFRONTEND DEVELOPER · BSC COMPUTER SCIENCE · UNIVERSITY OF CALICUT7306405336|imkir4n@gmail.com|kiranfolio.vercel.app|Kiranism|kiranismTechnical SkillsFrontend DevelopmentReact, Next.js, Javascript (ES6+), Typescript, HTML, CSS, Redux, React Router, Material UI, Tailwind CSSBuild Tools and othersJest, Vite, Webpack, Babel, Node, NPM, Express, MongoDB, Git, Github, RESTful APIsWork ExperienceSTACKROOTS TECHNOLOGY SOLUTIONSCyberpark Kozhikode, KeralaFULLSTACK DEVELOPER - MERN [1 YR 3 MOS]Jan. 2022 - Apr. 2023•Collaborated with a team of developers and designers to design, develop, and launch dynamic web applications usingReact js, Nextj s,Node js, meeting project requirements.•Successfullydeployed 3+ projectsto the cloud using Git, ensuringscalability and maintainabilityof the codebase.•Led comprehensive code reviews with the team lead, yielding20% fewer bugsand elevating application stability.•Enhanced user experience with dynamic JavaScript and CSS animations, powered byFramer Motion, driving a40% increase in user inter-actionand heightened application engagement.ProjectsXarchView ProjectSAAS-BASED ARCHITECTURAL PROJECT MANAGEMENT ERP SOLUTION, STREAMLINING PROJECT TASKS AND COMMUNICATION.•Implemented secure role-based authenticationfor controlled access, along withConditional UI Renderingbased on user roles.•UtilizedFormik and Yup for seamless form handling, validation, and included file upload usingReact FilePond. Increased form submissionefficiency by20%.•Managed application state efficiently usingRedux Toolkit. Resulting inoptimized rendering and enhanced data flow.•Enabledlive chat functionalityusingSocket.IOfor real-time user interaction. Boosted user engagement by30%.•Applied advancedReact optimization techniques, includingcode splitting, debouncing, for enhanced performance.REACT.JSREDUX TOOLKITMUIREST APIREACT ROUTERNODEEXPRESSGITE-commerceView ProjectONLINE MERCHANDISE STORE•Optimized performance by incorporating lazy loading of product images and content, resulting in a significant33% reduction in initial pageload time.•Leveraged React, Redux, Zustand, and Material UI to architect dynamic frontend components, achieving over70% component reusabilitythrough strategicutilization of MUI components.•ImplementedRazorpay for secure online payments, offering seamless UPI transactions for effortless and convenient user experience.•Maximized the potential of Formik byimplementing comprehensive form validations, resulting inerror-free data submission.REACT.JSREDUXZUSTANDMUIREST APIRAZORPAYNODEEXPRESSGITWallpaperNextView ProjectA WALLPAPER WEBSITE USING NEXT.JS 12 AS A PERSONAL PROJECT•Prepared GROQ queries to retrieve and display data fromSanity.io CMSin web application.•RevampedSEO techniquesand applied keyword optimization strategies, resulting in a 40% increase in website visibility and a 60% boost inorganic traffic within 6 months.•Enhanced website performance through the application ofoptimization techniques, resulting in significantly improvedPageSpeed scoresand faster page load times.NEXT.JSSANITY CMSGROQTAILWIND CSSDAISY UIGITEducationUniversity of CalicutKozhikode,KeralaBSC IN COMPUTER SCIENCE2018 - 2021Certifications2021Certificate of Achievement,Completed Full stack Mern BootcampLearnCodeOnlineAUGUST 9, 2023KIRAN · RÉSUMÉ1',
162 | loc: { lines: { from: 1, to: 1 } }
163 | }
164 | }
165 | ]
166 | ] */
167 |
--------------------------------------------------------------------------------
/src/lib/transformers.ts:
--------------------------------------------------------------------------------
1 | import { pipeline } from "@xenova/transformers";
2 |
3 | export async function embeddingTransformer(text: string) {
4 | try {
5 | console.log("transformer initialized")
6 | const generateEmbeddings = await pipeline(
7 | "feature-extraction",
8 | "Xenova/e5-large-v2"
9 | );
10 | const response = await generateEmbeddings(text.replace(/\n/g, " "), {
11 | pooling: "mean",
12 | normalize: true,
13 | });
14 | console.log("getEmb result-=>", response);
15 | return Array.from(response?.data) as number[];
16 | } catch (error) {
17 | console.log("error calling transformer for embeddings ai", error);
18 | throw error;
19 | }
20 | }
21 |
22 | // railway
23 |
--------------------------------------------------------------------------------
/src/lib/uploadThing.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { generateReactHelpers } from "@uploadthing/react/hooks";
3 |
4 | import type { OurFileRouter } from "@/app/api/uploadthing/core";
5 |
6 | export const { useUploadThing, uploadFiles } =
7 | generateReactHelpers();
8 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
8 | export function convertToAscii(inputString: string) {
9 | // remove non ascii characters
10 | const asciiString = inputString.replace(/[^\x00-\x7F]+/g, "");
11 | return asciiString;
12 | }
13 |
--------------------------------------------------------------------------------
/src/middleware.ts:
--------------------------------------------------------------------------------
1 | import { authMiddleware } from "@clerk/nextjs";
2 |
3 | // This example protects all routes including api/trpc routes
4 | // Please edit this to allow other routes to be public as needed.
5 | // See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your middleware
6 | export default authMiddleware({
7 | publicRoutes: ["/", "/api/uploadthing", "/api/welcome"],
8 | });
9 |
10 | export const config = {
11 | matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
12 | };
13 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | "./pages/**/*.{ts,tsx}",
6 | "./components/**/*.{ts,tsx}",
7 | "./app/**/*.{ts,tsx}",
8 | "./src/**/*.{ts,tsx}",
9 | ],
10 | theme: {
11 | container: {
12 | center: true,
13 | padding: "2rem",
14 | screens: {
15 | "2xl": "1400px",
16 | sm: "640px",
17 | lg: "1024px",
18 | },
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border))",
23 | input: "hsl(var(--input))",
24 | ring: "hsl(var(--ring))",
25 | background: "hsl(var(--background))",
26 | foreground: "hsl(var(--foreground))",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary))",
29 | foreground: "hsl(var(--primary-foreground))",
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary))",
33 | foreground: "hsl(var(--secondary-foreground))",
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive))",
37 | foreground: "hsl(var(--destructive-foreground))",
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted))",
41 | foreground: "hsl(var(--muted-foreground))",
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent))",
45 | foreground: "hsl(var(--accent-foreground))",
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover))",
49 | foreground: "hsl(var(--popover-foreground))",
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card))",
53 | foreground: "hsl(var(--card-foreground))",
54 | },
55 | },
56 | borderRadius: {
57 | lg: "var(--radius)",
58 | md: "calc(var(--radius) - 2px)",
59 | sm: "calc(var(--radius) - 4px)",
60 | },
61 | keyframes: {
62 | "accordion-down": {
63 | from: { height: 0 },
64 | to: { height: "var(--radix-accordion-content-height)" },
65 | },
66 | "accordion-up": {
67 | from: { height: "var(--radix-accordion-content-height)" },
68 | to: { height: 0 },
69 | },
70 | },
71 | animation: {
72 | "accordion-down": "accordion-down 0.2s ease-out",
73 | "accordion-up": "accordion-up 0.2s ease-out",
74 | },
75 | },
76 | },
77 | plugins: [require("tailwindcss-animate"), require("tailwind-scrollbar")],
78 | };
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------