Start on new sidebar (#456)

Co-authored-by: shadcn <m@shadcn.com>
This commit is contained in:
Jared Palmer 2024-10-24 16:35:51 -04:00 committed by GitHub
parent 7faa5f1c9f
commit a68eb2a011
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 2350 additions and 800 deletions

8
app/(chat)/actions.ts Normal file
View file

@ -0,0 +1,8 @@
'use server';
import { cookies } from 'next/headers';
export async function saveModel(model: string) {
const cookieStore = await cookies();
cookieStore.set('model', model);
}

View file

@ -1,38 +1,47 @@
import { convertToCoreMessages, Message, streamText } from "ai";
import { z } from "zod";
import { convertToCoreMessages, Message, streamText } from 'ai';
import { z } from 'zod';
import { customModel } from "@/ai";
import { auth } from "@/app/(auth)/auth";
import { deleteChatById, getChatById, saveChat } from "@/db/queries";
import { customModel } from '@/ai';
import { auth } from '@/app/(auth)/auth';
import { deleteChatById, getChatById, saveChat } from '@/db/queries';
import { Model, models } from '@/lib/model';
export async function POST(request: Request) {
const { id, messages }: { id: string; messages: Array<Message> } =
const {
id,
messages,
model,
}: { id: string; messages: Array<Message>; model: Model['name'] } =
await request.json();
const session = await auth();
if (!session) {
return new Response("Unauthorized", { status: 401 });
return new Response('Unauthorized', { status: 401 });
}
if (!models.find((m) => m.name === model)) {
return new Response('Model not found', { status: 404 });
}
const coreMessages = convertToCoreMessages(messages);
const result = await streamText({
model: customModel,
model: customModel(model),
system:
"you are a friendly assistant! keep your responses concise and helpful.",
'you are a friendly assistant! keep your responses concise and helpful.',
messages: coreMessages,
maxSteps: 5,
tools: {
getWeather: {
description: "Get the current weather at a location",
description: 'Get the current weather at a location',
parameters: z.object({
latitude: z.number(),
longitude: z.number(),
}),
execute: async ({ latitude, longitude }) => {
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto`,
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto`
);
const weatherData = await response.json();
@ -49,13 +58,13 @@ export async function POST(request: Request) {
userId: session.user.id,
});
} catch (error) {
console.error("Failed to save chat");
console.error('Failed to save chat');
}
}
},
experimental_telemetry: {
isEnabled: true,
functionId: "stream-text",
functionId: 'stream-text',
},
});
@ -64,30 +73,30 @@ export async function POST(request: Request) {
export async function DELETE(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");
const id = searchParams.get('id');
if (!id) {
return new Response("Not Found", { status: 404 });
return new Response('Not Found', { status: 404 });
}
const session = await auth();
if (!session || !session.user) {
return new Response("Unauthorized", { status: 401 });
return new Response('Unauthorized', { status: 401 });
}
try {
const chat = await getChatById({ id });
if (chat.userId !== session.user.id) {
return new Response("Unauthorized", { status: 401 });
return new Response('Unauthorized', { status: 401 });
}
await deleteChatById({ id });
return new Response("Chat deleted", { status: 200 });
return new Response('Chat deleted', { status: 200 });
} catch (error) {
return new Response("An error occurred while processing your request", {
return new Response('An error occurred while processing your request', {
status: 500,
});
}

View file

@ -1,11 +1,13 @@
import { CoreMessage } from "ai";
import { notFound } from "next/navigation";
import { CoreMessage } from 'ai';
import { cookies } from 'next/headers';
import { notFound } from 'next/navigation';
import { auth } from "@/app/(auth)/auth";
import { Chat as PreviewChat } from "@/components/custom/chat";
import { getChatById } from "@/db/queries";
import { Chat } from "@/db/schema";
import { convertToUIMessages, generateUUID } from "@/lib/utils";
import { auth } from '@/app/(auth)/auth';
import { Chat as PreviewChat } from '@/components/custom/chat';
import { getChatById } from '@/db/queries';
import { Chat } from '@/db/schema';
import { DEFAULT_MODEL_NAME, models } from '@/lib/model';
import { convertToUIMessages } from '@/lib/utils';
export default async function Page(props: { params: Promise<any> }) {
const params = await props.params;
@ -32,5 +34,16 @@ export default async function Page(props: { params: Promise<any> }) {
return notFound();
}
return <PreviewChat id={chat.id} initialMessages={chat.messages} />;
const cookieStore = await cookies();
const value = cookieStore.get('model')?.value;
const selectedModelName =
models.find((m) => m.name === value)?.name || DEFAULT_MODEL_NAME;
return (
<PreviewChat
id={chat.id}
initialMessages={chat.messages}
selectedModelName={selectedModelName}
/>
);
}

22
app/(chat)/layout.tsx Normal file
View file

@ -0,0 +1,22 @@
import { cookies } from 'next/headers';
import { AppSidebar } from '@/components/custom/app-sidebar';
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
import { auth } from '../(auth)/auth';
export default async function Layout({
children,
}: {
children: React.ReactNode;
}) {
const [session, cookieStore] = await Promise.all([auth(), cookies()]);
const isCollapsed = cookieStore.get('sidebar:state')?.value !== 'true';
return (
<SidebarProvider defaultOpen={!isCollapsed}>
<AppSidebar user={session?.user} />
<SidebarInset>{children}</SidebarInset>
</SidebarProvider>
);
}

View file

@ -1,7 +1,23 @@
import { Chat } from "@/components/custom/chat";
import { generateUUID } from "@/lib/utils";
import { cookies } from 'next/headers';
import { Chat } from '@/components/custom/chat';
import { DEFAULT_MODEL_NAME, models } from '@/lib/model';
import { generateUUID } from '@/lib/utils';
export default async function Page() {
const id = generateUUID();
return <Chat key={id} id={id} initialMessages={[]} />;
const cookieStore = await cookies();
const value = cookieStore.get('model')?.value;
const selectedModelName =
models.find((m) => m.name === value)?.name || DEFAULT_MODEL_NAME;
return (
<Chat
key={id}
id={id}
initialMessages={[]}
selectedModelName={selectedModelName}
/>
);
}

View file

@ -49,6 +49,14 @@
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 10% 3.9%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 5.9% 94%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--background: 240 10% 3.9%;
@ -75,6 +83,14 @@
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}
@ -88,17 +104,17 @@
}
@font-face {
font-family: "geist";
font-family: 'geist';
font-style: normal;
font-weight: 100 900;
src: url(/fonts/geist.woff2) format("woff2");
src: url(/fonts/geist.woff2) format('woff2');
}
@font-face {
font-family: "geist-mono";
font-family: 'geist-mono';
font-style: normal;
font-weight: 100 900;
src: url(/fonts/geist-mono.woff2) format("woff2");
src: url(/fonts/geist-mono.woff2) format('woff2');
}
}
@ -107,7 +123,7 @@
pointer-events: none !important;
}
*[class^="text-"] {
*[class^='text-'] {
color: transparent;
@apply rounded-md bg-foreground/20 select-none animate-pulse;
}

View file

@ -1,7 +1,6 @@
import { Metadata } from 'next';
import { Toaster } from 'sonner';
import { Navbar } from '@/components/custom/navbar';
import { ThemeProvider } from '@/components/custom/theme-provider';
import './globals.css';
@ -65,7 +64,6 @@ export default async function RootLayout({
disableTransitionOnChange
>
<Toaster position="top-center" />
<Navbar />
{children}
</ThemeProvider>
</body>