From a7cf16c6b47ec438d225e2809a44036f54e63ebb Mon Sep 17 00:00:00 2001 From: Taylor Lindores Reeves Date: Tue, 20 Aug 2024 09:43:16 +0100 Subject: [PATCH 1/2] add type safe env vars --- bun.lockb | Bin 0 -> 270842 bytes env.mjs | 37 +++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 38 insertions(+) create mode 100755 bun.lockb create mode 100644 env.mjs diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..0501c6c80a133dcaf78fd2f933b105c08d8c4250 GIT binary patch literal 270842 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

WdZhd0X)wHjdbjijC8oF@c+# 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 Date: Tue, 20 Aug 2024 09:44:55 +0100 Subject: [PATCH 2/2] use type safe env vars --- app/actions.ts | 53 +- app/api/chat/route.ts | 694 +++++----- app/page.tsx | 2892 ++++++++++++++++++++++------------------- 3 files changed, 1942 insertions(+), 1697 deletions(-) diff --git a/app/actions.ts b/app/actions.ts index df5f11d..f603edd 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -1,44 +1,43 @@ -'use server'; +"use server"; -import { generateObject } from 'ai'; -import { createOpenAI as createGroq } from '@ai-sdk/openai'; -import { z } from 'zod'; +import { env } from "@/env.mjs"; +import { createOpenAI as createGroq } from "@ai-sdk/openai"; +import { generateObject } from "ai"; +import { z } from "zod"; export interface Message { - role: 'user' | 'assistant'; - content: string; + role: "user" | "assistant"; + content: string; } const groq = createGroq({ - baseURL: 'https://api.groq.com/openai/v1', - apiKey: process.env.GROQ_API_KEY, + baseURL: "https://api.groq.com/openai/v1", + apiKey: env.GROQ_API_KEY, }); export async function suggestQuestions(history: Message[]) { - 'use server'; + "use server"; - const { object } = await generateObject({ - model: groq('llama-3.1-70b-versatile'), - temperature: 0, - system: - `You are a search engine query generator. You 'have' to create 3 questions for the search engine based on the message history which has been provided to you. -The questions should be open-ended and should encourage further discussion while maintaining the whole context. Limit it to 5-10 words per question. + const { object } = await generateObject({ + model: groq("llama-3.1-70b-versatile"), + temperature: 0, + system: `You are a search engine query generator. You 'have' to create 3 questions for the search engine based on the message history which has been provided to you. +The questions should be open-ended and should encourage further discussion while maintaining the whole context. Limit it to 5-10 words per question. Always put the user input's context is some way so that the next search knows what to search for exactly. Try to stick to the context of the conversation and avoid asking questions that are too general or too specific. For weather based converations sent to you, always generate questions that are about news, sports, or other topics that are not related to the weather. For programming based conversations, always generate questions that are about the algorithms, data structures, or other topics that are related to it or an improvement of the question. For location based conversations, always generate questions that are about the culture, history, or other topics that are related to the location. Never use pronouns in the questions as they blur the context.`, - messages: history, - schema: z.object({ - questions: z.array( - z.string() - ) - .describe('The generated questions based on the message history.') - }), - }); + messages: history, + schema: z.object({ + questions: z + .array(z.string()) + .describe("The generated questions based on the message history."), + }), + }); - return { - questions: object.questions - }; -} \ No newline at end of file + return { + questions: object.questions, + }; +} diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 78b34da..74716a2 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,28 +1,29 @@ -import { openai } from '@ai-sdk/openai' -import { convertToCoreMessages, streamText, tool } from "ai"; +import { env } from "@/env.mjs"; +import { openai } from "@ai-sdk/openai"; import { CodeInterpreter } from "@e2b/code-interpreter"; -import { z } from "zod"; import { geolocation } from "@vercel/functions"; +import { convertToCoreMessages, streamText, tool } from "ai"; +import { z } from "zod"; // Allow streaming responses up to 30 seconds export const maxDuration = 60; export async function POST(req: Request) { - const { messages } = await req.json(); - const { latitude, longitude, city } = geolocation(req) - - const result = await streamText({ - model: openai("gpt-4o-mini"), - messages: convertToCoreMessages(messages), - temperature: 0, - maxTokens: 800, - system: ` + const { messages } = await req.json(); + const { latitude, longitude, city } = geolocation(req); + + const result = await streamText({ + model: openai("gpt-4o-mini"), + messages: convertToCoreMessages(messages), + temperature: 0, + maxTokens: 800, + 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!! Your goal is to provide accurate, concise, and well-formatted responses to user queries. 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!!!!! -The current date is ${new Date().toLocaleDateString("en-US", { year: "numeric", month: "short", day: "2-digit", weekday: "short" })}. +The current date is ${new Date().toLocaleDateString("en-US", { year: "numeric", month: "short", day: "2-digit", weekday: "short" })}. The user is located in ${city}(${latitude}, ${longitude}). Here are the tools available to you: @@ -42,7 +43,7 @@ Here is the general guideline per tool to follow when responding to user queries - Show plots from the programming tool using plt.show() function. The tool will automatically capture the plot and display it in the response. - If asked for multiple plots, make it happen in one run of the tool. The tool will automatically capture the plots and display them in the response. - The location search tools return images in the response, please do not include them in the response at all costs. -- Never write a base64 image in the response at all costs. +- Never write a base64 image in the response at all costs. - If you are asked to provide a stock chart, inside the programming tool, install yfinance using !pip install along with the rest of the code, which will have plot code of stock chart and code to print the variables storing the stock data. Then, compose your response based on the output of the code execution. - Never run web_search tool for stock chart queries at all costs. @@ -58,309 +59,368 @@ DO NOT write any kind of html sort of tags(<>) or lists in the response at AL Format your response in paragraphs(min 4) with 3-6 sentences each, keeping it brief but informative. DO NOT use pointers or make lists of any kind at ALL! Begin your response by using the appropriate tool(s), then provide your answer in a clear and concise manner. -Never respond to user before running any tool like -- saying 'Certainly! Let me blah blah blah' -- or 'To provide you with the best answer, I will blah blah blah' +Never respond to user before running any tool like +- saying 'Certainly! Let me blah blah blah' +- or 'To provide you with the best answer, I will blah blah blah' - or that 'Based on search results, I think blah blah blah' at ALL COSTS!! Just run the tool and provide the answer.`, - tools: { - web_search: tool({ - description: - "Search the web for information with the given query, max results and search depth.", - parameters: z.object({ - query: z.string().describe("The search query to look up on the web."), - maxResults: z - .number() - .describe( - "The maximum number of results to return. Default to be used is 10.", - ), - topic: z - .enum(["general", "news"]) - .describe("The topic type to search for. Default is general."), - searchDepth: z - .enum(["basic", "advanced"]) - .describe( - "The search depth to use for the search. Default is basic.", - ), - exclude_domains: z - .array(z.string()) - .optional() - .describe( - "A list of domains to specifically exclude from the search results. Default is None, which doesn't exclude any domains.", - ), - }), - execute: async ({ - query, - maxResults, - topic, - searchDepth, - exclude_domains, - }: { - query: string; - maxResults: number; - topic: "general" | "news"; - searchDepth: "basic" | "advanced"; - exclude_domains?: string[]; - }) => { - const apiKey = process.env.TAVILY_API_KEY; - - let body = JSON.stringify({ - api_key: apiKey, - query, - topic: topic, - max_results: maxResults < 5 ? 5 : maxResults, - search_depth: searchDepth, - include_answers: true, - exclude_domains: exclude_domains, - }); - - if (topic === "news") { - body = JSON.stringify({ - api_key: apiKey, - query, - topic: topic, - days: 7, - max_results: maxResults < 5 ? 5 : maxResults, - search_depth: searchDepth, - include_answers: true, - exclude_domains: exclude_domains, - }); - } - - const response = await fetch("https://api.tavily.com/search", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body, - }); - - const data = await response.json(); - - let context = data.results.map( - (obj: { url: any; content: any; title: any; raw_content: any, published_date: any }) => { - if (topic === "news") { - return { - url: obj.url, - title: obj.title, - content: obj.content, - raw_content: obj.raw_content, - published_date: obj.published_date, - }; - } - return { - url: obj.url, - title: obj.title, - content: obj.content, - raw_content: obj.raw_content, - }; - }, - ); - - return { - results: context, - }; - }, - }), - retrieve: tool({ - description: "Retrieve the information from a URL.", - parameters: z.object({ - url: z.string().describe("The URL to retrieve the information from."), - }), - execute: async ({ url }: { url: string }) => { - let hasError = false; - - let results; - try { - const response = await fetch(`https://r.jina.ai/${url}`, { - method: "GET", - headers: { - Accept: "application/json", - "X-With-Generated-Alt": "true", - }, - }); - const json = await response.json(); - if (!json.data || json.data.length === 0) { - hasError = true; - } else { - // Limit the content to 5000 characters - if (json.data.content.length > 5000) { - json.data.content = json.data.content.slice(0, 5000); - } - results = { - results: [ - { - title: json.data.title, - content: json.data.content, - url: json.data.url, - }, - ], - query: "", - images: [], - }; - } - } catch (error) { - hasError = true; - console.error("Retrieve API error:", error); - } - - if (hasError || !results) { - return results; - } - - return results; - }, - }), - get_weather_data: tool({ - description: "Get the weather data for the given coordinates.", - parameters: z.object({ - lat: z.number().describe("The latitude of the location."), - lon: z.number().describe("The longitude of the location."), - }), - execute: async ({ lat, lon }: { lat: number; lon: number }) => { - const apiKey = process.env.OPENWEATHER_API_KEY; - const response = await fetch( - `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}`, - ); - const data = await response.json(); - return data; - }, - }), - programming: tool({ - description: "Write and execute Python code.", - parameters: z.object({ - code: z.string().describe("The Python code to execute."), - }), - execute: async ({ code }: { code: string }) => { - const sandbox = await CodeInterpreter.create(); - const execution = await sandbox.notebook.execCell(code); - let message = ""; - let images = []; - - if (execution.results.length > 0) { - for (const result of execution.results) { - if (result.isMainResult) { - message += `${result.text}\n`; - } else { - message += `${result.text}\n`; - } - if (result.formats().length > 0) { - const formats = result.formats(); - for (let format of formats) { - if (format === "png") { - images.push({ format: "png", data: result.png }); - } else if (format === "jpeg") { - images.push({ format: "jpeg", data: result.jpeg }); - } else if (format === "svg") { - images.push({ format: "svg", data: result.svg }); - } - } - } - } - } - - if (execution.logs.stdout.length > 0 || execution.logs.stderr.length > 0) { - if (execution.logs.stdout.length > 0) { - message += `${execution.logs.stdout.join("\n")}\n`; - } - if (execution.logs.stderr.length > 0) { - message += `${execution.logs.stderr.join("\n")}\n`; - } - } - - sandbox.close(); - return { message: message.trim(), images }; - }, - }), - 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, - }; - }, - }), - find_place: tool({ - description: "Find a specific place using Google Maps API.", - parameters: z.object({ - input: z.string().describe("The place to search for (e.g., 'Museum of Contemporary Art Australia')."), - inputtype: z.enum(["textquery", "phonenumber"]).describe("The type of input (textquery or phonenumber)."), - }), - execute: async ({ input, inputtype }: { input: string; inputtype: "textquery" | "phonenumber" }) => { - const apiKey = process.env.GOOGLE_MAPS_API_KEY; - const url = `https://maps.googleapis.com/maps/api/place/findplacefromtext/json?fields=formatted_address,name,rating,opening_hours,geometry&input=${encodeURIComponent(input)}&inputtype=${inputtype}&key=${apiKey}`; - - const response = await fetch(url); - const data = await response.json(); - - return data; - }, - }), - text_search: tool({ - description: "Perform a text-based search for places using Google Maps API.", - parameters: z.object({ - query: z.string().describe("The search query (e.g., '123 main street')."), - location: z.string().optional().describe("The location to center the search (e.g., '42.3675294,-71.186966')."), - radius: z.number().optional().describe("The radius of the search area in meters (max 50000)."), - }), - execute: async ({ query, location, radius }: { query: string; location?: string; radius?: number }) => { - const apiKey = process.env.GOOGLE_MAPS_API_KEY; - let url = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=${encodeURIComponent(query)}&key=${apiKey}`; - - if (location) { - url += `&location=${encodeURIComponent(location)}`; - } - if (radius) { - url += `&radius=${radius}`; - } - - const response = await fetch(url); - const data = await response.json(); - - return data; - }, - }), - }, - toolChoice: "auto", - }); - - return result.toAIStreamResponse(); + tools: { + web_search: tool({ + description: + "Search the web for information with the given query, max results and search depth.", + parameters: z.object({ + query: z.string().describe("The search query to look up on the web."), + maxResults: z + .number() + .describe( + "The maximum number of results to return. Default to be used is 10.", + ), + topic: z + .enum(["general", "news"]) + .describe("The topic type to search for. Default is general."), + searchDepth: z + .enum(["basic", "advanced"]) + .describe( + "The search depth to use for the search. Default is basic.", + ), + exclude_domains: z + .array(z.string()) + .optional() + .describe( + "A list of domains to specifically exclude from the search results. Default is None, which doesn't exclude any domains.", + ), + }), + execute: async ({ + query, + maxResults, + topic, + searchDepth, + exclude_domains, + }: { + query: string; + maxResults: number; + topic: "general" | "news"; + searchDepth: "basic" | "advanced"; + exclude_domains?: string[]; + }) => { + const apiKey = env.TAVILY_API_KEY; + + let body = JSON.stringify({ + api_key: apiKey, + query, + topic: topic, + max_results: maxResults < 5 ? 5 : maxResults, + search_depth: searchDepth, + include_answers: true, + exclude_domains: exclude_domains, + }); + + if (topic === "news") { + body = JSON.stringify({ + api_key: apiKey, + query, + topic: topic, + days: 7, + max_results: maxResults < 5 ? 5 : maxResults, + search_depth: searchDepth, + include_answers: true, + exclude_domains: exclude_domains, + }); + } + + const response = await fetch("https://api.tavily.com/search", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body, + }); + + const data = await response.json(); + + const context = data.results.map( + (obj: { + url: any; + content: any; + title: any; + raw_content: any; + published_date: any; + }) => { + if (topic === "news") { + return { + url: obj.url, + title: obj.title, + content: obj.content, + raw_content: obj.raw_content, + published_date: obj.published_date, + }; + } + return { + url: obj.url, + title: obj.title, + content: obj.content, + raw_content: obj.raw_content, + }; + }, + ); + + return { + results: context, + }; + }, + }), + retrieve: tool({ + description: "Retrieve the information from a URL.", + parameters: z.object({ + url: z.string().describe("The URL to retrieve the information from."), + }), + execute: async ({ url }: { url: string }) => { + let hasError = false; + + let results; + try { + const response = await fetch(`https://r.jina.ai/${url}`, { + method: "GET", + headers: { + Accept: "application/json", + "X-With-Generated-Alt": "true", + }, + }); + const json = await response.json(); + if (!json.data || json.data.length === 0) { + hasError = true; + } else { + // Limit the content to 5000 characters + if (json.data.content.length > 5000) { + json.data.content = json.data.content.slice(0, 5000); + } + results = { + results: [ + { + title: json.data.title, + content: json.data.content, + url: json.data.url, + }, + ], + query: "", + images: [], + }; + } + } catch (error) { + hasError = true; + console.error("Retrieve API error:", error); + } + + if (hasError || !results) { + return results; + } + + return results; + }, + }), + get_weather_data: tool({ + description: "Get the weather data for the given coordinates.", + parameters: z.object({ + lat: z.number().describe("The latitude of the location."), + lon: z.number().describe("The longitude of the location."), + }), + execute: async ({ lat, lon }: { lat: number; lon: number }) => { + const apiKey = env.OPENWEATHER_API_KEY; + const response = await fetch( + `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}`, + ); + const data = await response.json(); + return data; + }, + }), + programming: tool({ + description: "Write and execute Python code.", + parameters: z.object({ + code: z.string().describe("The Python code to execute."), + }), + execute: async ({ code }: { code: string }) => { + const sandbox = await CodeInterpreter.create(); + const execution = await sandbox.notebook.execCell(code); + let message = ""; + const images = []; + + if (execution.results.length > 0) { + for (const result of execution.results) { + if (result.isMainResult) { + message += `${result.text}\n`; + } else { + message += `${result.text}\n`; + } + if (result.formats().length > 0) { + const formats = result.formats(); + for (const format of formats) { + if (format === "png") { + images.push({ format: "png", data: result.png }); + } else if (format === "jpeg") { + images.push({ format: "jpeg", data: result.jpeg }); + } else if (format === "svg") { + images.push({ format: "svg", data: result.svg }); + } + } + } + } + } + + if ( + execution.logs.stdout.length > 0 || + execution.logs.stderr.length > 0 + ) { + if (execution.logs.stdout.length > 0) { + message += `${execution.logs.stdout.join("\n")}\n`; + } + if (execution.logs.stderr.length > 0) { + message += `${execution.logs.stderr.join("\n")}\n`; + } + } + + sandbox.close(); + return { message: message.trim(), images }; + }, + }), + 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 = 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, + }; + }, + }), + find_place: tool({ + description: "Find a specific place using Google Maps API.", + parameters: z.object({ + input: z + .string() + .describe( + "The place to search for (e.g., 'Museum of Contemporary Art Australia').", + ), + inputtype: z + .enum(["textquery", "phonenumber"]) + .describe("The type of input (textquery or phonenumber)."), + }), + execute: async ({ + input, + inputtype, + }: { input: string; inputtype: "textquery" | "phonenumber" }) => { + const apiKey = env.GOOGLE_MAPS_API_KEY; + const url = `https://maps.googleapis.com/maps/api/place/findplacefromtext/json?fields=formatted_address,name,rating,opening_hours,geometry&input=${encodeURIComponent(input)}&inputtype=${inputtype}&key=${apiKey}`; + + const response = await fetch(url); + const data = await response.json(); + + return data; + }, + }), + text_search: tool({ + description: + "Perform a text-based search for places using Google Maps API.", + parameters: z.object({ + query: z + .string() + .describe("The search query (e.g., '123 main street')."), + location: z + .string() + .optional() + .describe( + "The location to center the search (e.g., '42.3675294,-71.186966').", + ), + radius: z + .number() + .optional() + .describe("The radius of the search area in meters (max 50000)."), + }), + execute: async ({ + query, + location, + radius, + }: { query: string; location?: string; radius?: number }) => { + const apiKey = env.GOOGLE_MAPS_API_KEY; + let url = `https://maps.googleapis.com/maps/api/place/textsearch/json?query=${encodeURIComponent(query)}&key=${apiKey}`; + + if (location) { + url += `&location=${encodeURIComponent(location)}`; + } + if (radius) { + url += `&radius=${radius}`; + } + + const response = await fetch(url); + const data = await response.json(); + + return data; + }, + }), + }, + toolChoice: "auto", + }); + + return result.toAIStreamResponse(); } diff --git a/app/page.tsx b/app/page.tsx index 45e5bb7..86b6f63 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,1372 +1,1558 @@ /* eslint-disable @next/next/no-img-element */ "use client"; -import -React, -{ - useRef, - useCallback, - useState, - useEffect, - useMemo, - memo -} from 'react'; -import ReactMarkdown, { Components } from 'react-markdown'; -import { useRouter } from 'next/navigation'; -import remarkGfm from 'remark-gfm'; -import { useChat } from 'ai/react'; -import { ToolInvocation } from 'ai'; -import { toast } from 'sonner'; -import { motion, AnimatePresence } from 'framer-motion'; -import Image from 'next/image'; -import { suggestQuestions, Message } from './actions'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { - SearchIcon, - Sparkles, - ArrowRight, - Globe, - AlignLeft, - Newspaper, - Copy, - Cloud, - Code, - Check, - Loader2, - User2, - Edit2, - Heart, - X, - MapPin, - Star, - Plus, - Terminal, - ImageIcon, - Download, -} from 'lucide-react'; -import { - HoverCard, - HoverCardContent, - HoverCardTrigger, + HoverCard, + HoverCardContent, + HoverCardTrigger, } from "@/components/ui/hover-card"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import type { ToolInvocation } from "ai"; +import { useChat } from "ai/react"; +import { AnimatePresence, motion } from "framer-motion"; +import { + AlignLeft, + ArrowRight, + Check, + Cloud, + Code, + Copy, + Download, + Edit2, + Globe, + Heart, + Loader2, + MapPin, + Newspaper, + Plus, + SearchIcon, + Sparkles, + Star, + User2, + X, +} from "lucide-react"; +import Image from "next/image"; +import { useRouter } from "next/navigation"; +import React, { + memo, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import ReactMarkdown, { type Components } from "react-markdown"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { oneLight } from "react-syntax-highlighter/dist/esm/styles/prism"; +import remarkGfm from "remark-gfm"; +import { toast } from "sonner"; +import { type Message, suggestQuestions } from "./actions"; import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, } from "@/components/ui/accordion"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; 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'; -import { Line, LineChart, CartesianGrid, XAxis, YAxis, ResponsiveContainer } from "recharts"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { - ChartConfig, - ChartContainer, - ChartTooltip, - ChartTooltipContent, + type ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, } from "@/components/ui/chart"; -import { GitHubLogoIcon } from '@radix-ui/react-icons'; -import { Skeleton } from '@/components/ui/skeleton'; -import Link from 'next/link'; -import { cn } from '@/lib/utils'; +import { Input } from "@/components/ui/input"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { env } from "@/env.mjs"; +import { GitHubLogoIcon } from "@radix-ui/react-icons"; +import Link from "next/link"; +import { + CartesianGrid, + Line, + LineChart, + ResponsiveContainer, + XAxis, + YAxis, +} from "recharts"; export const maxDuration = 60; declare global { - interface Window { - google: any; - initMap: () => void; - } + interface Window { + google: any; + initMap: () => void; + } } export default function Home() { - const router = useRouter(); - const inputRef = useRef(null); - const [lastSubmittedQuery, setLastSubmittedQuery] = useState(""); - const [hasSubmitted, setHasSubmitted] = useState(false); - const [isAnimating, setIsAnimating] = useState(false); - const bottomRef = useRef(null); - const [suggestedQuestions, setSuggestedQuestions] = useState([]); - const [showExamples, setShowExamples] = useState(false) - const [isEditingQuery, setIsEditingQuery] = useState(false); - - const { isLoading, input, messages, setInput, append, handleSubmit, setMessages } = useChat({ - api: '/api/chat', - maxToolRoundtrips: 1, - onFinish: async (message, { finishReason }) => { - if (finishReason === 'stop') { - const newHistory: Message[] = [{ role: "user", content: lastSubmittedQuery, }, { role: "assistant", content: message.content }]; - const { questions } = await suggestQuestions(newHistory); - setSuggestedQuestions(questions); - } - setIsAnimating(false); - }, - onError: (error) => { - console.error("Chat error:", error); - toast.error("An error occurred.", { - description: "We must have ran out of credits. Sponsor us on GitHub to keep this service running.", - action: { - label: "Sponsor", - onClick: () => window.open("https://git.new/mplx", "_blank"), - }, - }); - }, - }); - - const CopyButton = ({ text }: { text: string }) => { - const [isCopied, setIsCopied] = useState(false); - - return ( - - ); - }; - - // 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 ( - - - Weather Forecast for {result.city.name} - - Showing min and max temperatures for the next 5 days - - - - - - - - new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })} - /> - `${value}°C`} - /> - } /> - - - - - - - -

-
-
- {result.city.name}, {result.city.country} -
-
- Next 5 days forecast -
-
-
- - - ); - }); - - WeatherChart.displayName = 'WeatherChart'; - - - // Google Maps components - - 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(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(async () => { - if (mapRef.current && isValidCoordinate(memoizedCenter.lat) && isValidCoordinate(memoizedCenter.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: memoizedCenter, - zoom: 14, - mapId: "347ff92e0c7225cf", - }); - } else { - googleMapRef.current.setCenter(memoizedCenter); - } - - // Clear existing markers - markersRef.current.forEach(marker => marker.map = null); - markersRef.current = []; - - memoizedPlaces.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'); - } - }, [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.map = 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 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 ( -
- -
- ); - }); - - 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 ( - - - - - {place.name} - - - - -
-

Address: {place.formatted_address}

- {place.rating && ( -
- Rating: - - - {place.rating} - -
- )} - {place.opening_hours && ( -

Open now: {place.opening_hours.open_now ? 'Yes' : 'No'}

- )} -
-
-
- ); - }); - - FindPlaceResult.displayName = 'FindPlaceResult'; - - const TextSearchResult = memo(({ result }: { result: any }) => { - const centerLocation = result.results[0]?.geometry?.location; - const mapLocation = centerLocation ? `${centerLocation.lat},${centerLocation.lng}` : ''; - - return ( - - - - - Text Search Results - - - - {mapLocation && } - - - Place Details - -
- {result.results.map((place: any, index: number) => ( -
-
-

{place.name}

-

- {place.formatted_address} -

-
- {place.rating && ( - - - {place.rating} ({place.user_ratings_total}) - - )} -
- ))} -
-
-
-
-
-
- ); - }); - - TextSearchResult.displayName = 'TextSearchResult'; - - - 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 === 'find_place') { - if (!result) { - return ( -
-
- - Finding place... -
- - {[0, 1, 2].map((index) => ( - - ))} - -
- ); - } - - return ; - } - - if (toolInvocation.toolName === 'text_search') { - if (!result) { - return ( -
-
- - Searching places... -
- - {[0, 1, 2].map((index) => ( - - ))} - -
- ); - } - - return ; - } - - if (toolInvocation.toolName === 'get_weather_data') { - if (!result) { - return ( -
-
- - Fetching weather data... -
-
- {[0, 1, 2].map((index) => ( - - ))} -
-
- ); - } - - if (isLoading) { - return ( - - - - - -
- - - ); - } - - return ; - } - - if (toolInvocation.toolName === 'programming') { - return ( -
-
- - Programming -
- - - - Code - - - Output - - {result?.images && result.images.length > 0 && ( - - Images - - )} - - -
- - {args.code} - -
- -
-
-
- -
- {result ? ( - <> -
-                      {result.message}
-                    
-
- -
- - ) : ( -
-
- - Executing code... -
-
- )} -
-
- {result?.images && result.images.length > 0 && ( - -
- {result.images.map((img: { format: 'png' | 'jpeg' | 'svg', data: string }, imgIndex: number) => ( -
-
-

Image {imgIndex + 1}

- -
-
- {`Generated -
-
- ))} -
-
- )} -
-
- ); - } - - 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 ? ( -
-
- - Running a search... -
-
- {[0, 1, 2].map((index) => ( - - ))} -
-
- ) : - - - -
-
- -

Sources Found

-
- {result && ( - {result.results.length} results - )} -
-
- - {args?.query && ( - - - {args.query} - - )} - {result && ( -
- {result.results.map((item: any, itemIndex: number) => ( - - - Favicon - {item.title} - - -

{item.content}

-
- -
- ))} -
- )} -
-
-
} -
- ); - }; - - interface CitationComponentProps { - href: string; - children: React.ReactNode; - index: number; - } - - const CitationComponent: React.FC = React.memo(({ href, index }) => { - const faviconUrl = `https://www.google.com/s2/favicons?sz=128&domain=${new URL(href).hostname}`; - - return ( - - - - {index + 1} - - - - Favicon - - {href} - - - - ); - }); - - CitationComponent.displayName = "CitationComponent"; - - interface MarkdownRendererProps { - content: string; - } - - const MarkdownRenderer: React.FC = React.memo(({ content }) => { - const citationLinks = useMemo(() => { - return [...content.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)].map(([_, text, link]) => ({ - text, - link, - })); - }, [content]); - - const components: Partial = useMemo(() => ({ - a: ({ href, children }) => { - if (!href) return null; - const index = citationLinks.findIndex((link) => link.link === href); - return index !== -1 ? ( - - {children} - - ) : ( - - {children} - - ); - }, - }), [citationLinks]); - - return ( - - {content} - - ); - }); - - MarkdownRenderer.displayName = "MarkdownRenderer"; - - - useEffect(() => { - if (bottomRef.current) { - bottomRef.current.scrollIntoView({ behavior: "smooth" }); - } - }, [messages, suggestedQuestions]); - - const handleExampleClick = useCallback(async (query: string) => { - setLastSubmittedQuery(query.trim()); - setHasSubmitted(true); - setSuggestedQuestions([]); - setIsAnimating(true); - await append({ - content: query.trim(), - role: 'user' - }); - }, [append]); - - const handleFormSubmit = useCallback((e: React.FormEvent) => { - e.preventDefault(); - if (input.trim()) { - setMessages([]); - setLastSubmittedQuery(input.trim()); - setHasSubmitted(true); - setIsAnimating(true); - setSuggestedQuestions([]); - handleSubmit(e); - } else { - toast.error("Please enter a search query."); - } - }, [input, setMessages, handleSubmit]); - - const handleSuggestedQuestionClick = useCallback(async (question: string) => { - setMessages([]); - setLastSubmittedQuery(question.trim()); - setHasSubmitted(true); - setSuggestedQuestions([]); - setIsAnimating(true); - await append({ - content: question.trim(), - role: 'user' - }); - }, [append, setMessages]); - - const handleQueryEdit = useCallback(() => { - setIsAnimating(true) - 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", - "What is new with Grok 2.0?", - "Count the number of r's in strawberry", - "Explain Claude 3.5 Sonnet" - ]; - - const Navbar = () => ( -
- - - -
- - - - - - - -

Sponsor this project on GitHub

-
-
-
-
-
- ); - - return ( -
- - -
- {!hasSubmitted && -
-

MiniPerplx

-

- In search for minimalism and simplicity -

-
- } - - {!hasSubmitted && ( - -
-
- setInput(e.target.value)} - disabled={isLoading} - className="w-full min-h-12 py-3 px-4 bg-muted border border-input rounded-full pr-12 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-200 focus-visible:ring-offset-2 text-sm sm:text-base" - onFocus={() => setShowExamples(true)} - onBlur={() => setShowExamples(false)} - /> - -
-
- -
-
-
- {exampleQueries.map((message, index) => ( - - ))} -
-
-
-
- )} -
- - - - {hasSubmitted && ( - setIsAnimating(false)} - > -
- - - - - {isEditingQuery ? ( -
- setInput(e.target.value)} - className="flex-grow" - /> - - -
- ) : ( - - - -

- {lastSubmittedQuery} -

-
- -

{lastSubmittedQuery}

-
-
-
- )} -
- {!isEditingQuery && ( - - - )} -
-
- )} -
- -
- {messages.map((message, index) => ( -
- {message.role === 'assistant' && message.content && ( -
-
-
- -

Answer

-
- -
-
- -
-
- )} - {message.toolInvocations?.map((toolInvocation: ToolInvocation, toolIndex: number) => ( -
- {renderToolInvocation(toolInvocation, toolIndex)} -
- ))} -
- ))} - {suggestedQuestions.length > 0 && ( - -
- -

Suggested questions

-
-
- {suggestedQuestions.map((question, index) => ( - - ))} -
-
- )} -
-
-
- - - {hasSubmitted && !isAnimating && ( - -
-
- setInput(e.target.value)} - disabled={isLoading} - className="w-full min-h-12 py-3 px-4 bg-muted border border-input rounded-full pr-12 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-200 focus-visible:ring-offset-2 text-sm sm:text-base" - /> - -
-
-
- )} -
-
- ); -} \ No newline at end of file + const router = useRouter(); + const inputRef = useRef(null); + const [lastSubmittedQuery, setLastSubmittedQuery] = useState(""); + const [hasSubmitted, setHasSubmitted] = useState(false); + const [isAnimating, setIsAnimating] = useState(false); + const bottomRef = useRef(null); + const [suggestedQuestions, setSuggestedQuestions] = useState([]); + const [showExamples, setShowExamples] = useState(false); + const [isEditingQuery, setIsEditingQuery] = useState(false); + + const { + isLoading, + input, + messages, + setInput, + append, + handleSubmit, + setMessages, + } = useChat({ + api: "/api/chat", + maxToolRoundtrips: 1, + onFinish: async (message, { finishReason }) => { + if (finishReason === "stop") { + const newHistory: Message[] = [ + { role: "user", content: lastSubmittedQuery }, + { role: "assistant", content: message.content }, + ]; + const { questions } = await suggestQuestions(newHistory); + setSuggestedQuestions(questions); + } + setIsAnimating(false); + }, + onError: (error) => { + console.error("Chat error:", error); + toast.error("An error occurred.", { + description: + "We must have ran out of credits. Sponsor us on GitHub to keep this service running.", + action: { + label: "Sponsor", + onClick: () => window.open("https://git.new/mplx", "_blank"), + }, + }); + }, + }); + + const CopyButton = ({ text }: { text: string }) => { + const [isCopied, setIsCopied] = useState(false); + + return ( + + ); + }; + + // 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 ( + + + Weather Forecast for {result.city.name} + + Showing min and max temperatures for the next 5 days + + + + + + + + + new Date(value).toLocaleDateString(undefined, { + month: "short", + day: "numeric", + }) + } + /> + `${value}°C`} + /> + } /> + + + + + + + +
+
+
+ {result.city.name}, {result.city.country} +
+
+ Next 5 days forecast +
+
+
+
+
+ ); + }); + + WeatherChart.displayName = "WeatherChart"; + + // Google Maps components + + 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=${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(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(async () => { + if ( + mapRef.current && + isValidCoordinate(memoizedCenter.lat) && + isValidCoordinate(memoizedCenter.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: memoizedCenter, + zoom: 14, + mapId: "347ff92e0c7225cf", + }); + } else { + googleMapRef.current.setCenter(memoizedCenter); + } + + // Clear existing markers + markersRef.current.forEach((marker) => (marker.map = null)); + markersRef.current = []; + + memoizedPlaces.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"); + } + }, [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.map = 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 MapEmbed = memo( + ({ location, zoom = 15 }: { location: string; zoom?: number }) => { + const apiKey = 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 ( +
+ +
+ ); + }, + ); + + 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 ( + + + + + {place.name} + + + + +
+

+ Address: {place.formatted_address} +

+ {place.rating && ( +
+ Rating: + + + {place.rating} + +
+ )} + {place.opening_hours && ( +

+ Open now:{" "} + {place.opening_hours.open_now ? "Yes" : "No"} +

+ )} +
+
+
+ ); + }); + + FindPlaceResult.displayName = "FindPlaceResult"; + + const TextSearchResult = memo(({ result }: { result: any }) => { + const centerLocation = result.results[0]?.geometry?.location; + const mapLocation = centerLocation + ? `${centerLocation.lat},${centerLocation.lng}` + : ""; + + return ( + + + + + Text Search Results + + + + {mapLocation && } + + + Place Details + +
+ {result.results.map((place: any, index: number) => ( +
+
+

{place.name}

+

+ {place.formatted_address} +

+
+ {place.rating && ( + + + {place.rating} ({place.user_ratings_total}) + + )} +
+ ))} +
+
+
+
+
+
+ ); + }); + + TextSearchResult.displayName = "TextSearchResult"; + + 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 === "find_place") { + if (!result) { + return ( +
+
+ + Finding place... +
+ + {[0, 1, 2].map((index) => ( + + ))} + +
+ ); + } + + return ; + } + + if (toolInvocation.toolName === "text_search") { + if (!result) { + return ( +
+
+ + + Searching places... + +
+ + {[0, 1, 2].map((index) => ( + + ))} + +
+ ); + } + + return ; + } + + if (toolInvocation.toolName === "get_weather_data") { + if (!result) { + return ( +
+
+ + + Fetching weather data... + +
+
+ {[0, 1, 2].map((index) => ( + + ))} +
+
+ ); + } + + if (isLoading) { + return ( + + + + + +
+ + + ); + } + + return ; + } + + if (toolInvocation.toolName === "programming") { + return ( +
+
+ + Programming +
+ + + + Code + + + Output + + {result?.images && result.images.length > 0 && ( + + Images + + )} + + +
+ + {args.code} + +
+ +
+
+
+ +
+ {result ? ( + <> +
+											{result.message}
+										
+
+ +
+ + ) : ( +
+
+ + + Executing code... + +
+
+ )} +
+
+ {result?.images && result.images.length > 0 && ( + +
+ {result.images.map( + ( + img: { format: "png" | "jpeg" | "svg"; data: string }, + imgIndex: number, + ) => ( +
+
+

+ Image {imgIndex + 1} +

+ +
+
+ {`Generated +
+
+ ), + )} +
+
+ )} +
+
+ ); + } + + 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=${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 ? ( +
+
+ + + Running a search... + +
+
+ {[0, 1, 2].map((index) => ( + + ))} +
+
+ ) : ( + + + +
+
+ +

Sources Found

+
+ {result && ( + + {result.results.length} results + + )} +
+
+ + {args?.query && ( + + + {args.query} + + )} + {result && ( +
+ {result.results.map((item: any, itemIndex: number) => ( + + + Favicon + + {item.title} + + + +

+ {item.content} +

+
+ +
+ ))} +
+ )} +
+
+
+ )} +
+ ); + }; + + interface CitationComponentProps { + href: string; + children: React.ReactNode; + index: number; + } + + const CitationComponent: React.FC = React.memo( + ({ href, index }) => { + const faviconUrl = `https://www.google.com/s2/favicons?sz=128&domain=${new URL(href).hostname}`; + + return ( + + + + {index + 1} + + + + Favicon + + {href} + + + + ); + }, + ); + + CitationComponent.displayName = "CitationComponent"; + + interface MarkdownRendererProps { + content: string; + } + + const MarkdownRenderer: React.FC = React.memo( + ({ content }) => { + const citationLinks = useMemo(() => { + return [...content.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)].map( + ([_, text, link]) => ({ + text, + link, + }), + ); + }, [content]); + + const components: Partial = useMemo( + () => ({ + a: ({ href, children }) => { + if (!href) return null; + const index = citationLinks.findIndex((link) => link.link === href); + return index !== -1 ? ( + + {children} + + ) : ( + + {children} + + ); + }, + }), + [citationLinks], + ); + + return ( + + {content} + + ); + }, + ); + + MarkdownRenderer.displayName = "MarkdownRenderer"; + + useEffect(() => { + if (bottomRef.current) { + bottomRef.current.scrollIntoView({ behavior: "smooth" }); + } + }, [messages, suggestedQuestions]); + + const handleExampleClick = useCallback( + async (query: string) => { + setLastSubmittedQuery(query.trim()); + setHasSubmitted(true); + setSuggestedQuestions([]); + setIsAnimating(true); + await append({ + content: query.trim(), + role: "user", + }); + }, + [append], + ); + + const handleFormSubmit = useCallback( + (e: React.FormEvent) => { + e.preventDefault(); + if (input.trim()) { + setMessages([]); + setLastSubmittedQuery(input.trim()); + setHasSubmitted(true); + setIsAnimating(true); + setSuggestedQuestions([]); + handleSubmit(e); + } else { + toast.error("Please enter a search query."); + } + }, + [input, setMessages, handleSubmit], + ); + + const handleSuggestedQuestionClick = useCallback( + async (question: string) => { + setMessages([]); + setLastSubmittedQuery(question.trim()); + setHasSubmitted(true); + setSuggestedQuestions([]); + setIsAnimating(true); + await append({ + content: question.trim(), + role: "user", + }); + }, + [append, setMessages], + ); + + const handleQueryEdit = useCallback(() => { + setIsAnimating(true); + 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", + "What is new with Grok 2.0?", + "Count the number of r's in strawberry", + "Explain Claude 3.5 Sonnet", + ]; + + const Navbar = () => ( +
+ + + +
+ + + + + + + +

Sponsor this project on GitHub

+
+
+
+
+
+ ); + + return ( +
+ + +
+ {!hasSubmitted && ( +
+

+ MiniPerplx +

+

+ In search for minimalism and simplicity +

+
+ )} + + {!hasSubmitted && ( + +
+
+ setInput(e.target.value)} + disabled={isLoading} + className="w-full min-h-12 py-3 px-4 bg-muted border border-input rounded-full pr-12 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-200 focus-visible:ring-offset-2 text-sm sm:text-base" + onFocus={() => setShowExamples(true)} + onBlur={() => setShowExamples(false)} + /> + +
+
+ +
+
+
+ {exampleQueries.map((message, index) => ( + + ))} +
+
+
+
+ )} +
+ + + {hasSubmitted && ( + setIsAnimating(false)} + > +
+ + + + + {isEditingQuery ? ( +
+ setInput(e.target.value)} + className="flex-grow" + /> + + +
+ ) : ( + + + +

+ {lastSubmittedQuery} +

+
+ +

{lastSubmittedQuery}

+
+
+
+ )} +
+ {!isEditingQuery && ( + + + + )} +
+
+ )} +
+ +
+ {messages.map((message, index) => ( +
+ {message.role === "assistant" && message.content && ( +
+
+
+ +

Answer

+
+ +
+
+ +
+
+ )} + {message.toolInvocations?.map( + (toolInvocation: ToolInvocation, toolIndex: number) => ( +
+ {renderToolInvocation(toolInvocation, toolIndex)} +
+ ), + )} +
+ ))} + {suggestedQuestions.length > 0 && ( + +
+ +

Suggested questions

+
+
+ {suggestedQuestions.map((question, index) => ( + + ))} +
+
+ )} +
+
+
+ + + {hasSubmitted && !isAnimating && ( + +
+
+ setInput(e.target.value)} + disabled={isLoading} + className="w-full min-h-12 py-3 px-4 bg-muted border border-input rounded-full pr-12 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-200 focus-visible:ring-offset-2 text-sm sm:text-base" + /> + +
+
+
+ )} +
+
+ ); +}