Skip to content

Commit

Permalink
Merge pull request #13 from chhakuli123/11-feature-request-intigratio…
Browse files Browse the repository at this point in the history
…n-with-llm

11 feature request intigration with llm
  • Loading branch information
chhakuli123 authored Jul 22, 2024
2 parents b589596 + f0caeef commit 0a083ff
Show file tree
Hide file tree
Showing 21 changed files with 789 additions and 6,442 deletions.
6,419 changes: 0 additions & 6,419 deletions package-lock.json

This file was deleted.

14 changes: 13 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,30 @@
"src/**/*.{js,jsx,ts,tsx}": "npm run lint-prettier"
},
"dependencies": {
"@langchain/community": "^0.2.15",
"@langchain/openai": "^0.2.1",
"@langchain/pinecone": "^0.0.7",
"@pinecone-database/pinecone": "2.2.2",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-slot": "^1.1.0",
"ai": "^2.1.34",
"ai-stream-experimental": "^2.2.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"framer-motion": "^11.2.12",
"git-repo-parser": "^2.0.6",
"langchain": "^0.2.8",
"lucide-react": "^0.399.0",
"next": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.2.1",
"react-intersection-observer": "^9.10.3",
"react-markdown": "^8.0.7",
"react-wrap-balancer": "^1.1.1",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.1.1",
Expand Down
39 changes: 39 additions & 0 deletions src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { NextRequest, NextResponse } from "next/server";
import { callChain } from "@/lib/langchain";
import { Message } from "ai";

const formatMessage = (message: Message) => {
return `${message.role === "user" ? "Human" : "Assistant"}: ${
message.content
}`;
};

export async function POST(req: NextRequest) {
const body = await req.json();
const messages: Message[] = body.messages ?? [];
console.log("Messages ", messages);
const formattedPreviousMessages = messages.slice(0, -1).map(formatMessage);
const question = messages[messages.length - 1].content;

console.log("Chat history ", formattedPreviousMessages.join("\n"));

if (!question) {
return NextResponse.json("Error: No question in the request", {
status: 400,
});
}

try {
const streamingTextResponse = callChain({
question,
chatHistory: formattedPreviousMessages.join("\n"),
});

return streamingTextResponse;
} catch (error) {
console.error("Internal server error ", error);
return NextResponse.json("Error: Something went wrong. Try again!", {
status: 500,
});
}
}
14 changes: 14 additions & 0 deletions src/app/chat/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

import { Chat } from "@/components/chat/chat";

export default function chat() {
return (
<main className="relative container flex min-h-screen flex-col">
<div className="flex flex-1 py-4">
<div className="w-full">
<Chat />
</div>
</div>
</main>
);
}
82 changes: 82 additions & 0 deletions src/components/chat/chat-line.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Balancer from "react-wrap-balancer";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Message } from "ai/react";
import ReactMarkdown from "react-markdown";
import { formattedText } from "@/lib/utils";

const convertNewLines = (text: string) =>
text.split("\n").map((line, i) => (
<span key={i}>
{line}
<br />
</span>
));

interface ChatLineProps extends Partial<Message> {
sources: string[];
}

export function ChatLine({
role = "assistant",
content,
sources,
}: ChatLineProps) {
if (!content) {
return null;
}
const formattedMessage = convertNewLines(content);

return (
<div>
<Card className="mb-2">
<CardHeader>
<CardTitle
className={
role != "assistant"
? "text-amber-500 dark:text-amber-200"
: "text-blue-500 dark:text-blue-200"
}
>
{role == "assistant" ? "AI" : "You"}
</CardTitle>
</CardHeader>
<CardContent className="text-sm">
<Balancer>{formattedMessage}</Balancer>
</CardContent>
<CardFooter>
<CardDescription className="w-full">
{sources && sources.length ? (
<Accordion type="single" collapsible className="w-full">
{sources.map((source, index) => (
<AccordionItem value={`source-${index}`} key={index}>
<AccordionTrigger>{`Source ${index + 1}`}</AccordionTrigger>
<AccordionContent>
<ReactMarkdown >
{formattedText(source)}
</ReactMarkdown>
</AccordionContent>
</AccordionItem>
))}
</Accordion>
) : (
<></>
)}
</CardDescription>
</CardFooter>
</Card>
</div>
);
}
53 changes: 53 additions & 0 deletions src/components/chat/chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";

import { scrollToBottom, initialMessages, getSources } from "@/lib/utils";
import { ChatLine } from "./chat-line";
import { useChat, Message } from "ai-stream-experimental/react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { useEffect, useRef } from "react";

export function Chat() {
const containerRef = useRef<HTMLDivElement | null>(null);
const { messages, input, handleInputChange, handleSubmit, isLoading, data } =
useChat({
initialMessages: initialMessages.map((message) => ({
...message,
role: message.role as "function" | "system" | "user" | "assistant",
})),
});

useEffect(() => {
setTimeout(() => scrollToBottom(containerRef), 100);
}, [messages]);

return (
<div className="rounded-2xl border h-[75vh] flex flex-col justify-between">
<div className="p-6 overflow-auto" ref={containerRef}>
{messages.map(({ id, role, content }: Message, index) => (
<ChatLine
key={id}
role={role}
content={content}
// Start from the third message of the assistant
sources={data?.length ? getSources(data, role, index) : []}
/>
))}
</div>

<form onSubmit={handleSubmit} className="p-4 flex clear-both">
<Input
value={input}
placeholder={"Type to chat with AI..."}
onChange={handleInputChange}
className="mr-2"
/>

<Button type="submit" className="w-24">
{isLoading ? <Spinner /> : "Ask"}
</Button>
</form>
</div>
);
}
58 changes: 58 additions & 0 deletions src/components/ui/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client";

import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDown } from "lucide-react";

import { cn } from "@/lib/utils";

const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
));
AccordionItem.displayName = "AccordionItem";

const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
));

AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
42 changes: 20 additions & 22 deletions src/components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,47 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from '@/lib/utils';
import { cn } from "@/lib/utils";

const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-gray-950 dark:focus-visible:ring-gray-300',
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-gray-950 dark:focus-visible:ring-gray-300",
{
variants: {
variant: {
default:
'bg-gray-900 text-gray-50 hover:bg-gray-900/90 dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/90',
default: "bg-gray-900 text-gray-50 hover:bg-gray-900/90 dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/90",
destructive:
'bg-red-500 text-gray-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/90',
"bg-red-500 text-gray-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/90",
outline:
'border border-gray-200 bg-white hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50',
"border border-gray-200 bg-white hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50",
secondary:
'bg-gray-100 text-gray-900 hover:bg-gray-100/80 dark:bg-gray-800 dark:text-gray-50 dark:hover:bg-gray-800/80',
ghost:
'hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-50',
link: 'text-gray-900 underline-offset-4 hover:underline dark:text-gray-50',
"bg-gray-100 text-gray-900 hover:bg-gray-100/80 dark:bg-gray-800 dark:text-gray-50 dark:hover:bg-gray-800/80",
ghost: "hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-50",
link: "text-gray-900 underline-offset-4 hover:underline dark:text-gray-50",
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: 'default',
size: 'default',
variant: "default",
size: "default",
},
}
);

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
Expand All @@ -53,6 +51,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
);
}
);
Button.displayName = 'Button';
Button.displayName = "Button";

export { Button, buttonVariants };
Loading

0 comments on commit 0a083ff

Please sign in to comment.