diff --git a/.env.example b/.env.example index acc5ec7..8b3f575 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,5 @@ -OPENAI_API_KEY=sk-**** ANTHROPIC_API_KEY=sk-ant-api**** -TAVILY_API_KEY=tvly-**** \ No newline at end of file +TAVILY_API_KEY=tvly-**** +GROQ_API_KEY=gsk_**** +OPENWEATHER_API_KEY=*** +E2B_API_KEY=e2b_**** \ No newline at end of file diff --git a/README.md b/README.md index cfc696a..dccd9a1 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,29 @@ A minimalistic AI-powered search engine that helps you find information on the internet. +## Features + +- **AI-powered search**: Get answers to your questions using Anthropic's Models. +- **Web search**: Search the web using Tavily's API. +- **URL Specific search**: Get information from a specific URL. +- **Weather**: Get the current weather for any location using OpenWeather's API. +- **Programming**: Run code snippets in multiple languages using E2B's API. + +## Built with +- [Next.js](https://nextjs.org/) +- [Tailwind CSS](https://tailwindcss.com/) +- [Vercel AI SDK](https://sdk.vercel.ai/docs) +- [Shadcn/UI](https://ui.shadcn.com/) +- [Anthropic](https://www.anthropic.com/) +- [Tavily](https://tavily.com/) +- [OpenWeather](https://openweathermap.org/) +- [E2B](https://e2b.dev/) + ### Deploy your own -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fzaidmukaddam%2Fminiperplx&env=OPENAI_API_KEY,ANTHROPIC_API_KEY,TAVILY_API_KEY,OPENWEATHER_API_KEY,E2B_API_KEY&envDescription=API%20keys%20needed%20for%20application) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fzaidmukaddam%2Fminiperplx&env=ANTHROPIC_API_KEY,GROQ_API_KEY,TAVILY_API_KEY,OPENWEATHER_API_KEY,E2B_API_KEY&envDescription=API%20keys%20needed%20for%20application) + +### Local development To run the example locally you need to: diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index d145621..961c435 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,4 +1,3 @@ -import { openai } from "@ai-sdk/openai"; import { anthropic } from '@ai-sdk/anthropic' import { convertToCoreMessages, streamText, tool } from "ai"; import { CodeInterpreter } from '@e2b/code-interpreter' @@ -12,36 +11,34 @@ export async function POST(req: Request) { const { messages, model } = await req.json(); const { latitude, longitude, city } = geolocation(req) - let ansmodel; - - if (model === "claude-3-5-sonnet-20240620") { - ansmodel = anthropic("claude-3-5-sonnet-20240620") - } else { - ansmodel = openai(model) - } + const ansmodel = anthropic(model); const result = await streamText({ model: ansmodel, messages: convertToCoreMessages(messages), + temperature: 0, + maxTokens: 500, system: - "You are an AI web search engine that helps users find information on the internet." + - "Always start with running the tool(s) and then and then only write your response AT ALL COSTS!" + - "Never write a response without running the tool(s) first!\n\n" + - "Do not announce that your going to run a tool, just run it and then write your response AT ALL COSTS!!!!!." + + "You are an AI web search engine that helps users find information on the internet.\n" + + "Always start with running the tool(s) and then and then only write your response AT ALL COSTS!!\n" + + "Never write a response without running the tool(s) first!\n" + + "Do not announce or inform the user in any way that your going to run a tool at ALL COSTS!! Just `run` it and then write your response AT ALL COSTS!!!!!." + "Tool Usage Instructions:\n" + "The user is located in " + city + " at latitude " + latitude + " and longitude " + longitude + "." + "Use this geolocation data for weather tool, when the user doesn't provide a specific location." + - "You use the 'web_search' tool to search for information on the internet." + + "You use the 'web_search' tool to search for information on the internet before saying anyting to the user." + + "Incomplete details or any other said words before the search tool result in a bad response." + "Always call the 'web_search' tool to get the information, no need to do a chain of thought or say anything else, go straight to the point." + "Once you have found the information, you provide the user with the information you found in brief like a news paper detail." + - "The detail should be 3-5 paragraphs in 10-12 sentences, some time pointers, each with citations in the [Text](link) format always!" + - "Citations can be inline of the text like this: Hey there! [Google](https://google.com) is a search engine." + + "The detail should be 3-5 paragraphs in 10-12 sentences and put citations using the markdown link format like this always: [source text](link to the site) in the end of each paragraph!" + + "Citations will the render in the client side, so please make sure you format them correctly!" + + "Never use pointers, unless asked to." + "Do not start the responses with newline characters, always start with the first sentence." + "When the user asks about a Stock, you should 'always' first gather news about it with web search tool, then show the chart and then write your response. Follow these steps in this order only!" + "Never use the retrieve tool for general search. Always use it when the user provides an url! " + - "For weather related questions, use get_weather_data tool and write your response. No need to call any other tool. Put citation to OpenWeatherMaps API everytime." + + "For weather related questions, use get_weather_data tool and write your response. No need to call any other tool. DO NOT put citation to OpenWeatherMaps API EVER!" + "Use the 'programming' tool to execute Python code for cases like calculating, sorting, etc. that require computation. " + - "The environment is like a jupyter notebook so don't write or use print statements at all costs!, just the variables.\n\n" + + "The environment is like a jupyter notebook so don't write print statements at all costs!, just write variables in the end.\n\n" + "The current date is: " + new Date() .toLocaleDateString("en-US", { @@ -53,8 +50,12 @@ export async function POST(req: Request) { .replace(/(\w+), (\w+) (\d+), (\d+)/, "$4-$2-$3 ($1)") + "." + "Rules for the response:\n" + + "Use a story telling format in your response, like a news article ALWAYS! This is for all tools except programming!" + + "Never start with 'based on the search results,...' EVER! Always start with the information you found like an article!" + "Never use the heading format in your response!." + - "IMPORTANT!!!: Refrain from saying things like that mention that your going to perform a certain action, example: 'Certainly! I'll search for information about OpenAI GPT-4o mini using the web search tool.'", + "Do not use print statements in the code execution tool, just the variables." + + "Don't say things like 'Okay I am going to perform some action.' OR 'Certainly! To count the number of 'r's in the word 'strawberry', we can use a simple Python code. Let's use the programming tool to execute this task.' The user's DO NOT LIKE TO WAIT! REMEMBER THE THING ABOUT PRINT STATEMENTS! NEVER WRITE 'EM!" + + "IMPORTANT!!!: Refrain from saying things like that mention that your going to perform a certain action, example: 'Certainly! I'll search for information about using the web search tool.'", tools: { web_search: tool({ description: 'Search the web for information with the given query, max results and search depth.', diff --git a/app/page.tsx b/app/page.tsx index 197cef3..8362cd8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -36,6 +36,7 @@ import { Check, Loader2, User2, + Edit2, } from 'lucide-react'; import { HoverCard, @@ -103,11 +104,12 @@ export default function Home() { const [showExamples, setShowExamples] = useState(false) const [showConfirmModal, setShowConfirmModal] = useState(false); const [newSelectedModel, setNewSelectedModel] = useState(''); + const [isEditingQuery, setIsEditingQuery] = useState(false); const { isLoading, input, messages, setInput, append, reload, handleSubmit, setMessages } = useChat({ api: '/api/chat', body: { - model: selectedModel === 'Speed' ? 'gpt-4o-mini' : selectedModel === 'Quality (GPT)' ? 'gpt-4o' : 'claude-3-5-sonnet-20240620', + model: selectedModel === 'Speed' ? 'claude-3-haiku-20240307' : 'claude-3-5-sonnet-20240620', }, maxToolRoundtrips: 2, onFinish: async (message, { finishReason }) => { @@ -165,19 +167,18 @@ export default function Home() { onClick={handleCopy} className="h-8 px-2 text-xs" > + {isCopied ? "copied" : "copy"} {isCopied ? ( - + ) : ( - + )} - {isCopied ? "Copied" : "Copy"} ); }; const models = [ - { name: 'Speed', description: 'High speed, but lower quality.', details: '(OpenAI/GPT-4o-mini)', icon: FastForward }, - { name: 'Quality (GPT)', description: 'Speed and quality, balanced.', details: '(OpenAI/GPT | Optimized)', icon: Sparkles }, + { name: 'Speed', description: 'High speed, but lower quality.', details: '(Anthropic/Claude-3-Haiku)', icon: FastForward }, { name: 'Quality (Claude)', description: 'High quality generation.', details: '(Anthropic/Claude-3.5-Sonnet)', icon: Sparkles }, ]; @@ -190,7 +191,7 @@ export default function Home() { setSelectedModel(value); reload({ body: { - model: value === 'Speed' ? 'gpt-4o-mini' : value === 'Quality (GPT)' ? 'gpt-4o' : 'claude-3-5-sonnet-20240620', + model: value === 'Speed' ? 'claude-3-haiku-20240307' : 'claude-3-5-sonnet-20240620', }, }); } @@ -205,7 +206,7 @@ export default function Home() { setSuggestedQuestions([]); reload({ body: { - model: newSelectedModel === 'Speed' ? 'gpt-4o-mini' : newSelectedModel === 'Quality (GPT)' ? 'gpt-4o' : 'claude-3-5-sonnet-20240620', + model: newSelectedModel === 'Speed' ? 'claude-3-haiku-20240307' : 'claude-3-5-sonnet-20240620', }, }); } else { @@ -742,11 +743,31 @@ export default function Home() { }); }, [append, setMessages]); + const handleQueryEdit = useCallback(() => { + setIsEditingQuery(true); + setInput(lastSubmittedQuery); + }, [lastSubmittedQuery, setInput]); + + const handleQuerySubmit = useCallback((e: React.FormEvent) => { + e.preventDefault(); + if (input.trim()) { + setLastSubmittedQuery(input.trim()); + setIsEditingQuery(false); + setMessages([]); + setHasSubmitted(true); + setIsAnimating(true); + setSuggestedQuestions([]); + handleSubmit(e); + } else { + toast.error("Please enter a search query."); + } + }, [input, setMessages, handleSubmit]); + const exampleQueries = [ "Weather in Doha", "Latest on Paris Olympics", "Count the number of r's in strawberry", - "OpenAI GPT-4o mini" + "Explain Claude 3.5 Sonnet" ]; return ( @@ -779,7 +800,7 @@ export default function Home() { className={`flex items-center font-semibold ${models.find((model) => model.name === selectedModel)?.name.includes('Quality') ? 'text-purple-500' : 'text-green-500'} focus:outline-none focus:ring-0 `} > {selectedModel === 'Speed' && } - {(selectedModel === 'Quality (GPT)' || selectedModel === 'Quality (Claude)') && } + {(selectedModel === 'Quality (Claude)') && } {selectedModel} @@ -886,29 +907,53 @@ export default function Home() { transition={{ duration: 0.5, delay: 0.2 }} className="flex-grow min-w-0" > - - - -

- {lastSubmittedQuery} -

-
- -

{lastSubmittedQuery}

-
-
-
+ {isEditingQuery ? ( +
+ setInput(e.target.value)} + className="flex-grow" + /> + +
+ ) : ( + + + +

+ {lastSubmittedQuery} +

+
+ +

{lastSubmittedQuery}

+
+
+
+ )} + {!isEditingQuery && ( + + )} diff --git a/package.json b/package.json index f887a21..4610473 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@ai-sdk/anthropic": "^0.0.37", - "@ai-sdk/openai": "^0.0.40", "@e2b/code-interpreter": "^0.0.8", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-dialog": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4197451..7ec5c9d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,6 @@ dependencies: '@ai-sdk/anthropic': specifier: ^0.0.37 version: 0.0.37(zod@3.23.8) - '@ai-sdk/openai': - specifier: ^0.0.40 - version: 0.0.40(zod@3.23.8) '@e2b/code-interpreter': specifier: ^0.0.8 version: 0.0.8 @@ -49,7 +46,7 @@ dependencies: version: 1.4.0 ai: specifier: latest - version: 3.3.6(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8) + version: 3.3.7(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8) class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -141,19 +138,8 @@ packages: zod: 3.23.8 dev: false - /@ai-sdk/openai@0.0.40(zod@3.23.8): - resolution: {integrity: sha512-9Iq1UaBHA5ZzNv6j3govuKGXrbrjuWvZIgWNJv4xzXlDMHu9P9hnqlBr/Aiay54WwCuTVNhTzAUTfFgnTs2kbQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.0.0 - dependencies: - '@ai-sdk/provider': 0.0.14 - '@ai-sdk/provider-utils': 1.0.5(zod@3.23.8) - zod: 3.23.8 - dev: false - - /@ai-sdk/provider-utils@1.0.10(zod@3.23.8): - resolution: {integrity: sha512-xciXF2PorLQMNdhYe+n9CafVkXZANHURsME85RXjtAoZSs631l2t8Blqwz2C/pHUb9bxLdMRRuIEB4PnHLnHvQ==} + /@ai-sdk/provider-utils@1.0.11(zod@3.23.8): + resolution: {integrity: sha512-u3BmXKg4MeA5s5eN9bWP4ybGJuOTRC2H0YacMCag5fcZ14S6kfukGE8MzRsGU2wTv6A16zLY0XqVvwcqe13mUA==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -161,23 +147,7 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider': 0.0.18 - eventsource-parser: 1.1.2 - nanoid: 3.3.6 - secure-json-parse: 2.7.0 - zod: 3.23.8 - dev: false - - /@ai-sdk/provider-utils@1.0.5(zod@3.23.8): - resolution: {integrity: sha512-XfOawxk95X3S43arn2iQIFyWGMi0DTxsf9ETc6t7bh91RPWOOPYN1tsmS5MTKD33OGJeaDQ/gnVRzXUCRBrckQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.0.0 - peerDependenciesMeta: - zod: - optional: true - dependencies: - '@ai-sdk/provider': 0.0.14 + '@ai-sdk/provider': 0.0.19 eventsource-parser: 1.1.2 nanoid: 3.3.6 secure-json-parse: 2.7.0 @@ -200,13 +170,6 @@ packages: zod: 3.23.8 dev: false - /@ai-sdk/provider@0.0.14: - resolution: {integrity: sha512-gaQ5Y033nro9iX1YUjEDFDRhmMcEiCk56LJdIUbX5ozEiCNCfpiBpEqrjSp/Gp5RzBS2W0BVxfG7UGW6Ezcrzg==} - engines: {node: '>=18'} - dependencies: - json-schema: 0.4.0 - dev: false - /@ai-sdk/provider@0.0.15: resolution: {integrity: sha512-phX/YdwKd8q8/uZ7MsUytcHuN5KvT+wgM+y78eu6E+VyFE3GRwelctBFnaaA96uRL6xnKNmb0e7e+2fDOYuBoA==} engines: {node: '>=18'} @@ -214,15 +177,15 @@ packages: json-schema: 0.4.0 dev: false - /@ai-sdk/provider@0.0.18: - resolution: {integrity: sha512-LF4aUAKDTKIHa2e7ozwRJDMhUC9cs7t224sUilG1HfyFWyyh+01oPZwMob/hj111SozZkvXIukN0BIa+sXS0mw==} + /@ai-sdk/provider@0.0.19: + resolution: {integrity: sha512-WXyyJ0Fqg0OHzUQ/spem4jRFPq+NkYB8YNTVbSQrE+Rpxtm7DqK1x9MrHtIEhoikCMg9wBOCbm7HuMnfsIR5GA==} engines: {node: '>=18'} dependencies: json-schema: 0.4.0 dev: false - /@ai-sdk/react@0.0.42(react@18.3.1)(zod@3.23.8): - resolution: {integrity: sha512-oiwXKLc5n7SwaTZWldMrpyJEWTgLh35NXuxQIGDGVHTIryxxMk9lgOQ+vQ8uri3WGMzGQQGRfwh8MTijJ8cN2A==} + /@ai-sdk/react@0.0.43(react@18.3.1)(zod@3.23.8): + resolution: {integrity: sha512-maWuV9529tIVVST9iXgnxBWUoM5Z8DW0lyrMYnsaLJAZ4kostt+PbqJjhy6eAQPzmXGcu4+OdgT1v1ZNCZR4+Q==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 @@ -233,15 +196,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.10(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.30(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) react: 18.3.1 swr: 2.2.5(react@18.3.1) zod: 3.23.8 dev: false - /@ai-sdk/solid@0.0.33(zod@3.23.8): - resolution: {integrity: sha512-6AV4g6IrQ7bPcfQgwOjNMT50W2lljk/sgzJ3qx+Bt/lobvhA7khyW1RVYTnxx3OBdf4/qB1D2BAAbUrrm/na8A==} + /@ai-sdk/solid@0.0.34(zod@3.23.8): + resolution: {integrity: sha512-puVv9rrskWXrtaikDbpoMkGeTboa4ZY6wTmC66Xw9rhZ0zK5yN15lLJBf/LeBIV6J1V9F9bBxjRX7UQXjE3sZg==} engines: {node: '>=18'} peerDependencies: solid-js: ^1.7.7 @@ -249,14 +212,14 @@ packages: solid-js: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.10(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.30(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) transitivePeerDependencies: - zod dev: false - /@ai-sdk/svelte@0.0.35(svelte@4.2.18)(zod@3.23.8): - resolution: {integrity: sha512-vbDmvcu2MRZvvxoOtCUH8ydKSaugaQkhiBtZRp/U1YvSIuzR7xUkYSf0EQ173kWBWsaoPO9PFava0WxF7k1q4g==} + /@ai-sdk/svelte@0.0.36(svelte@4.2.18)(zod@3.23.8): + resolution: {integrity: sha512-5pSaKt+UZK9+9AsbIYLs4REtAc/0HOLX4DK3nRtMcDqDLoWDoSJDKK/EjDMYVhYB1gqQmT0AeiSLo2WH0nf00w==} engines: {node: '>=18'} peerDependencies: svelte: ^3.0.0 || ^4.0.0 @@ -264,16 +227,16 @@ packages: svelte: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.10(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.30(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) sswr: 2.1.0(svelte@4.2.18) svelte: 4.2.18 transitivePeerDependencies: - zod dev: false - /@ai-sdk/ui-utils@0.0.30(zod@3.23.8): - resolution: {integrity: sha512-ifmYSQtSVdeY1XlOFirAdbgWVnSg5hYPhG8bEiNI9TvR1HFdMN/zZhxscjLyKXXAelf/ACYvUjwNQmCnWSurZQ==} + /@ai-sdk/ui-utils@0.0.31(zod@3.23.8): + resolution: {integrity: sha512-PA1mI+WC69Bc8JCTDOXwhLv9OAfocex/d+MRtQjfuWE6jTBjkBMa6davw+JjN7Vcp6zP0JLQG6gd7n6MnkFepQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -281,16 +244,16 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider': 0.0.18 - '@ai-sdk/provider-utils': 1.0.10(zod@3.23.8) + '@ai-sdk/provider': 0.0.19 + '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) json-schema: 0.4.0 secure-json-parse: 2.7.0 zod: 3.23.8 zod-to-json-schema: 3.22.5(zod@3.23.8) dev: false - /@ai-sdk/vue@0.0.34(vue@3.4.35)(zod@3.23.8): - resolution: {integrity: sha512-Nbht63i4NJrau5Yrf6dH6paH2mj/5CSmHopjA0IRHdOTvh3lKe382oZP2hLnN/xa575r25as67l7P/j/iq8ULQ==} + /@ai-sdk/vue@0.0.35(vue@3.4.35)(zod@3.23.8): + resolution: {integrity: sha512-7cPShsxiyxzoSB5orjCqwnFWvjpM/YX2W+a2K6lyV2Z2JAgHc+4PHhVnrKwc0c9Q7vwfpvW+3MoKM6U2xZaS+w==} engines: {node: '>=18'} peerDependencies: vue: ^3.3.4 @@ -298,8 +261,8 @@ packages: vue: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.10(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.30(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) swrv: 1.0.4(vue@3.4.35) vue: 3.4.35(typescript@5.5.4) transitivePeerDependencies: @@ -1584,8 +1547,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - /ai@3.3.6(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8): - resolution: {integrity: sha512-XrbjYyugNF0SlTEVAO8B7tlW6iW47+DYk1hkyGj0vwp93J0F19bdrtN6NbFJOzSHj/R4FGvGk0IPQz5A3Eq1IQ==} + /ai@3.3.7(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8): + resolution: {integrity: sha512-xMfQdOL2s0aiGozdUO0ahOAfcwkGBUye3q4wC64PPNpmE3Qeing1Tv4JSsHk0zymhCMHBDiI1Tky8BNGdu+V6A==} engines: {node: '>=18'} peerDependencies: openai: ^4.42.0 @@ -1605,13 +1568,13 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider': 0.0.18 - '@ai-sdk/provider-utils': 1.0.10(zod@3.23.8) - '@ai-sdk/react': 0.0.42(react@18.3.1)(zod@3.23.8) - '@ai-sdk/solid': 0.0.33(zod@3.23.8) - '@ai-sdk/svelte': 0.0.35(svelte@4.2.18)(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.30(zod@3.23.8) - '@ai-sdk/vue': 0.0.34(vue@3.4.35)(zod@3.23.8) + '@ai-sdk/provider': 0.0.19 + '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) + '@ai-sdk/react': 0.0.43(react@18.3.1)(zod@3.23.8) + '@ai-sdk/solid': 0.0.34(zod@3.23.8) + '@ai-sdk/svelte': 0.0.36(svelte@4.2.18)(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) + '@ai-sdk/vue': 0.0.35(vue@3.4.35)(zod@3.23.8) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0