├── .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 | [![Deploy with Vercel](https://vercel.com/button)](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 | 136 | 137 | ))} 138 |
139 | 140 |
144 | { 149 | setInput(event.target.value); 150 | }} 151 | /> 152 | 153 |
{ 156 | setIsDropdownVisible(!isDropdownVisible); 157 | }} 158 | > 159 | {getProviderIcon(selectedModel)} 160 |
161 |
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 | --------------------------------------------------------------------------------