├── .eslintrc.json
├── .env.example
├── public
├── tv.png
├── watch.png
└── iphone.png
├── app
├── favicon.ico
└── (preview)
│ ├── twitter-image.png
│ ├── uncut-sans.woff2
│ ├── opengraph-image.png
│ ├── layout.tsx
│ ├── api
│ └── chat
│ │ └── route.ts
│ ├── globals.css
│ └── page.tsx
├── next.config.mjs
├── postcss.config.mjs
├── .gitignore
├── LICENSE
├── tsconfig.json
├── components
├── use-scroll-to-bottom.ts
├── message.tsx
├── data.ts
└── icons.tsx
├── package.json
├── README.md
└── tailwind.config.ts
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=****
2 | GOOGLE_GENERATIVE_AI_API_KEY=****
3 | ANTHROPIC_API_KEY=****
4 |
--------------------------------------------------------------------------------
/public/tv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel-labs/ai-sdk-preview-provider-registry/HEAD/public/tv.png
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel-labs/ai-sdk-preview-provider-registry/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/public/watch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel-labs/ai-sdk-preview-provider-registry/HEAD/public/watch.png
--------------------------------------------------------------------------------
/public/iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel-labs/ai-sdk-preview-provider-registry/HEAD/public/iphone.png
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/app/(preview)/twitter-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel-labs/ai-sdk-preview-provider-registry/HEAD/app/(preview)/twitter-image.png
--------------------------------------------------------------------------------
/app/(preview)/uncut-sans.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel-labs/ai-sdk-preview-provider-registry/HEAD/app/(preview)/uncut-sans.woff2
--------------------------------------------------------------------------------
/app/(preview)/opengraph-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel-labs/ai-sdk-preview-provider-registry/HEAD/app/(preview)/opengraph-image.png
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2024 Vercel, Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/app/(preview)/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./globals.css";
2 | import { Metadata } from "next";
3 | import { Toaster } from "sonner";
4 |
5 | export const metadata: Metadata = {
6 | metadataBase: new URL("https://ai-sdk-preview-provider-registry.vercel.app"),
7 | title: "Provider Registry Preview",
8 | description:
9 | "Handle multiple providers and models and switch between them easily",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 |
20 |
21 | {children}
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/app/(preview)/api/chat/route.ts:
--------------------------------------------------------------------------------
1 | import { openai } from "@ai-sdk/openai";
2 | import { anthropic } from "@ai-sdk/anthropic";
3 | import { experimental_createProviderRegistry, streamText } from "ai";
4 | import { google } from "@ai-sdk/google";
5 |
6 | const registry = experimental_createProviderRegistry({
7 | openai,
8 | anthropic,
9 | google,
10 | });
11 |
12 | export async function POST(request: Request) {
13 | const { messages, model } = await request.json();
14 |
15 | const stream = streamText({
16 | model: registry.languageModel(model),
17 | system: `\
18 | - you are a friendly assistant
19 | - you are concise with your responses
20 | - you do not use lists, that's silly
21 | `,
22 | messages,
23 | });
24 |
25 | return stream.toDataStreamResponse();
26 | }
27 |
--------------------------------------------------------------------------------
/components/use-scroll-to-bottom.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, RefObject } from "react";
2 |
3 | export function useScrollToBottom(): [
4 | RefObject,
5 | RefObject,
6 | ] {
7 | const containerRef = useRef(null);
8 | const endRef = useRef(null);
9 |
10 | useEffect(() => {
11 | const container = containerRef.current;
12 | const end = endRef.current;
13 |
14 | if (container && end) {
15 | const observer = new MutationObserver(() => {
16 | end.scrollIntoView({ behavior: "smooth" });
17 | });
18 |
19 | observer.observe(container, {
20 | childList: true,
21 | subtree: true,
22 | });
23 |
24 | return () => observer.disconnect();
25 | }
26 | }, []);
27 |
28 | return [containerRef, endRef];
29 | }
30 |
--------------------------------------------------------------------------------
/components/message.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { motion } from "framer-motion";
4 | import { BotIcon, UserIcon } from "./icons";
5 | import { ReactNode } from "react";
6 | import { Streamdown as Markdown } from "streamdown";
7 |
8 | export const Message = ({
9 | role,
10 | content,
11 | }: {
12 | role: string;
13 | content: string | ReactNode;
14 | }) => {
15 | return (
16 |
21 |
22 | {role === "assistant" ? : }
23 |
24 |
25 |
26 |
27 | {content as string}
28 |
29 |
30 |
31 | );
32 | };
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ai-sdk-preview-provider-registry",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@ai-sdk/anthropic": "^1.0.5",
13 | "@ai-sdk/google": "^1.0.11",
14 | "@ai-sdk/openai": "^1.0.10",
15 | "@vercel/analytics": "^1.3.1",
16 | "@vercel/kv": "^2.0.0",
17 | "ai": "^4.0.21",
18 | "d3-scale": "^4.0.2",
19 | "date-fns": "^3.6.0",
20 | "framer-motion": "^11.3.19",
21 | "next": "14.2.5",
22 | "react": "^18",
23 | "react-dom": "^18",
24 | "sonner": "^1.5.0",
25 | "streamdown": "^1.6.7",
26 | "tailwindcss-animate": "^1.0.7",
27 | "zod": "^3.24.1"
28 | },
29 | "devDependencies": {
30 | "@types/d3-scale": "^4.0.8",
31 | "@types/node": "^20",
32 | "@types/react": "^18",
33 | "@types/react-dom": "^18",
34 | "eslint": "^8",
35 | "eslint-config-next": "14.2.5",
36 | "postcss": "^8",
37 | "tailwindcss": "^3.4.1",
38 | "typescript": "^5"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/components/data.ts:
--------------------------------------------------------------------------------
1 | export interface Order {
2 | id: string;
3 | name: string;
4 | orderedAt: string;
5 | image: string;
6 | }
7 |
8 | export const ORDERS: Order[] = [
9 | {
10 | id: "539182",
11 | name: "Apple TV",
12 | orderedAt: "2024-08-25",
13 | image: "tv.png",
14 | },
15 | {
16 | id: "281958",
17 | name: "Apple iPhone 14 Pro",
18 | orderedAt: "2024-08-24",
19 | image: "iphone.png",
20 | },
21 | {
22 | id: "281958",
23 | name: "Apple Watch Ultra 2",
24 | orderedAt: "2024-08-26",
25 | image: "watch.png",
26 | },
27 | ];
28 |
29 | export interface TrackingInformation {
30 | orderId: string;
31 | progress: "Shipped" | "Out for Delivery" | "Delivered";
32 | description: string;
33 | }
34 |
35 | export const TRACKING_INFORMATION = [
36 | {
37 | orderId: "412093",
38 | progress: "Shipped",
39 | description: "Last Updated Today 4:30 PM",
40 | },
41 | {
42 | orderId: "281958",
43 | progress: "Out for Delivery",
44 | description: "ETA Today 5:45 PM",
45 | },
46 | {
47 | orderId: "539182",
48 | progress: "Delivered",
49 | description: "Delivered Today 3:00 PM",
50 | },
51 | ];
52 |
53 | export const getOrders = () => {
54 | return ORDERS;
55 | };
56 |
57 | export const getTrackingInformation = ({ orderId }: { orderId: string }) => {
58 | return TRACKING_INFORMATION.find((info) => info.orderId === orderId);
59 | };
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Provider Registry Preview
2 |
3 | This example demonstrates how to use the [Vercel AI SDK](https://sdk.vercel.ai/docs) with [Next.js](https://nextjs.org/) and the `experimental_createProviderRegistry` function to handle multiple providers and models and switch between them easily in your application.
4 |
5 | ## Deploy your own
6 |
7 | [](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fai-sdk-preview-provider-registry&env=OPENAI_API_KEY,ANTHROPIC_API_KEY,GOOGLE_GENERATIVE_AI_API_KEY&envDescription=API%20Keys%20needed%20for%20the%20application.)
8 |
9 | ## How to use
10 |
11 | Run [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
12 |
13 | ```bash
14 | npx create-next-app --example https://github.com/vercel-labs/ai-sdk-preview-provider-registry ai-sdk-preview-provider-registry-example
15 | ```
16 |
17 | ```bash
18 | yarn create next-app --example https://github.com/vercel-labs/ai-sdk-preview-provider-registry ai-sdk-preview-provider-registry-example
19 | ```
20 |
21 | ```bash
22 | pnpm create next-app --example https://github.com/vercel-labs/ai-sdk-preview-provider-registry ai-sdk-preview-provider-registry-example
23 | ```
24 |
25 | To run the example locally you need to:
26 |
27 | 1. Sign up for accounts with the AI providers you want to use (e.g., OpenAI, Anthropic).
28 | 2. Obtain API keys for each provider.
29 | 3. Set the required environment variables as shown in the `.env.example` file, but in a new file called `.env`.
30 | 4. `npm install` to install the required dependencies.
31 | 5. `npm run dev` to launch the development server.
32 |
33 | ## Learn More
34 |
35 | To learn more about Vercel AI SDK or Next.js take a look at the following resources:
36 |
37 | - [Vercel AI SDK docs](https://sdk.vercel.ai/docs)
38 | - [Vercel AI Playground](https://play.vercel.ai)
39 | - [Next.js Documentation](https://nextjs.org/docs)
40 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 | import animate from "tailwindcss-animate";
3 | import { fontFamily } from "tailwindcss/defaultTheme";
4 |
5 | const config: Config = {
6 | content: [
7 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
8 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
9 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
10 | "./node_modules/streamdown/dist/*.js",
11 | ],
12 | theme: {
13 | container: {
14 | center: true,
15 | padding: "2rem",
16 | screens: {
17 | "2xl": "1400px",
18 | },
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border))",
23 | input: "hsl(var(--input))",
24 | ring: "hsl(var(--ring))",
25 | background: "hsl(var(--background))",
26 | foreground: "hsl(var(--foreground))",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary))",
29 | foreground: "hsl(var(--primary-foreground))",
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary))",
33 | foreground: "hsl(var(--secondary-foreground))",
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive))",
37 | foreground: "hsl(var(--destructive-foreground))",
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted))",
41 | foreground: "hsl(var(--muted-foreground))",
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent))",
45 | foreground: "hsl(var(--accent-foreground))",
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover))",
49 | foreground: "hsl(var(--popover-foreground))",
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card))",
53 | foreground: "hsl(var(--card-foreground))",
54 | },
55 | },
56 | borderRadius: {
57 | lg: `var(--radius)`,
58 | md: `calc(var(--radius) - 2px)`,
59 | sm: "calc(var(--radius) - 4px)",
60 | },
61 | fontFamily: {
62 | sans: ["var(--font-sans)", ...fontFamily.sans],
63 | },
64 | keyframes: {
65 | "accordion-down": {
66 | from: { height: "0" },
67 | to: { height: "var(--radix-accordion-content-height)" },
68 | },
69 | "accordion-up": {
70 | from: { height: "var(--radix-accordion-content-height)" },
71 | to: { height: "0" },
72 | },
73 | },
74 | animation: {
75 | "accordion-down": "accordion-down 0.2s ease-out",
76 | "accordion-up": "accordion-up 0.2s ease-out",
77 | },
78 | backgroundImage: {
79 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
80 | "gradient-conic":
81 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
82 | },
83 | },
84 | },
85 | plugins: [animate],
86 | };
87 | export default config;
88 |
--------------------------------------------------------------------------------
/app/(preview)/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 0 0% 3.9%;
9 |
10 | --card: 0 0% 100%;
11 | --card-foreground: 0 0% 3.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 0 0% 3.9%;
15 |
16 | --primary: 0 0% 9%;
17 | --primary-foreground: 0 0% 98%;
18 |
19 | --secondary: 0 0% 96.1%;
20 | --secondary-foreground: 0 0% 9%;
21 |
22 | --muted: 0 0% 96.1%;
23 | --muted-foreground: 0 0% 45.1%;
24 |
25 | --accent: 0 0% 96.1%;
26 | --accent-foreground: 0 0% 9%;
27 |
28 | --destructive: 0 84.2% 60.2%;
29 | --destructive-foreground: 0 0% 98%;
30 |
31 | --border: 0 0% 89.8%;
32 | --input: 0 0% 89.8%;
33 | --ring: 0 0% 3.9%;
34 |
35 | --radius: 0.5rem;
36 | }
37 |
38 | .dark {
39 | --background: 0 0% 3.9%;
40 | --foreground: 0 0% 98%;
41 |
42 | --card: 0 0% 3.9%;
43 | --card-foreground: 0 0% 98%;
44 |
45 | --popover: 0 0% 3.9%;
46 | --popover-foreground: 0 0% 98%;
47 |
48 | --primary: 0 0% 98%;
49 | --primary-foreground: 0 0% 9%;
50 |
51 | --secondary: 0 0% 14.9%;
52 | --secondary-foreground: 0 0% 98%;
53 |
54 | --muted: 0 0% 14.9%;
55 | --muted-foreground: 0 0% 63.9%;
56 |
57 | --accent: 0 0% 14.9%;
58 | --accent-foreground: 0 0% 98%;
59 |
60 | --destructive: 0 62.8% 30.6%;
61 | --destructive-foreground: 0 0% 98%;
62 |
63 | --border: 0 0% 14.9%;
64 | --input: 0 0% 14.9%;
65 | --ring: 0 0% 83.1%;
66 | }
67 | }
68 |
69 | @layer base {
70 | * {
71 | @apply border-border;
72 | }
73 | body {
74 | @apply bg-background text-foreground;
75 | }
76 | }
77 |
78 | :root {
79 | --foreground-rgb: 0, 0, 0;
80 | --background-start-rgb: 214, 219, 220;
81 | --background-end-rgb: 255, 255, 255;
82 | }
83 |
84 | @font-face {
85 | font-family: "uncut sans";
86 | src: url("./uncut-sans.woff2") format("woff2");
87 | }
88 |
89 | * {
90 | font-family: "uncut sans", sans-serif;
91 | }
92 |
93 | @media (prefers-color-scheme: dark) {
94 | :root {
95 | --foreground-rgb: 255, 255, 255;
96 | --background-start-rgb: 0, 0, 0;
97 | --background-end-rgb: 0, 0, 0;
98 | }
99 | }
100 |
101 | body {
102 | color: rgb(var(--foreground-rgb));
103 | background: linear-gradient(
104 | to bottom,
105 | transparent,
106 | rgb(var(--background-end-rgb))
107 | )
108 | rgb(var(--background-start-rgb));
109 | }
110 |
111 | @layer utilities {
112 | .text-balance {
113 | text-wrap: balance;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/(preview)/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import { Message } from "@/components/message";
5 | import { useScrollToBottom } from "@/components/use-scroll-to-bottom";
6 | import { AnimatePresence, motion } from "framer-motion";
7 | import {
8 | LogoAnthropic,
9 | LogoGoogle,
10 | LogoOpenAI,
11 | RouteIcon,
12 | VercelIcon,
13 | } from "@/components/icons";
14 | import Link from "next/link";
15 | import { useChat } from "ai/react";
16 |
17 | const suggestedActions = [
18 | {
19 | title: "Why is",
20 | label: "the sky blue?",
21 | action: "why is the sky blue?",
22 | },
23 | {
24 | title: "What does it mean",
25 | label: "to be in the arena?",
26 | action: "what does it mean to be in the arena?",
27 | },
28 | ];
29 |
30 | const models = [
31 | "openai:gpt-4o",
32 | "anthropic:claude-3-sonnet-20240229",
33 | "google:gemini-1.5-flash",
34 | ];
35 |
36 | const getProviderIcon = (model: string) => {
37 | const provider = model.split(":")[0];
38 |
39 | switch (provider) {
40 | case "openai":
41 | return ;
42 | case "anthropic":
43 | return ;
44 | case "google":
45 | return ;
46 | default:
47 | return null;
48 | }
49 | };
50 |
51 | export default function Home() {
52 | const [selectedModel, setSelectedModel] = useState(
53 | "anthropic:claude-3-sonnet-20240229",
54 | );
55 | const [isDropdownVisible, setIsDropdownVisible] = useState(false);
56 |
57 | const { messages, handleSubmit, input, setInput, append } = useChat({
58 | body: {
59 | model: selectedModel,
60 | },
61 | });
62 |
63 | const [messagesContainerRef, messagesEndRef] =
64 | useScrollToBottom();
65 |
66 | return (
67 |
68 |
69 |
73 | {messages.length === 0 && (
74 |
75 |
76 |
77 |
78 | +
79 |
80 |
81 |
82 | The experimental_createProviderRegistry function allows you to
83 | create a registry of providers and models that you can can
84 | switch between and use in your application.
85 |
86 |
87 | {" "}
88 | Learn more about the{" "}
89 |
94 | Provider Registry{" "}
95 |
96 | from Vercel AI SDK.
97 |
98 |
99 |
100 | )}
101 |
102 | {messages.map((message) => (
103 |
108 | ))}
109 |
110 |
111 |
112 |
113 | {messages.length === 0 &&
114 | suggestedActions.map((suggestedAction, index) => (
115 | 1 ? "hidden sm:block" : "block"}
121 | >
122 | {
124 | append({
125 | role: "user",
126 | content: suggestedAction.action,
127 | });
128 | }}
129 | className="w-full text-left border border-zinc-200 dark:border-zinc-800 text-zinc-800 dark:text-zinc-300 rounded-lg p-2 text-sm hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors flex flex-col"
130 | >
131 | {suggestedAction.title}
132 |
133 | {suggestedAction.label}
134 |
135 |
136 |
137 | ))}
138 |
139 |
140 |
162 |
163 |
164 |
165 | {isDropdownVisible && (
166 | <>
167 | {
173 | setIsDropdownVisible(false);
174 | }}
175 | />
176 |
177 |
184 | Choose a Model
185 |
186 | {models.map((model) => {
187 | return (
188 |
{
196 | setSelectedModel(model);
197 | setIsDropdownVisible(false);
198 | }}
199 | >
200 |
{getProviderIcon(model)}
201 |
202 | {model.split(":")[1]}
203 |
204 |
205 | );
206 | })}
207 |
208 |
209 | >
210 | )}
211 |
212 |
213 | );
214 | }
215 |
--------------------------------------------------------------------------------
/components/icons.tsx:
--------------------------------------------------------------------------------
1 | export const BotIcon = () => {
2 | return (
3 |
10 |
16 |
17 | );
18 | };
19 |
20 | export const UserIcon = () => {
21 | return (
22 |
30 |
36 |
37 | );
38 | };
39 |
40 | export const AttachmentIcon = () => {
41 | return (
42 |
49 |
55 |
56 | );
57 | };
58 |
59 | export const VercelIcon = ({ size = 17 }) => {
60 | return (
61 |
68 |
74 |
75 | );
76 | };
77 |
78 | export const GitIcon = () => {
79 | return (
80 |
87 |
88 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | );
102 | };
103 |
104 | export const BoxIcon = ({ size = 16 }: { size: number }) => {
105 | return (
106 |
113 |
119 |
120 | );
121 | };
122 |
123 | export const HomeIcon = ({ size = 16 }: { size: number }) => {
124 | return (
125 |
132 |
138 |
139 | );
140 | };
141 |
142 | export const GPSIcon = ({ size = 16 }: { size: number }) => {
143 | return (
144 |
151 |
159 |
160 | );
161 | };
162 |
163 | export const InvoiceIcon = ({ size = 16 }: { size: number }) => {
164 | return (
165 |
172 |
178 |
179 | );
180 | };
181 |
182 | export const LogoOpenAI = ({ size = 16 }: { size?: number }) => {
183 | return (
184 |
191 |
195 |
196 | );
197 | };
198 |
199 | export const LogoGoogle = ({ size = 16 }: { size?: number }) => {
200 | return (
201 |
209 |
213 |
217 |
221 |
225 |
226 | );
227 | };
228 |
229 | export const LogoAnthropic = () => {
230 | return (
231 |
241 |
245 |
246 | );
247 | };
248 |
249 | export const RouteIcon = ({ size = 16 }: { size?: number }) => {
250 | return (
251 |
258 |
264 |
265 | );
266 | };
267 |
--------------------------------------------------------------------------------