Skip to content

Commit

Permalink
Merge pull request #86 from wtfdivyansh/feat/onboarding-ai
Browse files Browse the repository at this point in the history
core: add onboarding ai agent
  • Loading branch information
SkidGod4444 authored Dec 10, 2024
2 parents 3f510e7 + a699836 commit d6645ae
Show file tree
Hide file tree
Showing 36 changed files with 1,020 additions and 40 deletions.
3 changes: 2 additions & 1 deletion apps/api/app/v1/[[...route]]/health.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Hono } from "hono";
const app = new Hono().get("/", (c) => {
const app = new Hono()
.get("/", (c) => {
return c.json({
message: "i am alive",
status: 200,
Expand Down
3 changes: 3 additions & 0 deletions apps/api/app/v1/[[...route]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const allowedOrigins = [
"http://localhost:3004",
"https://www.plura.pro",
"https://app.plura.pro",
"https://status.plura.pro",
];

app.use(
Expand Down Expand Up @@ -52,3 +53,5 @@ const DELETE = handle(app);
const OPTIONS = handle(app);

export { GET, PATCH, POST, DELETE, OPTIONS };


2 changes: 1 addition & 1 deletion apps/api/app/v1/[[...route]]/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { auth } from "@repo/auth";
const app = new Hono()
.get("/", async (c) => {
const session = await auth.api.getSession({ headers: c.req.raw.headers });

if (!session) return c.json({ message: "no session found" }, 401);

return c.json({
Expand Down
3 changes: 2 additions & 1 deletion apps/api/app/v1/[[...route]]/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { prisma } from "@repo/db";
import { auth } from "@repo/auth";

const app = new Hono()
.get("/self", async (c) => {
.get("/self", async (c) => {
const currentUser = await auth.api.getSession({
headers: c.req.raw.headers,
});
Expand All @@ -23,6 +23,7 @@ const app = new Hono()
id: currentUser.user.id,
},
});

return c.json(
{
user,
Expand Down
4 changes: 2 additions & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"dev": "next dev -p 3001 --turbopack",
"prebuild": "turbo --filter=@repo/db db:generate",
"build": "next build",
"start": "next start -p 5555",
"preview": "next build && next start -p 5555",
"start": "next start -p 3001",
"preview": "next build && next start -p 3001",
"lint": "next lint",
"lint:fix": "next lint --fix",
"test": "vitest --run",
Expand Down
156 changes: 156 additions & 0 deletions apps/app/actions/action.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"use server";
import { ReactNode } from "react";
import { createStreamableValue, getMutableAIState, streamUI } from "ai/rsc";
import { togetherai } from "@ai-sdk/togetherai";
import { AI } from "@/lib/ai";
import { BotMessage } from "@/components/custom/onboarding/message";
import BeatLoader from "@/components/custom/onboarding/BeatLoader";
import { getSession } from "./session";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import Proceed from "@/components/custom/onboarding/proceed";
import WorkspaceForm from "@/components/custom/onboarding/workspace-form";
import {sleep } from "@/lib/utils";
import { CoreMessage, generateId,ToolInvocation } from "ai";
export type ServerMessage = {
id?: number;
name?: "proceed" | "workspace" ;
role: "user" | "assistant";
content: string;
};

export type ClientMessage = {
id: number;
role: "user" | "assistant";
display: ReactNode;
toolInvocations?: ToolInvocation[]
};

export const sendMessage = async (
message: string
): Promise<ClientMessage> => {
const history = getMutableAIState<typeof AI>();
console.log(history.get().length)
console.log("ai",history.get())

history.update([...history.get(), { role: "user", content: message }]);

const response = await streamUI({
model: togetherai("meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"),
system: `
You are an onboarding assitand and you are helping users to onboard them to Plura AI.
-any question not related to the onbaording should not be answered by you
-if someone asks any message that is not related to the onboarding then you should respond with the exact same text "wlecome to Plura"
-if the message comes as "should we continue" then call proceed tool
The workflow is as follows:
-User sends "yes" or "no" to proceed
-If the user sends "yes" then the workflow is as follows:
-then you call the workspace tool
- If the user sends "no", respond with exactly: "please create a workspace to continue your onboarding" Do not call any tools for "no"
- Only trigger the proceed tool when asking: "should we continue?"
-If the user sends any message after workspace tool is called then you should respond with the same text:"Please create a workspace to continue"
-dont call any tools if the user doesnt creates a workspace
-If the message comes as workspace {workspaceName} created then respond with the exact same text "your first workspace has been created with name: {workspaceName} created" and dont call any tools
`,
messages: [{ role: "user", content: message }, ...history.get()],
temperature: 0,
initial: (
<BotMessage>
<BeatLoader />
</BotMessage>
),
text: async function ({ content, done }) {
await sleep(1000);
if (done) {
history.done([...history.get(), { role: "assistant", content }]);
}
return <BotMessage>{content}</BotMessage>;
},
tools: {
workspace: {
description:
"when the user responds with yes then render the workspace form",
parameters: z.object({}),
generate: async function* ({}) {
yield (
<BotMessage>
<BeatLoader />
</BotMessage>
);
console.log("before");
history.done([
...history.get(),
{ role: "assistant", content: "workspace form rendered" },
]);
console.log("history", history.get());
console.log("after");

return (
<BotMessage>
<WorkspaceForm />
</BotMessage>
);
},
},
proceed: {
description: `should we continue option that contains yes and no options`,
parameters: z.object({}),
generate: async function* ({}) {
yield (
<BotMessage>
<BeatLoader />
</BotMessage>
);

history.done([
...history.get(),
{ role: "assistant", content: "should be continue rendered" },
]);

return (
<BotMessage>
<Proceed />
</BotMessage>
);
},
},
},
});

return {
id: Date.now(),
role: "assistant" as const,
display: response.value,
};
};
export const sendAiGreeting = async ():Promise<ClientMessage[]> => {
const session= await getSession()
const {name,email} = session!.user
const contentString = `Hi ${name}, welcome to Plura AI!.Your email is ${email}.I am going to help you with oboarding your acccount`
const history = getMutableAIState<typeof AI>();
console.log("greeting history", history.get())
const value = await streamUI({
model: togetherai("meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"),
system: ` always reply the exact same text exactly as it is: ${contentString}
`,
messages: history.get(),
initial: (
<BotMessage>
<BeatLoader />
</BotMessage>
),
text: async function ({ content, done }) {
await sleep(1000);
if (done) {
history.done([...history.get(), { role: "assistant", content }]);
}
return <BotMessage>{content}</BotMessage>;
},
});

return [{
id: Date.now(),
role: "assistant" as const,
display: value.value,
}]
}
17 changes: 17 additions & 0 deletions apps/app/actions/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use server"
import { betterFetch } from "@better-fetch/fetch";
import { Session } from "@repo/auth";
import { headers } from "next/headers";

export const getSession = async () => {

const response = await betterFetch<Session>("http://localhost:3001/api/auth/get-session",
{
baseURL: "http://localhost:3002",
headers: {
cookie: (await headers()).get("cookie") || "",
},
});
return response.data

}
5 changes: 5 additions & 0 deletions apps/app/app/(onboarding)/onboarding/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AI } from "@/lib/ai";

export default function OnboardingLayout({children}: {children: React.ReactNode}) {
return <AI>{children}</AI>;
}
6 changes: 6 additions & 0 deletions apps/app/app/(onboarding)/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Chatbox from "@/components/custom/onboarding/chatbox";

export default function Onboarding() {

return <Chatbox />
}
20 changes: 20 additions & 0 deletions apps/app/app/(routes)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Infobar from "@/components/custom/infobar/infobar";
import ProgressBar from "@/components/custom/progress.bar";
import { AppSidebar } from "@/components/custom/sidebar/sidebar";
import { SidebarProvider } from "@/components/ui/sidebar";
import { cookies } from "next/headers";
async function RouteLayout({children}: {children: React.ReactNode}) {
const cookieStore = await cookies();
const defaultOpen = cookieStore.get("plura-sidebar:state")?.value === "true";
return (
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<div className="p-2">
<ProgressBar />
<Infobar />
{children}
</div>
</SidebarProvider>
);
}
export default RouteLayout;
20 changes: 4 additions & 16 deletions apps/app/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import "./globals.css";
import { ThemeProvider } from "@/hooks/theme-provider";
import { SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/custom/sidebar/sidebar";
import { cookies } from "next/headers";
import Infobar from "@/components/custom/infobar/infobar";
import ProgressBar from "@/components/custom/progress.bar";
import { PosthogProvider } from "@/hooks/posthog";

import { AI } from "@/lib/ai";
import { Toaster } from "sonner";
export const metadata: Metadata = {
title: "Plura",
description: "Generated by create next app",
Expand All @@ -19,8 +15,6 @@ async function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const defaultOpen = cookieStore.get("plura-sidebar:state")?.value === "true";

return (
<html lang="en" suppressHydrationWarning>
Expand All @@ -34,14 +28,8 @@ async function RootLayout({
enableSystem
disableTransitionOnChange
>
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<div className="p-2">
<ProgressBar />
<Infobar />
{children}
</div>
</SidebarProvider>
<Toaster />
{children}
</ThemeProvider>
</body>
</PosthogProvider>
Expand Down
27 changes: 27 additions & 0 deletions apps/app/components/custom/onboarding/BeatLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import { motion } from "motion/react";

export default function BeatLoader() {
return (
<div className="flex backdrop:space-x-1">
{[0, 1, 2].map((index) => (
<motion.div
key={index}
className="w-3 h-3 bg-muted rounded-full"
animate={{
scale: [1, 0.5, 1],
opacity: [1, 0.5, 1],
}}
transition={{
duration: 0.6,
repeat: Infinity,
repeatType: "loop",
ease: "easeInOut",
delay: index * 0.2,
}}
/>
))}
</div>
);
}
34 changes: 34 additions & 0 deletions apps/app/components/custom/onboarding/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Textarea } from "@/components/ui/textarea";
import {ArrowUp} from "lucide-react";

export function ChatInput() {
return (
<div className="fixed inset-x-0 w-full bottom-4 ">
<div className="max-w-2xl mx-auto ">
<form>
<div className="grow border border-card flex flex-col justify-center items-center rounded-2xl bg-card p-1 shadow-md ">
<Textarea
id="textarea-17"
className="resize-none placeholder:text-neutral-600"
placeholder="Leave a comment"
/>

<div className=" px-2 self-end group">
<div className="p-1 rounded-md bg-woodsmoke-700 border border-neutral-700">
<ArrowUp
size={20}
strokeWidth={2}
aria-hidden="true"
className="text-neutral-500 group-hover:text-neutral-200 "
/>
</div>
</div>
</div>
</form>
</div>
</div>
);
}



16 changes: 16 additions & 0 deletions apps/app/components/custom/onboarding/chatList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { UIState } from "@/lib/ai";

export function ChatList({ messages }: { messages: UIState[number][] }) {

console.log(messages)

return (
<div className="relative mx-auto w-full md:p-0 p-4 ">
{messages.map((message, index) => (
<div key={index} className="pb-4">
{message.display}
</div>
))}
</div>
);
}
Loading

0 comments on commit d6645ae

Please sign in to comment.