Skip to content

Commit e94d488

Browse files
dancerfelixbridell
authored andcommitted
integrate ai gateway and improve ui components (vercel#1145)
# Conflicts: # app/(chat)/api/chat/route.ts # lib/ai/providers.ts # package.json # pnpm-lock.yaml
1 parent 9c827c2 commit e94d488

22 files changed

+382
-1665
lines changed

.env.example

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ AUTH_SECRET=****
44
# The following keys below are automatically created and
55
# added to your environment when you deploy on vercel
66

7-
# Get your xAI API Key here for chat and image models: https://console.x.ai/
8-
XAI_API_KEY=****
7+
# AI Gateway API Key (required for non-Vercel deployments)
8+
# For Vercel deployments, OIDC tokens are used automatically
9+
AI_GATEWAY_API_KEY=****
10+
911

1012
# Instructions to create a Vercel Blob Store here: https://vercel.com/docs/storage/vercel-blob
1113
BLOB_READ_WRITE_TOKEN=****

.github/workflows/lint.yml

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ jobs:
99
matrix:
1010
node-version: [20]
1111
steps:
12-
- uses: actions/checkout@v4
13-
- name: Install pnpm
14-
uses: pnpm/action-setup@v4
15-
with:
16-
version: 9.12.3
17-
- name: Use Node.js ${{ matrix.node-version }}
18-
uses: actions/setup-node@v4
19-
with:
20-
node-version: ${{ matrix.node-version }}
21-
cache: 'pnpm'
22-
- name: Install dependencies
23-
run: pnpm install
24-
- name: Run lint
25-
run: pnpm lint
12+
- uses: actions/checkout@v4
13+
- name: Install pnpm
14+
uses: pnpm/action-setup@v4
15+
with:
16+
version: 9.12.3
17+
- name: Use Node.js ${{ matrix.node-version }}
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: ${{ matrix.node-version }}
21+
cache: "pnpm"
22+
- name: Install dependencies
23+
run: pnpm install
24+
- name: Run lint
25+
run: pnpm lint
26+
1

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,15 @@
3939

4040
## Model Providers
4141

42-
This template ships with [xAI](https://x.ai) `grok-2-1212` as the default chat model. However, with the [AI SDK](https://sdk.vercel.ai/docs), you can switch LLM providers to [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), and [many more](https://sdk.vercel.ai/providers/ai-sdk-providers) with just a few lines of code.
42+
This template uses the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway) to access multiple AI models through a unified interface. The default configuration includes [xAI](https://x.ai) models (`grok-2-vision-1212`, `grok-3-mini-beta`) routed through the gateway.
43+
44+
### AI Gateway Authentication
45+
46+
**For Vercel deployments**: Authentication is handled automatically via OIDC tokens.
47+
48+
**For non-Vercel deployments**: You need to provide an AI Gateway API key by setting the `AI_GATEWAY_API_KEY` environment variable in your `.env.local` file.
49+
50+
With the [AI SDK](https://ai-sdk.dev/docs/introduction), you can also switch to direct LLM providers like [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), [Cohere](https://cohere.com/), and [many more](https://ai-sdk.dev/providers/ai-sdk-providers) with just a few lines of code.
4351

4452
See `docs/berget-ai.md` for details about using Berget AI models.
4553

app/(chat)/api/chat/route.ts

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -167,18 +167,18 @@ export async function POST(request: Request) {
167167
stopWhen: stepCountIs(5),
168168
experimental_activeTools:
169169
selectedChatModel === 'chat-model-reasoning' ||
170-
selectedChatModel === 'deepseek-r1' ||
171-
selectedChatModel === 'openai-gpt-oss-120b'
170+
selectedChatModel === 'deepseek-r1' ||
171+
selectedChatModel === 'openai-gpt-oss-120b'
172172
? []
173173
: [
174-
'getWeather',
175-
'searchWeb',
176-
'scrapeUrl',
177-
'createDocument',
178-
'updateDocument',
179-
'requestSuggestions',
180-
'generateImageTool',
181-
],
174+
'getWeather',
175+
'searchWeb',
176+
'scrapeUrl',
177+
'createDocument',
178+
'updateDocument',
179+
'requestSuggestions',
180+
'generateImageTool',
181+
],
182182
experimental_transform: smoothStream({ chunking: 'word' }),
183183
tools: {
184184
getWeather,
@@ -240,16 +240,9 @@ export async function POST(request: Request) {
240240
if (error instanceof ChatSDKError) {
241241
return error.toResponse();
242242
}
243-
// Return a proper error response for other errors
244-
return new Response(
245-
JSON.stringify({
246-
error: error instanceof Error ? error.message : 'An error occurred',
247-
}),
248-
{
249-
status: 500,
250-
headers: { 'Content-Type': 'application/json' },
251-
},
252-
);
243+
244+
console.error('Unhandled error in chat API:', error);
245+
return new ChatSDKError('offline:chat').toResponse();
253246
}
254247
}
255248

artifacts/image/server.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

components/artifact-messages.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function PureArtifactMessages({
4242
return (
4343
<div
4444
ref={messagesContainerRef}
45-
className="flex flex-col gap-4 h-full items-center overflow-y-scroll px-4 pt-20"
45+
className="flex overflow-y-scroll flex-col gap-4 items-center px-4 pt-20 h-full"
4646
>
4747
{messages.map((message, index) => (
4848
<PreviewMessage
@@ -61,6 +61,7 @@ function PureArtifactMessages({
6161
requiresScrollPadding={
6262
hasSentMessage && index === messages.length - 1
6363
}
64+
isArtifactVisible={true}
6465
/>
6566
))}
6667

components/artifact.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ function PureArtifact({
6767
votes,
6868
isReadonly,
6969
selectedVisibilityType,
70+
selectedModelId,
7071
}: {
7172
chatId: string;
7273
input: string;
@@ -82,6 +83,7 @@ function PureArtifact({
8283
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
8384
isReadonly: boolean;
8485
selectedVisibilityType: VisibilityType;
86+
selectedModelId: string;
8587
}) {
8688
const { artifact, setArtifact, metadata, setMetadata } = useArtifact();
8789

@@ -286,9 +288,9 @@ function PureArtifact({
286288
x: 0,
287289
scale: 1,
288290
transition: {
289-
delay: 0.2,
291+
delay: 0.1,
290292
type: 'spring',
291-
stiffness: 200,
293+
stiffness: 300,
292294
damping: 30,
293295
},
294296
}}
@@ -336,6 +338,7 @@ function PureArtifact({
336338
className="bg-background dark:bg-muted"
337339
setMessages={setMessages}
338340
selectedVisibilityType={selectedVisibilityType}
341+
selectedModelId={selectedModelId}
339342
/>
340343
</div>
341344
</div>
@@ -375,9 +378,9 @@ function PureArtifact({
375378
transition: {
376379
delay: 0,
377380
type: 'spring',
378-
stiffness: 200,
381+
stiffness: 300,
379382
damping: 30,
380-
duration: 5000,
383+
duration: 0.8,
381384
},
382385
}
383386
: {
@@ -392,9 +395,9 @@ function PureArtifact({
392395
transition: {
393396
delay: 0,
394397
type: 'spring',
395-
stiffness: 200,
398+
stiffness: 300,
396399
damping: 30,
397-
duration: 5000,
400+
duration: 0.8,
398401
},
399402
}
400403
}

components/chat-header.tsx

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import Link from 'next/link';
44
import { useRouter } from 'next/navigation';
55
import { useWindowSize } from 'usehooks-ts';
66

7-
import { ModelSelector } from '@/components/model-selector';
87
import { SidebarToggle } from '@/components/sidebar-toggle';
98
import { Button } from '@/components/ui/button';
109
import { PlusIcon, VercelIcon } from './icons';
@@ -16,13 +15,11 @@ import type { Session } from 'next-auth';
1615

1716
function PureChatHeader({
1817
chatId,
19-
selectedModelId,
2018
selectedVisibilityType,
2119
isReadonly,
2220
session,
2321
}: {
2422
chatId: string;
25-
selectedModelId: string;
2623
selectedVisibilityType: VisibilityType;
2724
isReadonly: boolean;
2825
session: Session;
@@ -55,24 +52,16 @@ function PureChatHeader({
5552
</Tooltip>
5653
)}
5754

58-
{!isReadonly && (
59-
<ModelSelector
60-
session={session}
61-
selectedModelId={selectedModelId}
62-
className="order-1 md:order-2"
63-
/>
64-
)}
65-
6655
{!isReadonly && (
6756
<VisibilitySelector
6857
chatId={chatId}
6958
selectedVisibilityType={selectedVisibilityType}
70-
className="order-1 md:order-3"
59+
className="order-1 md:order-2"
7160
/>
7261
)}
7362

7463
<Button
75-
className="bg-zinc-900 dark:bg-zinc-100 hover:bg-zinc-800 dark:hover:bg-zinc-200 text-zinc-50 dark:text-zinc-900 hidden md:flex py-1.5 px-2 h-fit md:h-[34px] order-4 md:ml-auto"
64+
className="bg-zinc-900 dark:bg-zinc-100 hover:bg-zinc-800 dark:hover:bg-zinc-200 text-zinc-50 dark:text-zinc-900 hidden md:flex py-1.5 px-2 h-fit md:h-[34px] order-3 md:ml-auto"
7665
asChild
7766
>
7867
<Link
@@ -88,5 +77,9 @@ function PureChatHeader({
8877
}
8978

9079
export const ChatHeader = memo(PureChatHeader, (prevProps, nextProps) => {
91-
return prevProps.selectedModelId === nextProps.selectedModelId;
80+
return (
81+
prevProps.chatId === nextProps.chatId &&
82+
prevProps.selectedVisibilityType === nextProps.selectedVisibilityType &&
83+
prevProps.isReadonly === nextProps.isReadonly
84+
);
9285
});

components/chat.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useEffect, useState } from 'react';
66
import useSWR, { useSWRConfig } from 'swr';
77
import { ChatHeader } from '@/components/chat-header';
88
import type { Vote } from '@/lib/db/schema';
9-
import { fetcher, fetchWithErrorHandlers, generateUUID } from '@/lib/utils';
9+
import { fetcher, fetchWithErrorHandlers, generateUUID, cn } from '@/lib/utils';
1010
import { Artifact } from './artifact';
1111
import { MultimodalInput } from './multimodal-input';
1212
import { Messages } from './messages';
@@ -131,7 +131,6 @@ export function Chat({
131131
<div className="flex flex-col min-w-0 h-dvh bg-background">
132132
<ChatHeader
133133
chatId={id}
134-
selectedModelId={initialChatModel}
135134
selectedVisibilityType={initialVisibilityType}
136135
isReadonly={isReadonly}
137136
session={session}
@@ -162,6 +161,7 @@ export function Chat({
162161
setMessages={setMessages}
163162
sendMessage={sendMessage}
164163
selectedVisibilityType={visibilityType}
164+
selectedModelId={initialChatModel}
165165
/>
166166
)}
167167
</div>
@@ -182,6 +182,7 @@ export function Chat({
182182
votes={votes}
183183
isReadonly={isReadonly}
184184
selectedVisibilityType={visibilityType}
185+
selectedModelId={initialChatModel}
185186
/>
186187
</>
187188
);

components/elements/code-block.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ export const CodeBlock = ({
5454
fontSize: '0.875rem',
5555
background: 'hsl(var(--background))',
5656
color: 'hsl(var(--foreground))',
57+
overflowX: 'auto',
58+
overflowWrap: 'break-word',
59+
wordBreak: 'break-all',
5760
}}
5861
language={language}
5962
lineNumberStyle={{
@@ -77,6 +80,9 @@ export const CodeBlock = ({
7780
fontSize: '0.875rem',
7881
background: 'hsl(var(--background))',
7982
color: 'hsl(var(--foreground))',
83+
overflowX: 'auto',
84+
overflowWrap: 'break-word',
85+
wordBreak: 'break-all',
8086
}}
8187
language={language}
8288
lineNumberStyle={{

0 commit comments

Comments
 (0)