24 | {children}
25 |
26 |
34 | {children}
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/hooks/use-messages.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { useScrollToBottom } from './use-scroll-to-bottom';
3 | import type { UseChatHelpers } from '@ai-sdk/react';
4 |
5 | export function useMessages({
6 | chatId,
7 | status,
8 | }: {
9 | chatId: string;
10 | status: UseChatHelpers['status'];
11 | }) {
12 | const {
13 | containerRef,
14 | endRef,
15 | isAtBottom,
16 | scrollToBottom,
17 | onViewportEnter,
18 | onViewportLeave,
19 | } = useScrollToBottom();
20 |
21 | const [hasSentMessage, setHasSentMessage] = useState(false);
22 |
23 | useEffect(() => {
24 | if (chatId) {
25 | scrollToBottom('instant');
26 | setHasSentMessage(false);
27 | }
28 | }, [chatId, scrollToBottom]);
29 |
30 | useEffect(() => {
31 | if (status === 'submitted') {
32 | setHasSentMessage(true);
33 | }
34 | }, [status]);
35 |
36 | return {
37 | containerRef,
38 | endRef,
39 | isAtBottom,
40 | scrollToBottom,
41 | onViewportEnter,
42 | onViewportLeave,
43 | hasSentMessage,
44 | };
45 | }
46 |
--------------------------------------------------------------------------------
/app/(chat)/api/history/route.ts:
--------------------------------------------------------------------------------
1 | import { auth } from '@/app/(auth)/auth';
2 | import { NextRequest } from 'next/server';
3 | import { getChatsByUserId } from '@/lib/db/queries';
4 |
5 | export async function GET(request: NextRequest) {
6 | const { searchParams } = request.nextUrl;
7 |
8 | const limit = parseInt(searchParams.get('limit') || '10');
9 | const startingAfter = searchParams.get('starting_after');
10 | const endingBefore = searchParams.get('ending_before');
11 |
12 | if (startingAfter && endingBefore) {
13 | return Response.json(
14 | 'Only one of starting_after or ending_before can be provided!',
15 | { status: 400 },
16 | );
17 | }
18 |
19 | const session = await auth();
20 |
21 | if (!session?.user?.id) {
22 | return Response.json('Unauthorized!', { status: 401 });
23 | }
24 |
25 | try {
26 | const chats = await getChatsByUserId({
27 | id: session.user.id,
28 | limit,
29 | startingAfter,
30 | endingBefore,
31 | });
32 |
33 | return Response.json(chats);
34 | } catch (_) {
35 | return Response.json('Failed to fetch chats!', { status: 500 });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/ai/providers.ts:
--------------------------------------------------------------------------------
1 | import {
2 | customProvider,
3 | extractReasoningMiddleware,
4 | wrapLanguageModel,
5 | } from 'ai';
6 | import { xai } from '@ai-sdk/xai';
7 | import { isTestEnvironment } from '../constants';
8 | import {
9 | artifactModel,
10 | chatModel,
11 | reasoningModel,
12 | titleModel,
13 | } from './models.test';
14 |
15 | export const myProvider = isTestEnvironment
16 | ? customProvider({
17 | languageModels: {
18 | 'chat-model': chatModel,
19 | 'chat-model-reasoning': reasoningModel,
20 | 'title-model': titleModel,
21 | 'artifact-model': artifactModel,
22 | },
23 | })
24 | : customProvider({
25 | languageModels: {
26 | 'chat-model': xai('grok-2-vision-1212'),
27 | 'chat-model-reasoning': wrapLanguageModel({
28 | model: xai('grok-3-mini-beta'),
29 | middleware: extractReasoningMiddleware({ tagName: 'think' }),
30 | }),
31 | 'title-model': xai('grok-2-1212'),
32 | 'artifact-model': xai('grok-2-1212'),
33 | },
34 | imageModels: {
35 | 'small-model': xai.image('grok-2-image'),
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/artifacts/image/server.ts:
--------------------------------------------------------------------------------
1 | import { myProvider } from '@/lib/ai/providers';
2 | import { createDocumentHandler } from '@/lib/artifacts/server';
3 | import { experimental_generateImage } from 'ai';
4 |
5 | export const imageDocumentHandler = createDocumentHandler<'image'>({
6 | kind: 'image',
7 | onCreateDocument: async ({ title, dataStream }) => {
8 | let draftContent = '';
9 |
10 | const { image } = await experimental_generateImage({
11 | model: myProvider.imageModel('small-model'),
12 | prompt: title,
13 | n: 1,
14 | });
15 |
16 | draftContent = image.base64;
17 |
18 | dataStream.writeData({
19 | type: 'image-delta',
20 | content: image.base64,
21 | });
22 |
23 | return draftContent;
24 | },
25 | onUpdateDocument: async ({ description, dataStream }) => {
26 | let draftContent = '';
27 |
28 | const { image } = await experimental_generateImage({
29 | model: myProvider.imageModel('small-model'),
30 | prompt: description,
31 | n: 1,
32 | });
33 |
34 | draftContent = image.base64;
35 |
36 | dataStream.writeData({
37 | type: 'image-delta',
38 | content: image.base64,
39 | });
40 |
41 | return draftContent;
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/tests/prompts/routes.ts:
--------------------------------------------------------------------------------
1 | import { generateUUID } from '@/lib/utils';
2 |
3 | export const TEST_PROMPTS = {
4 | SKY: {
5 | MESSAGE: {
6 | id: generateUUID(),
7 | createdAt: new Date().toISOString(),
8 | role: 'user',
9 | content: 'Why is the sky blue?',
10 | parts: [{ type: 'text', text: 'Why is the sky blue?' }],
11 | },
12 | OUTPUT_STREAM: [
13 | '0:"It\'s "',
14 | '0:"just "',
15 | '0:"blue "',
16 | '0:"duh! "',
17 | 'e:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10},"isContinued":false}',
18 | 'd:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10}}',
19 | ],
20 | },
21 | GRASS: {
22 | MESSAGE: {
23 | id: generateUUID(),
24 | createdAt: new Date().toISOString(),
25 | role: 'user',
26 | content: 'Why is grass green?',
27 | parts: [{ type: 'text', text: 'Why is grass green?' }],
28 | },
29 |
30 | OUTPUT_STREAM: [
31 | '0:"It\'s "',
32 | '0:"just "',
33 | '0:"green "',
34 | '0:"duh! "',
35 | 'e:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10},"isContinued":false}',
36 | 'd:{"finishReason":"stop","usage":{"promptTokens":3,"completionTokens":10}}',
37 | ],
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as React from 'react';
4 | import * as TooltipPrimitive from '@radix-ui/react-tooltip';
5 |
6 | import { cn } from '@/lib/utils';
7 |
8 | const TooltipProvider = TooltipPrimitive.Provider;
9 |
10 | const Tooltip = TooltipPrimitive.Root;
11 |
12 | const TooltipTrigger = TooltipPrimitive.Trigger;
13 |
14 | const TooltipContent = React.forwardRef<
15 | React.ElementRef58 | Use your email and password to sign in 59 |
60 |64 | {"Don't have an account? "} 65 | 69 | Sign up 70 | 71 | {' for free.'} 72 |
73 |59 | Create an account with your email and password 60 |
61 |65 | {'Already have an account? '} 66 | 70 | Sign in 71 | 72 | {' instead.'} 73 |
74 |
3 | 7 | Chat SDK is a free, open-source template built with Next.js and the AI SDK that helps you quickly build powerful chatbot applications. 8 |
9 | 10 |11 | Read Docs · 12 | Features · 13 | Model Providers · 14 | Deploy Your Own · 15 | Running locally 16 |
17 |