From c41f46df9c4869143c6785009270399e10dd3834 Mon Sep 17 00:00:00 2001 From: flozi00 Date: Mon, 21 Oct 2024 12:13:20 +0000 Subject: [PATCH 1/8] improve web search --- src/lib/server/websearch/runWebSearch.ts | 2 +- .../server/websearch/search/generateQuery.ts | 37 +++++++++++-- src/lib/server/websearch/search/search.ts | 53 ++++++++++++++----- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/lib/server/websearch/runWebSearch.ts b/src/lib/server/websearch/runWebSearch.ts index 39f203b1a38..72fad4b02b1 100644 --- a/src/lib/server/websearch/runWebSearch.ts +++ b/src/lib/server/websearch/runWebSearch.ts @@ -20,7 +20,7 @@ import { mergeAsyncGenerators } from "$lib/utils/mergeAsyncGenerators"; import { MetricsServer } from "../metrics"; import { logger } from "$lib/server/logger"; -const MAX_N_PAGES_TO_SCRAPE = 8 as const; +const MAX_N_PAGES_TO_SCRAPE = 5 as const; const MAX_N_PAGES_TO_EMBED = 5 as const; export async function* runWebSearch( diff --git a/src/lib/server/websearch/search/generateQuery.ts b/src/lib/server/websearch/search/generateQuery.ts index c71841a8c17..c3b474ce619 100644 --- a/src/lib/server/websearch/search/generateQuery.ts +++ b/src/lib/server/websearch/search/generateQuery.ts @@ -3,7 +3,7 @@ import { format } from "date-fns"; import type { EndpointMessage } from "../../endpoints/endpoints"; import { generateFromDefaultEndpoint } from "../../generateFromDefaultEndpoint"; -export async function generateQuery(messages: Message[]) { +export async function generateQuery(messages: Message[], generatedQueries?: string[]) { const currentDate = format(new Date(), "MMMM d, yyyy"); const userMessages = messages.filter(({ from }) => from === "user"); const previousUserMessages = userMessages.slice(0, -1); @@ -17,6 +17,9 @@ export async function generateQuery(messages: Message[]) { - Who is the president of France? Current Question: What about Mexico? + +Previously generated queries: +- President list of Mexico `, }, { @@ -47,8 +50,26 @@ Current Question: Where is it being hosted?`, from: "assistant", content: `news ${format(new Date(Date.now() - 864e5), "MMMM d, yyyy")}`, }, - { from: "user", content: "What is the current weather in Paris?" }, - { from: "assistant", content: `weather in Paris ${currentDate}` }, + { + from: "user", + content: `Current Question: My dog has been bitten, what should the gums look like so that he is healthy and when does he need an infusion? + +Previously generated queries: +- What healthy gums look like in dogs +- What unhealthy gums look like in dogs +`, + }, + { from: "assistant", content: `dog, gums, indications of necessary infusion` }, + { + from: "user", + content: `Current Question: Who is Elon Musk ? + +Previously generated queries: +- Elon Musk biography +- Who is Elon Musk +`, + }, + { from: "assistant", content: `Elon Musk` }, { from: "user", content: @@ -58,13 +79,19 @@ Current Question: Where is it being hosted?`, .join("\n")}` : "") + "\n\nCurrent Question: " + - lastMessage.content, + lastMessage.content + + "\n" + + (generatedQueries && generatedQueries.length > 0 + ? `Previously generated queries:\n${generatedQueries.map((q) => `- ${q}`).join("\n")}` + : ""), }, ]; + const preprompt = `You are tasked with generating precise and effective web search queries to answer the user's question. Provide a concise and specific query for Google search that will yield the most relevant and up-to-date results. Include key terms and related phrases, and avoid unnecessary words. Answer with only the query. Avoid duplicates, make the prompts as divers as you can. You are not allowed to repeat queries. Today is ${currentDate}`; + const webQuery = await generateFromDefaultEndpoint({ messages: convQuery, - preprompt: `You are tasked with generating web search queries. Give me an appropriate query to answer my question for google search. Answer with only the query. Today is ${currentDate}`, + preprompt, generateSettings: { max_new_tokens: 30, }, diff --git a/src/lib/server/websearch/search/search.ts b/src/lib/server/websearch/search/search.ts index 9f232a0ea98..9c35e228331 100644 --- a/src/lib/server/websearch/search/search.ts +++ b/src/lib/server/websearch/search/search.ts @@ -15,6 +15,7 @@ import type { MessageWebSearchUpdate } from "$lib/types/MessageUpdate"; const listSchema = z.array(z.string()).default([]); const allowList = listSchema.parse(JSON5.parse(env.WEBSEARCH_ALLOWLIST)); const blockList = listSchema.parse(JSON5.parse(env.WEBSEARCH_BLOCKLIST)); +const num_searches = 3; export async function* search( messages: Message[], @@ -25,6 +26,8 @@ export async function* search( { searchQuery: string; pages: WebSearchSource[] }, undefined > { + const searchQueries: string[] = []; + if (ragSettings && ragSettings?.allowedLinks.length > 0) { yield makeGeneralUpdate({ message: "Using links specified in Assistant" }); return { @@ -33,24 +36,48 @@ export async function* search( }; } - const searchQuery = query ?? (await generateQuery(messages)); - yield makeGeneralUpdate({ message: `Searching ${getWebSearchProvider()}`, args: [searchQuery] }); + for (let i = 0; i < num_searches; i++) { + const searchQuery = query ?? (await generateQuery(messages, searchQueries)); + searchQueries.push(searchQuery); + yield makeGeneralUpdate({ + message: `Searching ${getWebSearchProvider()}`, + args: [searchQuery], + }); + } + + let combinedResults: WebSearchSource[] = []; - // handle the global and (optional) rag lists - if (ragSettings && ragSettings?.allowedDomains.length > 0) { - yield makeGeneralUpdate({ message: "Filtering on specified domains" }); + for (const searchQuery of searchQueries) { + // handle the global and (optional) rag lists + if (ragSettings && ragSettings?.allowedDomains.length > 0) { + yield makeGeneralUpdate({ message: "Filtering on specified domains" }); + } + const filters = buildQueryFromSiteFilters( + [...(ragSettings?.allowedDomains ?? []), ...allowList], + blockList + ); + + const searchQueryWithFilters = `${filters} ${searchQuery}`; + const searchResults = await searchWeb(searchQueryWithFilters).then(filterByBlockList); + combinedResults = [...combinedResults, ...searchResults]; } - const filters = buildQueryFromSiteFilters( - [...(ragSettings?.allowedDomains ?? []), ...allowList], - blockList - ); - const searchQueryWithFilters = `${filters} ${searchQuery}`; - const searchResults = await searchWeb(searchQueryWithFilters).then(filterByBlockList); + // re-sort the results by relevance + // all results are appended to the end of the list + // so the most relevant results are at the beginning + // using num_searches iterating over the list to get the most relevant results + // example input: [a1,a2,a3,a4,a5,b1,b2,b3,b4,b5,c1,c2,c3,c4,c5] + // example output: [a1,b1,c1,a2,b2,c2,a3,b3,c3,a4,b4,c4,a5,b5,c5] + const sortedResults = []; + for (let i = 0; i < num_searches; i++) { + for (let j = i; j < combinedResults.length; j += num_searches) { + sortedResults.push(combinedResults[j]); + } + } return { - searchQuery: searchQueryWithFilters, - pages: searchResults, + searchQuery: searchQueries.join(" | "), + pages: sortedResults, }; } From 8b5e2b3b290770f73d8e835789d6429b0af96093 Mon Sep 17 00:00:00 2001 From: flozi00 Date: Mon, 21 Oct 2024 12:29:48 +0000 Subject: [PATCH 2/8] feat: increase maximum pages to scrape from 5 to 15 --- src/lib/server/websearch/runWebSearch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/websearch/runWebSearch.ts b/src/lib/server/websearch/runWebSearch.ts index 72fad4b02b1..9093e5be45a 100644 --- a/src/lib/server/websearch/runWebSearch.ts +++ b/src/lib/server/websearch/runWebSearch.ts @@ -20,7 +20,7 @@ import { mergeAsyncGenerators } from "$lib/utils/mergeAsyncGenerators"; import { MetricsServer } from "../metrics"; import { logger } from "$lib/server/logger"; -const MAX_N_PAGES_TO_SCRAPE = 5 as const; +const MAX_N_PAGES_TO_SCRAPE = 15 as const; const MAX_N_PAGES_TO_EMBED = 5 as const; export async function* runWebSearch( From 0bfd13515cfa70cc2aa2723249b43e05df675150 Mon Sep 17 00:00:00 2001 From: flozi00 Date: Tue, 22 Oct 2024 14:11:36 +0000 Subject: [PATCH 3/8] feat(search): enhance link handling with dynamic query replacement --- src/lib/server/websearch/search/search.ts | 48 ++++++++++++++++++----- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/lib/server/websearch/search/search.ts b/src/lib/server/websearch/search/search.ts index 9c35e228331..0b44067ed65 100644 --- a/src/lib/server/websearch/search/search.ts +++ b/src/lib/server/websearch/search/search.ts @@ -27,22 +27,52 @@ export async function* search( undefined > { const searchQueries: string[] = []; + const newLinks: string[] = []; + let requireQuery = false; if (ragSettings && ragSettings?.allowedLinks.length > 0) { - yield makeGeneralUpdate({ message: "Using links specified in Assistant" }); - return { - searchQuery: "", - pages: await directLinksToSource(ragSettings.allowedLinks).then(filterByBlockList), - }; + for (const link of ragSettings.allowedLinks) { + if (link.includes("[query]")) { + requireQuery = true; + break; + } + } + if (!requireQuery) { + yield makeGeneralUpdate({ message: "Using links specified in Assistant" }); + return { + searchQuery: "", + pages: await directLinksToSource(ragSettings?.allowedLinks).then(filterByBlockList), + }; + } } for (let i = 0; i < num_searches; i++) { const searchQuery = query ?? (await generateQuery(messages, searchQueries)); searchQueries.push(searchQuery); - yield makeGeneralUpdate({ - message: `Searching ${getWebSearchProvider()}`, - args: [searchQuery], - }); + + if (ragSettings && ragSettings?.allowedLinks.length > 0) { + for (const link of ragSettings.allowedLinks) { + const newLink = link.replace("[query]", encodeURIComponent(searchQuery)); + newLinks.push(newLink); + } + yield makeGeneralUpdate({ + message: `Querying provided Endpoints with`, + args: [searchQuery], + }); + } else { + yield makeGeneralUpdate({ + message: `Searching ${getWebSearchProvider()}`, + args: [searchQuery], + }); + } + } + + if (newLinks.length > 0) { + yield makeGeneralUpdate({ message: "Using links specified in Assistant" }); + return { + searchQuery: "", + pages: await directLinksToSource(newLinks).then(filterByBlockList), + }; } let combinedResults: WebSearchSource[] = []; From 529c1e84c26cb5e3923eaed0e4911dcfc0b2b4b9 Mon Sep 17 00:00:00 2001 From: flozi00 Date: Tue, 22 Oct 2024 17:40:49 +0000 Subject: [PATCH 4/8] fix duplicates --- src/lib/server/websearch/search/search.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/server/websearch/search/search.ts b/src/lib/server/websearch/search/search.ts index 0b44067ed65..88a20e22b2f 100644 --- a/src/lib/server/websearch/search/search.ts +++ b/src/lib/server/websearch/search/search.ts @@ -53,7 +53,9 @@ export async function* search( if (ragSettings && ragSettings?.allowedLinks.length > 0) { for (const link of ragSettings.allowedLinks) { const newLink = link.replace("[query]", encodeURIComponent(searchQuery)); - newLinks.push(newLink); + if (!newLinks.includes(newLink)) { + newLinks.push(newLink); + } } yield makeGeneralUpdate({ message: `Querying provided Endpoints with`, From 67260a1a03988e8123187cb40ecc0a8ebe887c46 Mon Sep 17 00:00:00 2001 From: flozi00 Date: Wed, 23 Oct 2024 11:18:33 +0000 Subject: [PATCH 5/8] feat(search): implement dynamic search query limit and enhance query generation --- .../server/websearch/search/generateQuery.ts | 52 +++++++++---------- src/lib/server/websearch/search/search.ts | 14 ++--- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/lib/server/websearch/search/generateQuery.ts b/src/lib/server/websearch/search/generateQuery.ts index c3b474ce619..b77b82c2e10 100644 --- a/src/lib/server/websearch/search/generateQuery.ts +++ b/src/lib/server/websearch/search/generateQuery.ts @@ -2,8 +2,11 @@ import type { Message } from "$lib/types/Message"; import { format } from "date-fns"; import type { EndpointMessage } from "../../endpoints/endpoints"; import { generateFromDefaultEndpoint } from "../../generateFromDefaultEndpoint"; +import { env } from "$env/dynamic/private"; -export async function generateQuery(messages: Message[], generatedQueries?: string[]) { +const num_searches = env.NUM_SEARCHES ? parseInt(env.NUM_SEARCHES, 10) : 3; + +export async function generateQuery(messages: Message[]) { const currentDate = format(new Date(), "MMMM d, yyyy"); const userMessages = messages.filter(({ from }) => from === "user"); const previousUserMessages = userMessages.slice(0, -1); @@ -17,9 +20,6 @@ export async function generateQuery(messages: Message[], generatedQueries?: stri - Who is the president of France? Current Question: What about Mexico? - -Previously generated queries: -- President list of Mexico `, }, { @@ -52,24 +52,24 @@ Current Question: Where is it being hosted?`, }, { from: "user", - content: `Current Question: My dog has been bitten, what should the gums look like so that he is healthy and when does he need an infusion? - -Previously generated queries: -- What healthy gums look like in dogs -- What unhealthy gums look like in dogs + content: `Current Question: My dog has been bitten, what should the gums look like so that he is healthy and when does he need an infusion?`, + }, + { + from: "assistant", + content: `What healthy gums look like in dogs +What unhealthy gums look like in dogs +When dogs need an infusion, gum signals `, }, - { from: "assistant", content: `dog, gums, indications of necessary infusion` }, { from: "user", - content: `Current Question: Who is Elon Musk ? - -Previously generated queries: -- Elon Musk biography -- Who is Elon Musk -`, + content: `Current Question: Who is Elon Musk ?`, + }, + { + from: "assistant", + content: `Elon Musk +Elon Musk Biography`, }, - { from: "assistant", content: `Elon Musk` }, { from: "user", content: @@ -79,23 +79,23 @@ Previously generated queries: .join("\n")}` : "") + "\n\nCurrent Question: " + - lastMessage.content + - "\n" + - (generatedQueries && generatedQueries.length > 0 - ? `Previously generated queries:\n${generatedQueries.map((q) => `- ${q}`).join("\n")}` - : ""), + lastMessage.content, }, ]; - const preprompt = `You are tasked with generating precise and effective web search queries to answer the user's question. Provide a concise and specific query for Google search that will yield the most relevant and up-to-date results. Include key terms and related phrases, and avoid unnecessary words. Answer with only the query. Avoid duplicates, make the prompts as divers as you can. You are not allowed to repeat queries. Today is ${currentDate}`; + const preprompt = `You are tasked with generating precise and effective web search queries to answer the user's question. Provide a concise and specific query for Google search that will yield the most relevant and up-to-date results. Include key terms and related phrases, and avoid unnecessary words. Answer with only the queries split by linebreaks. Avoid duplicates, make the prompts as divers as you can. You are not allowed to repeat queries. Today is ${currentDate}`; const webQuery = await generateFromDefaultEndpoint({ messages: convQuery, preprompt, generateSettings: { - max_new_tokens: 30, + max_new_tokens: 128, }, }); - - return webQuery.trim(); + // transform to list, split by linebreaks + const webQueryList = webQuery.split("\n").map((query) => query.trim()); + // remove duplicates + const uniqueWebQueryList = Array.from(new Set(webQueryList)); + // return only the first num_searches queries + return uniqueWebQueryList.slice(0, num_searches); } diff --git a/src/lib/server/websearch/search/search.ts b/src/lib/server/websearch/search/search.ts index 88a20e22b2f..dba61807877 100644 --- a/src/lib/server/websearch/search/search.ts +++ b/src/lib/server/websearch/search/search.ts @@ -15,7 +15,6 @@ import type { MessageWebSearchUpdate } from "$lib/types/MessageUpdate"; const listSchema = z.array(z.string()).default([]); const allowList = listSchema.parse(JSON5.parse(env.WEBSEARCH_ALLOWLIST)); const blockList = listSchema.parse(JSON5.parse(env.WEBSEARCH_BLOCKLIST)); -const num_searches = 3; export async function* search( messages: Message[], @@ -26,7 +25,6 @@ export async function* search( { searchQuery: string; pages: WebSearchSource[] }, undefined > { - const searchQueries: string[] = []; const newLinks: string[] = []; let requireQuery = false; @@ -46,10 +44,12 @@ export async function* search( } } - for (let i = 0; i < num_searches; i++) { - const searchQuery = query ?? (await generateQuery(messages, searchQueries)); - searchQueries.push(searchQuery); + let searchQueries = await generateQuery(messages); + if (!searchQueries.length && query) { + searchQueries = [query]; + } + for (const searchQuery of searchQueries) { if (ragSettings && ragSettings?.allowedLinks.length > 0) { for (const link of ragSettings.allowedLinks) { const newLink = link.replace("[query]", encodeURIComponent(searchQuery)); @@ -101,8 +101,8 @@ export async function* search( // example input: [a1,a2,a3,a4,a5,b1,b2,b3,b4,b5,c1,c2,c3,c4,c5] // example output: [a1,b1,c1,a2,b2,c2,a3,b3,c3,a4,b4,c4,a5,b5,c5] const sortedResults = []; - for (let i = 0; i < num_searches; i++) { - for (let j = i; j < combinedResults.length; j += num_searches) { + for (let i = 0; i < searchQueries.length; i++) { + for (let j = i; j < combinedResults.length; j += searchQueries.length) { sortedResults.push(combinedResults[j]); } } From b44f8db8f87054c54f9117c35756604f1d5effb9 Mon Sep 17 00:00:00 2001 From: Florian Zimmermeister Date: Thu, 24 Oct 2024 11:04:51 +0200 Subject: [PATCH 6/8] Add weather tool to fetch weather data Add a new tool to fetch weather data using the Open-Meteo API. * **New Tool Implementation** - Create `weather.ts` to define the weather tool with inputs and call method to fetch weather data. - Implement `call` method to fetch coordinates and weather data using utility functions. * **Tool Integration** - Import and add the weather tool to the list of tools in `index.ts`. * **Output Handling** - Add a new output component for weather data in `outputs.ts`. * **Utility Functions** - Add utility functions in `utils.ts` to fetch weather data and coordinates from the Open-Meteo API. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/flozi00/chat-ui?shareId=XXXX-XXXX-XXXX-XXXX). --- src/lib/server/tools/index.ts | 3 ++- src/lib/server/tools/outputs.ts | 4 ++++ src/lib/server/tools/utils.ts | 25 +++++++++++++++++++++ src/lib/server/tools/weather.ts | 39 +++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/lib/server/tools/weather.ts diff --git a/src/lib/server/tools/index.ts b/src/lib/server/tools/index.ts index 2cb06903586..306b1d142a3 100644 --- a/src/lib/server/tools/index.ts +++ b/src/lib/server/tools/index.ts @@ -19,6 +19,7 @@ import calculator from "./calculator"; import directlyAnswer from "./directlyAnswer"; import fetchUrl from "./web/url"; import websearch from "./web/search"; +import weather from "./weather"; import { callSpace, getIpToken } from "./utils"; import { uploadFile } from "../files/uploadFile"; import type { MessageFile } from "$lib/types/Message"; @@ -127,7 +128,7 @@ export const configTools = z })) ) // add the extra hardcoded tools - .transform((val) => [...val, calculator, directlyAnswer, fetchUrl, websearch]); + .transform((val) => [...val, calculator, directlyAnswer, fetchUrl, websearch, weather]); export function getCallMethod(tool: Omit): BackendCall { return async function* (params, ctx, uuid) { diff --git a/src/lib/server/tools/outputs.ts b/src/lib/server/tools/outputs.ts index d8484c6abd2..95ddd0c7187 100644 --- a/src/lib/server/tools/outputs.ts +++ b/src/lib/server/tools/outputs.ts @@ -43,6 +43,10 @@ export const ToolOutputPaths: Record< type: "str", path: "$", }, + weather: { + type: "str", + path: "$", + }, }; export const isValidOutputComponent = ( diff --git a/src/lib/server/tools/utils.ts b/src/lib/server/tools/utils.ts index 30cc9ae0ec4..bf790a3aee6 100644 --- a/src/lib/server/tools/utils.ts +++ b/src/lib/server/tools/utils.ts @@ -111,3 +111,28 @@ export async function extractJson(text: string): Promise { } return calls.flat(); } + +export async function fetchWeatherData(latitude: number, longitude: number): Promise { + const response = await fetch( + `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m` + ); + if (!response.ok) { + throw new Error("Failed to fetch weather data"); + } + return response.json(); +} + +export async function fetchCoordinates(location: string): Promise<{ latitude: number; longitude: number }> { + const response = await fetch( + `https://geocoding-api.open-meteo.com/v1/search?name=${location}&count=1` + ); + if (!response.ok) { + throw new Error("Failed to fetch coordinates"); + } + const data = await response.json(); + if (data.results.length === 0) { + throw new Error("Location not found"); + } + const { latitude, longitude } = data.results[0]; + return { latitude, longitude }; +} diff --git a/src/lib/server/tools/weather.ts b/src/lib/server/tools/weather.ts new file mode 100644 index 00000000000..06388d9806b --- /dev/null +++ b/src/lib/server/tools/weather.ts @@ -0,0 +1,39 @@ +import type { ConfigTool } from "$lib/types/Tool"; +import { ObjectId } from "mongodb"; +import { fetchWeatherData, fetchCoordinates } from "./utils"; + +const weather: ConfigTool = { + _id: new ObjectId("00000000000000000000000D"), + type: "config", + description: "Fetch the weather for a specified location", + color: "blue", + icon: "cloud", + displayName: "Weather", + name: "weather", + endpoint: null, + inputs: [ + { + name: "location", + type: "str", + description: "The name of the location to fetch the weather for", + paramType: "required", + }, + ], + outputComponent: null, + outputComponentIdx: null, + showOutput: false, + async *call({ location }) { + try { + const coordinates = await fetchCoordinates(location); + const weatherData = await fetchWeatherData(coordinates.latitude, coordinates.longitude); + + return { + outputs: [{ weather: weatherData }], + }; + } catch (error) { + throw new Error("Failed to fetch weather data", { cause: error }); + } + }, +}; + +export default weather; From ffce5dbcb5c99f4654c993096aae682d45f78401 Mon Sep 17 00:00:00 2001 From: flozi00 Date: Thu, 24 Oct 2024 09:20:41 +0000 Subject: [PATCH 7/8] clean --- src/lib/server/tools/outputs.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib/server/tools/outputs.ts b/src/lib/server/tools/outputs.ts index 95ddd0c7187..d8484c6abd2 100644 --- a/src/lib/server/tools/outputs.ts +++ b/src/lib/server/tools/outputs.ts @@ -43,10 +43,6 @@ export const ToolOutputPaths: Record< type: "str", path: "$", }, - weather: { - type: "str", - path: "$", - }, }; export const isValidOutputComponent = ( From 6f9e378c89bf3b41cc60f17e6f59cc2312b23da3 Mon Sep 17 00:00:00 2001 From: flozi00 Date: Thu, 24 Oct 2024 14:42:34 +0000 Subject: [PATCH 8/8] prettier happy --- src/lib/server/tools/utils.ts | 6 ++-- src/lib/server/tools/weather.ts | 63 +++++++++++++++++---------------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/lib/server/tools/utils.ts b/src/lib/server/tools/utils.ts index bf790a3aee6..74e209027b6 100644 --- a/src/lib/server/tools/utils.ts +++ b/src/lib/server/tools/utils.ts @@ -112,7 +112,7 @@ export async function extractJson(text: string): Promise { return calls.flat(); } -export async function fetchWeatherData(latitude: number, longitude: number): Promise { +export async function fetchWeatherData(latitude: number, longitude: number): Promise { const response = await fetch( `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m` ); @@ -122,7 +122,9 @@ export async function fetchWeatherData(latitude: number, longitude: number): Pro return response.json(); } -export async function fetchCoordinates(location: string): Promise<{ latitude: number; longitude: number }> { +export async function fetchCoordinates( + location: string +): Promise<{ latitude: number; longitude: number }> { const response = await fetch( `https://geocoding-api.open-meteo.com/v1/search?name=${location}&count=1` ); diff --git a/src/lib/server/tools/weather.ts b/src/lib/server/tools/weather.ts index 06388d9806b..09819d8717b 100644 --- a/src/lib/server/tools/weather.ts +++ b/src/lib/server/tools/weather.ts @@ -3,37 +3,40 @@ import { ObjectId } from "mongodb"; import { fetchWeatherData, fetchCoordinates } from "./utils"; const weather: ConfigTool = { - _id: new ObjectId("00000000000000000000000D"), - type: "config", - description: "Fetch the weather for a specified location", - color: "blue", - icon: "cloud", - displayName: "Weather", - name: "weather", - endpoint: null, - inputs: [ - { - name: "location", - type: "str", - description: "The name of the location to fetch the weather for", - paramType: "required", - }, - ], - outputComponent: null, - outputComponentIdx: null, - showOutput: false, - async *call({ location }) { - try { - const coordinates = await fetchCoordinates(location); - const weatherData = await fetchWeatherData(coordinates.latitude, coordinates.longitude); + _id: new ObjectId("00000000000000000000000D"), + type: "config", + description: "Fetch the weather for a specified location", + color: "blue", + icon: "cloud", + displayName: "Weather", + name: "weather", + endpoint: null, + inputs: [ + { + name: "location", + type: "str", + description: "The name of the location to fetch the weather for", + paramType: "required", + }, + ], + outputComponent: null, + outputComponentIdx: null, + showOutput: false, + async *call({ location }) { + try { + if (typeof location !== "string") { + throw new Error("Location must be a string"); + } + const coordinates = await fetchCoordinates(location); + const weatherData = await fetchWeatherData(coordinates.latitude, coordinates.longitude); - return { - outputs: [{ weather: weatherData }], - }; - } catch (error) { - throw new Error("Failed to fetch weather data", { cause: error }); - } - }, + return { + outputs: [{ weather: weatherData }], + }; + } catch (error) { + throw new Error("Failed to fetch weather data", { cause: error }); + } + }, }; export default weather;