Refactor dependencies and add new features

This commit is contained in:
zaidmukaddam 2024-10-25 01:14:58 +05:30
parent be141ead3a
commit d2c299350e
8 changed files with 590 additions and 175 deletions

View File

@ -1,16 +1,15 @@
import { z } from "zod";
import { createAzure } from '@ai-sdk/azure';
import { anthropic } from '@ai-sdk/anthropic'
import {
convertToCoreMessages,
streamText,
tool,
experimental_createProviderRegistry
} from "ai";
import { createAnthropicVertex } from 'anthropic-vertex-ai';
import { BlobRequestAbortedError, put } from '@vercel/blob';
import { CodeInterpreter } from "@e2b/code-interpreter";
import FirecrawlApp from '@mendable/firecrawl-js';
import { GoogleAuth } from 'google-auth-library';
// Allow streaming responses up to 60 seconds
export const maxDuration = 120;
@ -21,33 +20,9 @@ const azure = createAzure({
apiKey: process.env.AZURE_API_KEY,
});
// Helper function to get Google credentials
// You can encode your service account key using the following command:
// base64 -i /path/to/your-service-account-key.json | tr -d '\n' > encoded_credentials.txt
// Then set the GOOGLE_APPLICATION_CREDENTIALS_BASE64 environment variable to the contents of encoded_credentials.txt
function getCredentials() {
const credentialsBase64 = process.env.GOOGLE_APPLICATION_CREDENTIALS_BASE64;
if (!credentialsBase64) {
throw new Error('GOOGLE_APPLICATION_CREDENTIALS_BASE64 environment variable is not set');
}
return JSON.parse(Buffer.from(credentialsBase64, 'base64').toString());
}
// Google Vertex setup for Anthropic
const auth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
credentials: getCredentials(),
});
const anthropicVertex = createAnthropicVertex({
region: process.env.GOOGLE_VERTEX_REGION,
projectId: process.env.GOOGLE_VERTEX_PROJECT_ID,
googleAuth: auth,
});
// Provider registry
const registry = experimental_createProviderRegistry({
anthropicVertex,
anthropic,
azure,
});
@ -75,6 +50,7 @@ export async function POST(req: Request) {
topP: 0.5,
frequencyPenalty: 0,
presencePenalty: 0,
experimental_activeTools: ["get_weather_data","programming", "web_search", "text_translate"],
system: `
You are an expert AI web search engine called MiniPerplx, that helps users find information on the internet with no bullshit talks.
Always start with running the tool(s) and then and then only write your response AT ALL COSTS!!

View File

@ -123,6 +123,7 @@ import {
} from "@/components/ui/table";
import Autoplay from 'embla-carousel-autoplay';
import FormComponent from '@/components/ui/form-component';
import WeatherChart from '@/components/weather-chart';
export const maxDuration = 60;
@ -320,117 +321,6 @@ GPT-4o has been re-enabled! You can use it by selecting the model from the dropd
);
};
// Weather chart components
interface WeatherDataPoint {
date: string;
minTemp: number;
maxTemp: number;
}
const WeatherChart: React.FC<{ result: any }> = React.memo(({ result }) => {
const { chartData, minTemp, maxTemp } = useMemo(() => {
const weatherData: WeatherDataPoint[] = result.list.map((item: any) => ({
date: new Date(item.dt * 1000).toLocaleDateString(),
minTemp: Number((item.main.temp_min - 273.15).toFixed(1)),
maxTemp: Number((item.main.temp_max - 273.15).toFixed(1)),
}));
// Group data by date and calculate min and max temperatures
const groupedData: { [key: string]: WeatherDataPoint } = weatherData.reduce((acc, curr) => {
if (!acc[curr.date]) {
acc[curr.date] = { ...curr };
} else {
acc[curr.date].minTemp = Math.min(acc[curr.date].minTemp, curr.minTemp);
acc[curr.date].maxTemp = Math.max(acc[curr.date].maxTemp, curr.maxTemp);
}
return acc;
}, {} as { [key: string]: WeatherDataPoint });
const chartData = Object.values(groupedData);
// Calculate overall min and max temperatures
const minTemp = Math.min(...chartData.map(d => d.minTemp));
const maxTemp = Math.max(...chartData.map(d => d.maxTemp));
return { chartData, minTemp, maxTemp };
}, [result]);
const chartConfig: ChartConfig = useMemo(() => ({
minTemp: {
label: "Min Temp.",
color: "hsl(var(--chart-1))",
},
maxTemp: {
label: "Max Temp.",
color: "hsl(var(--chart-2))",
},
}), []);
return (
<Card className="my-4 shadow-none bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700">
<CardHeader>
<CardTitle className="text-neutral-800 dark:text-neutral-100">Weather Forecast for {result.city.name}</CardTitle>
<CardDescription className="text-neutral-600 dark:text-neutral-400">
Showing min and max temperatures for the next 5 days
</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig}>
<ResponsiveContainer width="100%" height={300}>
<LineChart
data={chartData}
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
<XAxis
dataKey="date"
tickFormatter={(value) => new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}
stroke="#9CA3AF"
/>
<YAxis
domain={[Math.floor(minTemp) - 2, Math.ceil(maxTemp) + 2]}
tickFormatter={(value) => `${value}°C`}
stroke="#9CA3AF"
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Line
type="monotone"
dataKey="minTemp"
stroke="var(--color-minTemp)"
strokeWidth={2}
dot={false}
name="Min Temp."
/>
<Line
type="monotone"
dataKey="maxTemp"
stroke="var(--color-maxTemp)"
strokeWidth={2}
dot={false}
name="Max Temp."
/>
</LineChart>
</ResponsiveContainer>
</ChartContainer>
</CardContent>
<CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center gap-2 font-medium leading-none text-neutral-800 dark:text-neutral-100">
{result.city.name}, {result.city.country}
</div>
<div className="flex items-center gap-2 leading-none text-neutral-600 dark:text-neutral-400">
Next 5 days forecast
</div>
</div>
</div>
</CardFooter>
</Card>
);
});
WeatherChart.displayName = 'WeatherChart';
// Google Maps components
@ -1144,20 +1034,6 @@ GPT-4o has been re-enabled! You can use it by selecting the model from the dropd
</div>
);
}
if (isLoading) {
return (
<Card className="my-4 shadow-none bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700">
<CardHeader>
<CardTitle className="h-6 w-3/4 bg-neutral-200 dark:bg-neutral-700 rounded animate-pulse" />
</CardHeader>
<CardContent>
<div className="h-[300px] bg-neutral-200 dark:bg-neutral-700 rounded animate-pulse" />
</CardContent>
</Card>
);
}
return <WeatherChart result={result} />;
}

View File

@ -0,0 +1,207 @@
import React, { useRef, useState, useEffect, useCallback, memo } from 'react';
import { Card, CardHeader, CardContent, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { MapPin, Star } from 'lucide-react';
import { Skeleton } from '@/components/ui/skeleton';
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,marker&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<HTMLDivElement>(null);
const [mapError, setMapError] = useState<string | null>(null);
const googleMapRef = useRef<google.maps.Map | null>(null);
const markersRef = useRef<google.maps.marker.AdvancedMarkerElement[]>([]);
const initializeMap = useCallback(async () => {
if (mapRef.current && isValidCoordinate(center.lat) && isValidCoordinate(center.lng)) {
const { Map } = await google.maps.importLibrary("maps") as google.maps.MapsLibrary;
const { AdvancedMarkerElement } = await google.maps.importLibrary("marker") as google.maps.MarkerLibrary;
if (!googleMapRef.current) {
googleMapRef.current = new Map(mapRef.current, {
center: center,
zoom: 14,
mapId: "347ff92e0c7225cf",
});
} else {
googleMapRef.current.setCenter(center);
}
markersRef.current.forEach(marker => marker.map = null);
markersRef.current = [];
places.forEach((place) => {
if (isValidCoordinate(place.location.lat) && isValidCoordinate(place.location.lng)) {
const marker = new AdvancedMarkerElement({
map: googleMapRef.current,
position: place.location,
title: place.name,
});
markersRef.current.push(marker);
}
});
} else {
setMapError('Invalid coordinates provided');
}
}, [center, places]);
useEffect(() => {
loadGoogleMapsScript(() => {
try {
initializeMap();
} catch (error) {
console.error('Error initializing map:', error);
setMapError('Failed to initialize Google Maps');
}
});
return () => {
markersRef.current.forEach(marker => marker.map = null);
};
}, [initializeMap]);
if (mapError) {
return <div className="h-64 flex items-center justify-center bg-neutral-100 dark:bg-neutral-800 text-neutral-800 dark:text-neutral-200">{mapError}</div>;
}
return <div ref={mapRef} className="w-full h-64" />;
});
MapComponent.displayName = 'MapComponent';
const MapSkeleton = () => (
<Skeleton className="w-full h-64 bg-neutral-200 dark:bg-neutral-700" />
);
const PlaceDetails = ({ place }: { place: any }) => (
<div className="flex justify-between items-start py-2">
<div>
<h4 className="font-semibold text-neutral-800 dark:text-neutral-200">{place.name}</h4>
<p className="text-sm text-neutral-600 dark:text-neutral-400 max-w-[200px]" title={place.vicinity}>
{place.vicinity}
</p>
</div>
{place.rating && (
<Badge variant="secondary" className="flex items-center bg-neutral-200 dark:bg-neutral-700 text-neutral-800 dark:text-neutral-200">
<Star className="h-3 w-3 mr-1 text-yellow-400" />
{place.rating} ({place.user_ratings_total})
</Badge>
)}
</div>
);
const MapEmbed = memo(({ location, zoom = 15 }: { location: string, zoom?: number }) => {
const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY;
const mapUrl = `https://www.google.com/maps/embed/v1/place?key=${apiKey}&q=${encodeURIComponent(location)}&zoom=${zoom}`;
return (
<div className="aspect-video w-full">
<iframe
width="100%"
height="100%"
style={{ border: 0 }}
loading="lazy"
allowFullScreen
referrerPolicy="no-referrer-when-downgrade"
src={mapUrl}
className='rounded-xl'
></iframe>
</div>
);
});
MapEmbed.displayName = 'MapEmbed';
const FindPlaceResult = memo(({ result }: { result: any }) => {
const place = result.candidates[0];
const location = `${place.geometry.location.lat},${place.geometry.location.lng}`;
return (
<Card className="w-full my-4 overflow-hidden shadow-none bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-neutral-800 dark:text-neutral-100">
<MapPin className="h-5 w-5 text-primary" />
<span>{place.name}</span>
</CardTitle>
</CardHeader>
<CardContent>
<MapEmbed location={location} />
<div className="mt-4 space-y-2 text-neutral-800 dark:text-neutral-200">
<p><strong>Address:</strong> {place.formatted_address}</p>
{place.rating && (
<div className="flex items-center">
<strong className="mr-2">Rating:</strong>
<Badge variant="secondary" className="flex items-center bg-neutral-200 dark:bg-neutral-700 text-neutral-800 dark:text-neutral-200">
<Star className="h-3 w-3 mr-1 text-yellow-400" />
{place.rating}
</Badge>
</div>
)}
{place.opening_hours && (
<p><strong>Open now:</strong> {place.opening_hours.open_now ? 'Yes' : 'No'}</p>
)}
</div>
</CardContent>
</Card>
);
});
FindPlaceResult.displayName = 'FindPlaceResult';
const TextSearchResult = React.memo(({ result }: { result: any }) => {
const centerLocation = result.results[0]?.geometry?.location;
const mapLocation = centerLocation ? `${centerLocation.lat},${centerLocation.lng}` : '';
return (
<Card className="w-full my-4 overflow-hidden shadow-none bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-neutral-800 dark:text-neutral-100">
<MapPin className="h-5 w-5 text-primary" />
<span>Text Search Results</span>
</CardTitle>
</CardHeader>
<CardContent>
{mapLocation && <MapComponent center={centerLocation} places={result.results} />}
<Accordion type="single" collapsible className="w-full mt-4">
<AccordionItem value="place-details">
<AccordionTrigger className="text-neutral-800 dark:text-neutral-200">Place Details</AccordionTrigger>
<AccordionContent>
<div className="space-y-4 max-h-64 overflow-y-auto">
{result.results.map((place: any, index: number) => (
<PlaceDetails key={index} place={place} />
))}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</CardContent>
</Card>
);
});
TextSearchResult.displayName = 'TextSearchResult';
export { MapComponent, MapSkeleton, FindPlaceResult, TextSearchResult };

View File

@ -0,0 +1,208 @@
import React, { useState, useCallback, useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import Marked, { ReactRenderer } from 'marked-react';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
import { Button } from '@/components/ui/button';
import { Check, Copy } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';
import { fetchMetadata } from '@/app/actions';
interface MarkdownRendererProps {
content: string;
}
interface CitationLink {
text: string;
link: string;
}
interface LinkMetadata {
title: string;
description: string;
}
const isValidUrl = (str: string) => {
try {
new URL(str);
return true;
} catch {
return false;
}
};
const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content }) => {
const [metadataCache, setMetadataCache] = useState<Record<string, LinkMetadata>>({});
const citationLinks = useMemo<CitationLink[]>(() => {
return Array.from(content.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)).map(([_, text, link]) => ({
text,
link,
}));
}, [content]);
const fetchMetadataWithCache = useCallback(async (url: string) => {
if (metadataCache[url]) {
return metadataCache[url];
}
const metadata = await fetchMetadata(url);
if (metadata) {
setMetadataCache(prev => ({ ...prev, [url]: metadata }));
}
return metadata;
}, [metadataCache]);
const CodeBlock = ({ language, children }: { language: string | undefined; children: string }) => {
const [isCopied, setIsCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(children);
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
};
return (
<div className="relative group">
<SyntaxHighlighter
language={language || 'text'}
style={oneDark}
showLineNumbers
wrapLines
customStyle={{
margin: 0,
borderRadius: '0.375rem',
fontSize: '0.875rem',
}}
>
{children}
</SyntaxHighlighter>
<Button
onClick={handleCopy}
className="absolute top-2 right-2 p-2 bg-neutral-700 dark:bg-neutral-600 bg-opacity-80 rounded-md opacity-0 group-hover:opacity-100 transition-opacity duration-200"
variant="ghost"
size="sm"
>
{isCopied ? <Check size={16} className="text-green-500" /> : <Copy size={16} className="text-neutral-200" />}
</Button>
</div>
);
};
const LinkPreview = ({ href }: { href: string }) => {
const [metadata, setMetadata] = useState<LinkMetadata | null>(null);
const [isLoading, setIsLoading] = useState(false);
React.useEffect(() => {
setIsLoading(true);
fetchMetadataWithCache(href).then((data) => {
setMetadata(data);
setIsLoading(false);
});
}, [href]);
if (isLoading) {
return (
<div className="flex items-center justify-center p-4">
<div className="animate-spin h-5 w-5 text-neutral-500 dark:text-neutral-400" />
</div>
);
}
const domain = new URL(href).hostname;
return (
<div className="flex flex-col space-y-2 bg-white dark:bg-neutral-800 rounded-md shadow-md overflow-hidden">
<div className="flex items-center space-x-2 p-3 bg-neutral-100 dark:bg-neutral-700">
<Image
src={`https://www.google.com/s2/favicons?domain=${domain}&sz=256`}
alt="Favicon"
width={20}
height={20}
className="rounded-sm"
/>
<span className="text-sm font-medium text-neutral-600 dark:text-neutral-300 truncate">{domain}</span>
</div>
<div className="px-3 pb-3">
<h3 className="text-base font-semibold text-neutral-800 dark:text-neutral-200 line-clamp-2">
{metadata?.title || "Untitled"}
</h3>
{metadata?.description && (
<p className="text-sm text-neutral-600 dark:text-neutral-400 mt-1 line-clamp-2">
{metadata.description}
</p>
)}
</div>
</div>
);
};
const renderHoverCard = (href: string, text: React.ReactNode, isCitation: boolean = false) => {
return (
<HoverCard>
<HoverCardTrigger asChild>
<Link
href={href}
target="_blank"
rel="noopener noreferrer"
className={isCitation ? "cursor-help text-sm text-primary py-0.5 px-1.5 m-0 bg-neutral-200 dark:bg-neutral-700 rounded-full no-underline" : "text-teal-600 dark:text-teal-400 no-underline hover:underline"}
>
{text}
</Link>
</HoverCardTrigger>
<HoverCardContent
side="top"
align="start"
className="w-80 p-0 shadow-lg"
>
<LinkPreview href={href} />
</HoverCardContent>
</HoverCard>
);
};
const renderer: Partial<ReactRenderer> = {
paragraph(children) {
return <p className="my-4 text-neutral-800 dark:text-neutral-200">{children}</p>;
},
code(children, language) {
return <CodeBlock language={language}>{String(children)}</CodeBlock>;
},
link(href, text) {
const citationIndex = citationLinks.findIndex(link => link.link === href);
if (citationIndex !== -1) {
return (
<sup>
{renderHoverCard(href, citationIndex + 1, true)}
</sup>
);
}
return isValidUrl(href) ? renderHoverCard(href, text) : <a href={href} className="text-blue-600 dark:text-blue-400 hover:underline">{text}</a>;
},
heading(children, level) {
const HeadingTag = `h${level}` as keyof JSX.IntrinsicElements;
const className = `text-${4 - level}xl font-bold my-4 text-neutral-800 dark:text-neutral-100`;
return <HeadingTag className={className}>{children}</HeadingTag>;
},
list(children, ordered) {
const ListTag = ordered ? 'ol' : 'ul';
return <ListTag className="list-inside list-disc my-4 pl-4 text-neutral-800 dark:text-neutral-200">{children}</ListTag>;
},
listItem(children) {
return <li className="my-2 text-neutral-800 dark:text-neutral-200">{children}</li>;
},
blockquote(children) {
return <blockquote className="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic my-4 text-neutral-700 dark:text-neutral-300">{children}</blockquote>;
},
};
return (
<div className="markdown-body dark:text-neutral-200">
<Marked renderer={renderer}>{content}</Marked>
</div>
);
};
export default MarkdownRenderer;

View File

@ -24,7 +24,7 @@ interface ModelSwitcherProps {
const models = [
{ value: "azure:gpt4o-mini", label: "GPT-4o Mini", icon: Zap, description: "High speed, good quality", color: "emerald" },
{ value: "anthropicVertex:claude-3-5-sonnet@20240620", label: "Claude", icon: Sparkles, description: "High quality, lower speed", color: "indigo" },
{ value: "anthropic:claude-3-5-sonnet-latest", label: "Claude", icon: Sparkles, description: "High quality, lower speed", color: "indigo" },
{ value: "azure:gpt-4o", label: "GPT-4o", icon: Cpu, description: "Higher quality, normal speed", color: "blue" },
];

View File

@ -0,0 +1,118 @@
import React, { useMemo } from 'react';
import { Line, LineChart, CartesianGrid, XAxis, YAxis, ResponsiveContainer } from "recharts";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, CardFooter } from "@/components/ui/card";
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart";
interface WeatherDataPoint {
date: string;
minTemp: number;
maxTemp: number;
}
interface WeatherChartProps {
result: any;
}
const WeatherChart: React.FC<WeatherChartProps> = React.memo(({ result }) => {
const { chartData, minTemp, maxTemp } = useMemo(() => {
const weatherData: WeatherDataPoint[] = result.list.map((item: any) => ({
date: new Date(item.dt * 1000).toLocaleDateString(),
minTemp: Number((item.main.temp_min - 273.15).toFixed(1)),
maxTemp: Number((item.main.temp_max - 273.15).toFixed(1)),
}));
const groupedData: { [key: string]: WeatherDataPoint } = weatherData.reduce((acc, curr) => {
if (!acc[curr.date]) {
acc[curr.date] = { ...curr };
} else {
acc[curr.date].minTemp = Math.min(acc[curr.date].minTemp, curr.minTemp);
acc[curr.date].maxTemp = Math.max(acc[curr.date].maxTemp, curr.maxTemp);
}
return acc;
}, {} as { [key: string]: WeatherDataPoint });
const chartData = Object.values(groupedData);
const minTemp = Math.min(...chartData.map(d => d.minTemp));
const maxTemp = Math.max(...chartData.map(d => d.maxTemp));
return { chartData, minTemp, maxTemp };
}, [result]);
const chartConfig: ChartConfig = useMemo(() => ({
minTemp: {
label: "Min Temp.",
color: "hsl(var(--chart-1))",
},
maxTemp: {
label: "Max Temp.",
color: "hsl(var(--chart-2))",
},
}), []);
return (
<Card className="my-4 shadow-none bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700">
<CardHeader>
<CardTitle className="text-neutral-800 dark:text-neutral-100">Weather Forecast for {result.city.name}</CardTitle>
<CardDescription className="text-neutral-600 dark:text-neutral-400">
Showing min and max temperatures for the next 5 days
</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig}>
<ResponsiveContainer width="100%" height={300}>
<LineChart
data={chartData}
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
<XAxis
dataKey="date"
tickFormatter={(value) => new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}
stroke="#9CA3AF"
/>
<YAxis
domain={[Math.floor(minTemp) - 2, Math.ceil(maxTemp) + 2]}
tickFormatter={(value) => `${value}°C`}
stroke="#9CA3AF"
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Line
type="monotone"
dataKey="minTemp"
stroke="var(--color-minTemp)"
strokeWidth={2}
dot={false}
name="Min Temp."
/>
<Line
type="monotone"
dataKey="maxTemp"
stroke="var(--color-maxTemp)"
strokeWidth={2}
dot={false}
name="Max Temp."
/>
</LineChart>
</ResponsiveContainer>
</ChartContainer>
</CardContent>
<CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center gap-2 font-medium leading-none text-neutral-800 dark:text-neutral-100">
{result.city.name}, {result.city.country}
</div>
<div className="flex items-center gap-2 leading-none text-neutral-600 dark:text-neutral-400">
Next 5 days forecast
</div>
</div>
</div>
</CardFooter>
</Card>
);
});
WeatherChart.displayName = 'WeatherChart';
export default WeatherChart;

View File

@ -9,9 +9,11 @@
"lint": "next lint"
},
"dependencies": {
"@ai-sdk/anthropic": "^0.0.51",
"@ai-sdk/azure": "^0.0.31",
"@ai-sdk/cohere": "latest",
"@ai-sdk/google": "^0.0.46",
"@ai-sdk/google": "^0.0.52",
"@ai-sdk/groq": "^0.0.1",
"@ai-sdk/mistral": "^0.0.41",
"@ai-sdk/openai": "^0.0.58",
"@e2b/code-interpreter": "^0.0.8",

View File

@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false
dependencies:
'@ai-sdk/anthropic':
specifier: ^0.0.51
version: 0.0.51(zod@3.23.8)
'@ai-sdk/azure':
specifier: ^0.0.31
version: 0.0.31(zod@3.23.8)
@ -12,8 +15,11 @@ dependencies:
specifier: latest
version: 0.0.25(zod@3.23.8)
'@ai-sdk/google':
specifier: ^0.0.46
version: 0.0.46(zod@3.23.8)
specifier: ^0.0.52
version: 0.0.52(zod@3.23.8)
'@ai-sdk/groq':
specifier: ^0.0.1
version: 0.0.1(zod@3.23.8)
'@ai-sdk/mistral':
specifier: ^0.0.41
version: 0.0.41(zod@3.23.8)
@ -91,7 +97,7 @@ dependencies:
version: 1.4.2
ai:
specifier: latest
version: 3.4.9(openai@4.67.2)(react@18.3.1)(svelte@4.2.19)(vue@3.5.11)(zod@3.23.8)
version: 3.4.18(openai@4.67.2)(react@18.3.1)(svelte@4.2.19)(vue@3.5.11)(zod@3.23.8)
anthropic-vertex-ai:
specifier: ^1.0.0
version: 1.0.0(zod@3.23.8)
@ -226,6 +232,17 @@ devDependencies:
packages:
/@ai-sdk/anthropic@0.0.51(zod@3.23.8):
resolution: {integrity: sha512-XPLBvdwdMlNAvGMyfsDgrCDXN2Wz7M+wfCJthqiwdiKHmq2jDLGdt0ZCAozgxxW28HVzMfJlFjuyECiA5Le3YA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
dependencies:
'@ai-sdk/provider': 0.0.24
'@ai-sdk/provider-utils': 1.0.20(zod@3.23.8)
zod: 3.23.8
dev: false
/@ai-sdk/azure@0.0.31(zod@3.23.8):
resolution: {integrity: sha512-LTiv890qHcw3w87l+OOuYqW1HM9+7olS5mpSOriRY2uZxJWr3MGz8MYqJu2jGNajNKi4j64GsaOuNK69k8KXjw==}
engines: {node: '>=18'}
@ -249,18 +266,29 @@ packages:
zod: 3.23.8
dev: false
/@ai-sdk/google@0.0.46(zod@3.23.8):
resolution: {integrity: sha512-rsc3Wh54EfSt3l/7IqPdTeuxA7xvFk2p8/HxxyoHfcwvQYmQ/bpgxmadId862sVsK79L8k3iRxvVwGVkkaEeaA==}
/@ai-sdk/google@0.0.52(zod@3.23.8):
resolution: {integrity: sha512-bfsA/1Ae0SQ6NfLwWKs5SU4MBwlzJjVhK6bTVBicYFjUxg9liK/W76P1Tq/qK9OlrODACz3i1STOIWsFPpIOuQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
dependencies:
'@ai-sdk/provider': 0.0.22
'@ai-sdk/provider-utils': 1.0.17(zod@3.23.8)
'@ai-sdk/provider': 0.0.24
'@ai-sdk/provider-utils': 1.0.20(zod@3.23.8)
json-schema: 0.4.0
zod: 3.23.8
dev: false
/@ai-sdk/groq@0.0.1(zod@3.23.8):
resolution: {integrity: sha512-M8XHUovs2UqOx6xlhABXXCGlzbgeErSyIwvH1LQeDl3Z2CbSSgvttc0k6irm4J7ViuULE5XcIDQurXijIePWqQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
dependencies:
'@ai-sdk/provider': 0.0.24
'@ai-sdk/provider-utils': 1.0.20(zod@3.23.8)
zod: 3.23.8
dev: false
/@ai-sdk/mistral@0.0.41(zod@3.23.8):
resolution: {integrity: sha512-UTVtdC61AF4KQWnM3VAoo6/gi7G1frL3qVlKyW5toiRAUjCdeqLJUF2ho2iO8yqf+qIT6j57jWT3o6pqREy3Wg==}
engines: {node: '>=18'}
@ -402,8 +430,8 @@ packages:
json-schema: 0.4.0
dev: false
/@ai-sdk/react@0.0.62(react@18.3.1)(zod@3.23.8):
resolution: {integrity: sha512-1asDpxgmeHWL0/EZPCLENxfOHT+0jce0z/zasRhascodm2S6f6/KZn5doLG9jdmarcb+GjMjFmmwyOVXz3W1xg==}
/@ai-sdk/react@0.0.64(react@18.3.1)(zod@3.23.8):
resolution: {integrity: sha512-4LN2vleyA6rYHZ4Rk9CdxnJgaVkNPJDD4Wx1brUhc5RvUxj3TODcm2UwGOR/mxv4pcydtZGELfQQs/i/tkAUCw==}
engines: {node: '>=18'}
peerDependencies:
react: ^18 || ^19
@ -421,8 +449,8 @@ packages:
zod: 3.23.8
dev: false
/@ai-sdk/solid@0.0.49(zod@3.23.8):
resolution: {integrity: sha512-KnfWTt640cS1hM2fFIba8KHSPLpOIWXtEm28pNCHTvqasVKlh2y/zMQANTwE18pF2nuXL9P9F5/dKWaPsaEzQw==}
/@ai-sdk/solid@0.0.50(zod@3.23.8):
resolution: {integrity: sha512-JF+KKOgGAgcROgae6FU+hAtxMRhR896SzwI3H1h5hFOZrjqYeYzemJoKzA5MR5IBnPSK4FzEjunc8G5L67TyzQ==}
engines: {node: '>=18'}
peerDependencies:
solid-js: ^1.7.7
@ -436,8 +464,8 @@ packages:
- zod
dev: false
/@ai-sdk/svelte@0.0.51(svelte@4.2.19)(zod@3.23.8):
resolution: {integrity: sha512-aIZJaIds+KpCt19yUDCRDWebzF/17GCY7gN9KkcA2QM6IKRO5UmMcqEYja0ZmwFQPm1kBZkF2njhr8VXis2mAw==}
/@ai-sdk/svelte@0.0.52(svelte@4.2.19)(zod@3.23.8):
resolution: {integrity: sha512-ZGd81ruVuqpOh1Suma+HwBMBywcOV0IUzi96Q3knIoZIz99sVwebSKH8ExMofXm49bQdCTRa73Wn8sTs6QDIYg==}
engines: {node: '>=18'}
peerDependencies:
svelte: ^3.0.0 || ^4.0.0
@ -470,8 +498,8 @@ packages:
zod-to-json-schema: 3.23.2(zod@3.23.8)
dev: false
/@ai-sdk/vue@0.0.54(vue@3.5.11)(zod@3.23.8):
resolution: {integrity: sha512-Ltu6gbuii8Qlp3gg7zdwdnHdS4M8nqKDij2VVO1223VOtIFwORFJzKqpfx44U11FW8z2TPVBYN+FjkyVIcN2hg==}
/@ai-sdk/vue@0.0.55(vue@3.5.11)(zod@3.23.8):
resolution: {integrity: sha512-NZ89CeRPO3D9GjI7GmK3vC+YXjsaWi3iCIvxlGqfQYt0JFKcjgM6dfeq8Nkk+qWI9OoxoOhV/yQdqWQKPv3RRg==}
engines: {node: '>=18'}
peerDependencies:
vue: ^3.3.4
@ -2021,8 +2049,8 @@ packages:
humanize-ms: 1.2.1
dev: false
/ai@3.4.9(openai@4.67.2)(react@18.3.1)(svelte@4.2.19)(vue@3.5.11)(zod@3.23.8):
resolution: {integrity: sha512-wmVzpIHNGjCEjIJ/3945a/DIkz+gwObjC767ZRgO8AmtIZMO5KqvqNr7n2KF+gQrCPCMC8fM1ICQFXSvBZnBlA==}
/ai@3.4.18(openai@4.67.2)(react@18.3.1)(svelte@4.2.19)(vue@3.5.11)(zod@3.23.8):
resolution: {integrity: sha512-dc6rSBDgaRMX4VgTBsUZwEN5tBWMpJd+MJxB05E2cL4ft9mOmQEZNS6yeu4Ci5rUDj4ZhnmvANHrP7td8Ko9Og==}
engines: {node: '>=18'}
peerDependencies:
openai: ^4.42.0
@ -2044,11 +2072,11 @@ packages:
dependencies:
'@ai-sdk/provider': 0.0.24
'@ai-sdk/provider-utils': 1.0.20(zod@3.23.8)
'@ai-sdk/react': 0.0.62(react@18.3.1)(zod@3.23.8)
'@ai-sdk/solid': 0.0.49(zod@3.23.8)
'@ai-sdk/svelte': 0.0.51(svelte@4.2.19)(zod@3.23.8)
'@ai-sdk/react': 0.0.64(react@18.3.1)(zod@3.23.8)
'@ai-sdk/solid': 0.0.50(zod@3.23.8)
'@ai-sdk/svelte': 0.0.52(svelte@4.2.19)(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.46(zod@3.23.8)
'@ai-sdk/vue': 0.0.54(vue@3.5.11)(zod@3.23.8)
'@ai-sdk/vue': 0.0.55(vue@3.5.11)(zod@3.23.8)
'@opentelemetry/api': 1.9.0
eventsource-parser: 1.1.2
json-schema: 0.4.0