From 0c54998413f0131dc17a5dd4c3fcb2d5513aef28 Mon Sep 17 00:00:00 2001 From: zaidmukaddam Date: Mon, 19 Aug 2024 00:28:21 +0530 Subject: [PATCH] feat: added nearby_search tool to chat API and remove model switching --- README.md | 3 +- app/api/chat/route.ts | 62 ++++-- app/page.tsx | 451 ++++++++++++++++++++++++------------------ package.json | 1 + pnpm-lock.yaml | 68 ++++--- 5 files changed, 354 insertions(+), 231 deletions(-) diff --git a/README.md b/README.md index e9e0698..ce13f86 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,8 @@ A minimalistic AI-powered search engine that helps you find information on the i - [OpenWeather](https://openweathermap.org/) - [E2B](https://e2b.dev/) -## LLMs used +## LLM used - [OpenAI's GPT 4o mini](https://openai.com/index/gpt-4o-mini-advancing-cost-efficient-intelligence/) -- [Anthropic's Claude 3.5 Sonnet](https://www.anthropic.com/news/claude-3-5-sonnet) ### Deploy your own diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 1d4701f..11220a9 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -9,19 +9,11 @@ import { geolocation } from "@vercel/functions"; export const maxDuration = 60; export async function POST(req: Request) { - const { messages, model } = await req.json(); + const { messages } = await req.json(); const { latitude, longitude, city } = geolocation(req) - let ansmodel; - - if (model === "claude-3-5-sonnet-20240620") { - ansmodel = anthropic(model); - } else { - ansmodel = openai(model); - } - const result = await streamText({ - model: ansmodel, + model: openai("gpt-4o-mini"), messages: convertToCoreMessages(messages), temperature: 0, maxTokens: 800, @@ -36,7 +28,7 @@ The user is located in ${city}(${latitude}, ${longitude}). Here are the tools available to you: -web_search, retrieve, get_weather_data, programming +web_search, retrieve, get_weather_data, programming, nearby_search Here is the general guideline per tool to follow when responding to user queries: @@ -44,6 +36,8 @@ Here is the general guideline per tool to follow when responding to user queries - If you need to retrieve specific information from a webpage, use the retrieve tool. Then, compose your response based on the retrieved information. - For weather-related queries, use the get_weather_data tool. Then, provide the weather information in your response. - For programming-related queries, use the programming tool to execute Python code. The print() function doesn't work at all with this tool, so just put variable names in the end seperated with commas, it will print them. Then, compose your response based on the output of the code execution. +- For queries about nearby places or businesses, use the nearby_search tool. Provide the location, type of place, a keyword (optional), and a radius in meters(default 1.5 Kilometers). Then, compose your response based on the search results. +- Do not use the retrieve tool for general web searches. It is only for retrieving specific information from a URL.- Do not use the retrieve tool for general web searches. It is only for retrieving specific information from a URL. Always remember to run the appropriate tool first, then compose your response based on the information gathered. All tool should be called only once per response. @@ -239,6 +233,52 @@ Just run the tool and provide the answer.`, return "There was no output of the execution."; }, }), + nearby_search: tool({ + description: "Search for nearby places using Google Maps API.", + parameters: z.object({ + location: z.string().describe("The location to search near (e.g., 'New York City' or '1600 Amphitheatre Parkway, Mountain View, CA')."), + type: z.string().describe("The type of place to search for (e.g., restaurant, cafe, park)."), + keyword: z.string().optional().describe("An optional keyword to refine the search."), + radius: z.number().default(3000).describe("The radius of the search area in meters (max 50000, default 3000)."), + }), + execute: async ({ location, type, keyword, radius }: { location: string; type: string; keyword?: string; radius: number }) => { + const apiKey = process.env.GOOGLE_MAPS_API_KEY; + + // First, use the Geocoding API to get the coordinates + const geocodeUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(location)}&key=${apiKey}`; + const geocodeResponse = await fetch(geocodeUrl); + const geocodeData = await geocodeResponse.json(); + + if (geocodeData.status !== "OK" || !geocodeData.results[0]) { + throw new Error("Failed to geocode the location"); + } + + const { lat, lng } = geocodeData.results[0].geometry.location; + + // perform the nearby search + let searchUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat},${lng}&radius=${radius}&type=${type}&key=${apiKey}`; + + if (keyword) { + searchUrl += `&keyword=${encodeURIComponent(keyword)}`; + } + + const searchResponse = await fetch(searchUrl); + const searchData = await searchResponse.json(); + + return { + results: searchData.results.slice(0, 5).map((place: any) => ({ + name: place.name, + vicinity: place.vicinity, + rating: place.rating, + user_ratings_total: place.user_ratings_total, + place_id: place.place_id, + location: place.geometry.location, + })), + center: { lat, lng }, + formatted_address: geocodeData.results[0].formatted_address, + }; + }, + }), }, toolChoice: "auto", }); diff --git a/app/page.tsx b/app/page.tsx index 8abb0b4..d41f8eb 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -23,8 +23,6 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { SearchIcon, - ChevronDown, - FastForward, Sparkles, ArrowRight, Globe, @@ -40,6 +38,8 @@ import { RefreshCw, Heart, X, + MapPin, + Star, } from 'lucide-react'; import { HoverCard, @@ -52,27 +52,12 @@ import { AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip" - import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -92,9 +77,17 @@ import { ChartTooltipContent, } from "@/components/ui/chart"; import { GitHubLogoIcon } from '@radix-ui/react-icons'; +import { Skeleton } from '@/components/ui/skeleton'; export const maxDuration = 60; +declare global { + interface Window { + google: any; + initMap: () => void; + } +} + export default function Home() { const router = useRouter(); const inputRef = useRef(null); @@ -103,18 +96,11 @@ export default function Home() { const [isAnimating, setIsAnimating] = useState(false); const bottomRef = useRef(null); const [suggestedQuestions, setSuggestedQuestions] = useState([]); - const [isModelSelectorOpen, setIsModelSelectorOpen] = useState(false); - const [selectedModel, setSelectedModel] = useState('Speed'); 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({ + const { isLoading, input, messages, setInput, append, handleSubmit, setMessages } = useChat({ api: '/api/chat', - body: { - model: selectedModel === 'Speed' ? 'gpt-4o-mini' : 'claude-3-5-sonnet-20240620', - }, maxToolRoundtrips: 1, onFinish: async (message, { finishReason }) => { if (finishReason === 'stop') { @@ -163,101 +149,6 @@ export default function Home() { ); }; - const models = [ - { name: 'Speed', description: 'High speed, but lower quality.', details: '(OpenAI/GPT-4o-mini)', icon: FastForward }, - { name: 'Quality', description: 'High quality generation.', details: '(Anthropic/Claude-3.5-Sonnet)', icon: Sparkles }, - ]; - - const handleModelChange = (value: string) => { - if (value !== selectedModel) { - if (hasSubmitted) { - setNewSelectedModel(value); - setShowConfirmModal(true); - } else { - setSelectedModel(value); - reload({ - body: { - model: value === 'Speed' ? 'gpt-4o-mini' : 'claude-3-5-sonnet-20240620', - }, - }); - } - } - setIsModelSelectorOpen(false); - }; - - const handleConfirmModelChange = () => { - if (newSelectedModel !== selectedModel) { - setSelectedModel(newSelectedModel); - setShowConfirmModal(false); - setSuggestedQuestions([]); - reload({ - body: { - model: newSelectedModel === 'Speed' ? 'gpt-4o-mini' : 'claude-3-5-sonnet-20240620', - }, - }); - } else { - setShowConfirmModal(false); - } - }; - - interface ModelSelectorProps { - selectedModel: string; - onModelSelect: (model: string) => void; - isDisabled: boolean; - } - - function ModelSelector({ selectedModel, onModelSelect, isDisabled }: ModelSelectorProps) { - const [isOpen, setIsOpen] = useState(false); - - const handleToggle = () => { - if (!isDisabled) { - setIsOpen(!isOpen); - } - }; - - return ( - - - - - - {models.map((model) => ( - onModelSelect(model.name)} - className={`flex items-start p-3 !font-sans rounded-md ${selectedModel === model.name ? 'bg-muted' : ''}`} - > - -
-
- {model.name} - {selectedModel === model.name && ( - - Active - - )} -
-
{model.description}
-
{model.details}
-
-
- ))} -
-
- ); - } - interface WeatherDataPoint { date: string; minTemp: number; @@ -366,10 +257,189 @@ export default function Home() { WeatherChart.displayName = 'WeatherChart'; + const isValidCoordinate = (coord: number) => { + return typeof coord === 'number' && !isNaN(coord) && isFinite(coord); + }; + + const loadGoogleMapsScript = (callback: () => void) => { + if (window.google && window.google.maps) { + callback(); + return; + } + + const existingScript = document.getElementById('googleMapsScript'); + if (existingScript) { + existingScript.remove(); + } + + window.initMap = callback; + const script = document.createElement('script'); + script.id = 'googleMapsScript'; + script.src = `https://maps.googleapis.com/maps/api/js?key=${process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}&libraries=places&callback=initMap`; + script.async = true; + script.defer = true; + document.head.appendChild(script); + }; + + const MapComponent = React.memo(({ center, places }: { center: { lat: number; lng: number }, places: any[] }) => { + const mapRef = useRef(null); + const [mapError, setMapError] = useState(null); + const googleMapRef = useRef(null); + const markersRef = useRef([]); + + const memoizedCenter = useMemo(() => center, [center]); + const memoizedPlaces = useMemo(() => places, [places]); + + const initializeMap = useCallback(() => { + if (mapRef.current && isValidCoordinate(memoizedCenter.lat) && isValidCoordinate(memoizedCenter.lng)) { + if (!googleMapRef.current) { + googleMapRef.current = new window.google.maps.Map(mapRef.current, { + center: memoizedCenter, + zoom: 14, + }); + } else { + googleMapRef.current.setCenter(memoizedCenter); + } + + // Clear existing markers + markersRef.current.forEach(marker => marker.setMap(null)); + markersRef.current = []; + + memoizedPlaces.forEach((place) => { + if (isValidCoordinate(place.location.lat) && isValidCoordinate(place.location.lng)) { + const MarkerClass = window.google.maps.marker?.AdvancedMarkerElement || window.google.maps.Marker; + const marker = new MarkerClass({ + position: place.location, + map: googleMapRef.current, + title: place.name, + }); + markersRef.current.push(marker); + } + }); + } else { + setMapError('Invalid coordinates provided'); + } + }, [memoizedCenter, memoizedPlaces]); + + useEffect(() => { + loadGoogleMapsScript(() => { + try { + initializeMap(); + } catch (error) { + console.error('Error initializing map:', error); + setMapError('Failed to initialize Google Maps'); + } + }); + + return () => { + // Clean up markers when component unmounts + markersRef.current.forEach(marker => marker.setMap(null)); + }; + }, [initializeMap]); + + if (mapError) { + return
{mapError}
; + } + + return
; + }); + + MapComponent.displayName = 'MapComponent'; + + const MapSkeleton = () => ( + + ); + + const PlaceDetails = ({ place }: { place: any }) => ( +
+
+

{place.name}

+

+ {place.vicinity} +

+
+ {place.rating && ( + + + {place.rating} ({place.user_ratings_total}) + + )} +
+ ); + const renderToolInvocation = (toolInvocation: ToolInvocation, index: number) => { const args = JSON.parse(JSON.stringify(toolInvocation.args)); const result = 'result' in toolInvocation ? JSON.parse(JSON.stringify(toolInvocation.result)) : null; + if (toolInvocation.toolName === 'nearby_search') { + if (!result) { + return ( +
+
+ + Searching nearby places... +
+
+ {[0, 1, 2].map((index) => ( + + ))} +
+
+ ); + } + + if (isLoading) { + return ( + + + + + + + + + ); + } + + return ( + + + + + Nearby {args.type ? args.type.charAt(0).toUpperCase() + args.type.slice(1) + 's' : 'Places'} + {args.keyword && {args.keyword}} + + + + + + + Place Details + +
+ {result.results.map((place: any, placeIndex: number) => ( + + ))} +
+
+
+
+
+
+ ); + } + if (toolInvocation.toolName === 'get_weather_data') { if (!result) { return ( @@ -505,6 +575,74 @@ export default function Home() { ); } + if (toolInvocation.toolName === 'nearby_search') { + if (!result) { + return ( +
+
+ + Searching nearby places... +
+
+ {[0, 1, 2].map((index) => ( + + ))} +
+
+ ); + } + + const mapUrl = `https://www.google.com/maps/embed/v1/search?key=${process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}&q=${encodeURIComponent(args.type)}¢er=${result.results[0].geometry.location.lat},${result.results[0].geometry.location.lng}&zoom=14`; + + return ( + + + + + Nearby {args.type.charAt(0).toUpperCase() + args.type.slice(1)}s + + + +
+ +
+
+ {result.results.map((place: any, placeIndex: number) => ( +
+
+

{place.name}

+

{place.vicinity}

+
+ + {place.rating} ★ ({place.user_ratings_total}) + +
+ ))} +
+
+
+ ); + } + return (
{!result ? ( @@ -600,8 +738,7 @@ export default function Home() { index: number; } - const CitationComponent: React.FC = React.memo(({ href, children, index }) => { - const citationText = Array.isArray(children) ? children[0] : children; + const CitationComponent: React.FC = React.memo(({ href, index }) => { const faviconUrl = `https://www.google.com/s2/favicons?sz=128&domain=${new URL(href).hostname}`; return ( @@ -789,17 +926,17 @@ export default function Home() {
-
+
-

MiniPerplx

+

MiniPerplx

{!hasSubmitted && -

- A minimalistic AI-powered search engine that helps you find information on the internet. +

+ In search for minimalism and simplicity

}
@@ -811,48 +948,6 @@ export default function Home() { exit={{ opacity: 0, y: 20 }} transition={{ duration: 0.5 }} > -
- - {isModelSelectorOpen && ( -
- {models.map((model) => ( - - ))} -
- )} -
-
- - )}
@@ -1078,20 +1167,6 @@ export default function Home() { )} - - - - Confirm Model Change - - Are you sure you want to change the model? This will change the quality of the responses and cannot be undone. - - - - - - - -
); } \ No newline at end of file diff --git a/package.json b/package.json index 70641b2..d78bb5b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@tailwindcss/typography": "^0.5.13", + "@types/googlemaps": "^3.43.3", "@vercel/analytics": "^1.3.1", "@vercel/functions": "^1.4.0", "ai": "latest", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb0a29a..4bb8271 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ dependencies: '@tailwindcss/typography': specifier: ^0.5.13 version: 0.5.13(tailwindcss@3.4.7) + '@types/googlemaps': + specifier: ^3.43.3 + version: 3.43.3 '@vercel/analytics': specifier: ^1.3.1 version: 1.3.1(next@14.2.5)(react@18.3.1) @@ -49,7 +52,7 @@ dependencies: version: 1.4.0 ai: specifier: latest - version: 3.3.7(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8) + version: 3.3.9(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 @@ -198,8 +201,8 @@ packages: json-schema: 0.4.0 dev: false - /@ai-sdk/react@0.0.43(react@18.3.1)(zod@3.23.8): - resolution: {integrity: sha512-maWuV9529tIVVST9iXgnxBWUoM5Z8DW0lyrMYnsaLJAZ4kostt+PbqJjhy6eAQPzmXGcu4+OdgT1v1ZNCZR4+Q==} + /@ai-sdk/react@0.0.45(react@18.3.1)(zod@3.23.8): + resolution: {integrity: sha512-rPsjHtJ+qsWoRV88xzEpvPXhqRBF2wljGjWrsFcao4p48hKwZkuhW/zzpxZj3A4ZPy6jpDHRTYtqMuPHVpc9Eg==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 @@ -210,15 +213,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.33(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.34(zod@3.23.8): - resolution: {integrity: sha512-puVv9rrskWXrtaikDbpoMkGeTboa4ZY6wTmC66Xw9rhZ0zK5yN15lLJBf/LeBIV6J1V9F9bBxjRX7UQXjE3sZg==} + /@ai-sdk/solid@0.0.36(zod@3.23.8): + resolution: {integrity: sha512-pm57goMQczSpPTNrUrwbab5BybZYofBRZ10UkTi2KgJP5i+S/sGHSh/xtgZz+xNpUt42pk8aYvOiNDN1ppjkDA==} engines: {node: '>=18'} peerDependencies: solid-js: ^1.7.7 @@ -226,14 +229,14 @@ packages: solid-js: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) transitivePeerDependencies: - zod dev: false - /@ai-sdk/svelte@0.0.36(svelte@4.2.18)(zod@3.23.8): - resolution: {integrity: sha512-5pSaKt+UZK9+9AsbIYLs4REtAc/0HOLX4DK3nRtMcDqDLoWDoSJDKK/EjDMYVhYB1gqQmT0AeiSLo2WH0nf00w==} + /@ai-sdk/svelte@0.0.38(svelte@4.2.18)(zod@3.23.8): + resolution: {integrity: sha512-sTNkxzhS1B0TDdVWZR6yXG+3qQGYAxMwcEKzMVjm1VdpGlZits1PxF39aVvPldaWM8QB4MrVE+H5b5dTA43D0Q==} engines: {node: '>=18'} peerDependencies: svelte: ^3.0.0 || ^4.0.0 @@ -241,16 +244,16 @@ packages: svelte: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.33(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.31(zod@3.23.8): - resolution: {integrity: sha512-PA1mI+WC69Bc8JCTDOXwhLv9OAfocex/d+MRtQjfuWE6jTBjkBMa6davw+JjN7Vcp6zP0JLQG6gd7n6MnkFepQ==} + /@ai-sdk/ui-utils@0.0.33(zod@3.23.8): + resolution: {integrity: sha512-2oZjZzZG3AGQihO1d3mWqgFuywTtjBtkUEeE7d8nicw3QQv9m1MwrbQqRhhKbbBetBke6V9o5FQ5wngmb/+3iw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -258,16 +261,16 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider': 0.0.19 - '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) + '@ai-sdk/provider': 0.0.20 + '@ai-sdk/provider-utils': 1.0.13(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.35(vue@3.4.35)(zod@3.23.8): - resolution: {integrity: sha512-7cPShsxiyxzoSB5orjCqwnFWvjpM/YX2W+a2K6lyV2Z2JAgHc+4PHhVnrKwc0c9Q7vwfpvW+3MoKM6U2xZaS+w==} + /@ai-sdk/vue@0.0.37(vue@3.4.35)(zod@3.23.8): + resolution: {integrity: sha512-m7dMi6qRoWPuru9TyWUm5jXAPGSDb1SHZp/Q+uYjhNY1dZwgsZxJvSeakogzR37uhiRCg3Kg8fCypQJe+dikPA==} engines: {node: '>=18'} peerDependencies: vue: ^3.3.4 @@ -275,8 +278,8 @@ packages: vue: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.11(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.31(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) swrv: 1.0.4(vue@3.4.35) vue: 3.4.35(typescript@5.5.4) transitivePeerDependencies: @@ -1321,6 +1324,11 @@ packages: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: false + /@types/googlemaps@3.43.3: + resolution: {integrity: sha512-ZWNoz/O8MPEpiajvj7QiqCY8tTLFNqNZ/a+s+zTV58wFVNAvvqV4bdGfnsjTb5Cs4V6wEsLrX8XRhmnyYJ2Tdg==} + deprecated: 'Types for the Google Maps browser API have moved to @types/google.maps. Note: these types are not for the googlemaps npm package, which is a Node API.' + dev: false + /@types/hast@2.3.10: resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} dependencies: @@ -1561,8 +1569,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - /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==} + /ai@3.3.9(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8): + resolution: {integrity: sha512-PS6xHzYIH+iVLG3xd/jwsqZW7+X8GrSrsJwVB4ACMbxhZN8KRs4yUcnGEDPcvRkEL3PqHHJFUo9iVysdZwOUwg==} engines: {node: '>=18'} peerDependencies: openai: ^4.42.0 @@ -1582,13 +1590,13 @@ packages: zod: optional: true dependencies: - '@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) + '@ai-sdk/provider': 0.0.20 + '@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) + '@ai-sdk/react': 0.0.45(react@18.3.1)(zod@3.23.8) + '@ai-sdk/solid': 0.0.36(zod@3.23.8) + '@ai-sdk/svelte': 0.0.38(svelte@4.2.18)(zod@3.23.8) + '@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) + '@ai-sdk/vue': 0.0.37(vue@3.4.35)(zod@3.23.8) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0