From 7d11f7632a070230ce6ea26fbac76a1a5dd0292e Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Tue, 4 Jun 2024 18:59:44 -0500 Subject: [PATCH] feat: add wolfram alpha client --- bin/scratch.ts | 22 ++++++--- readme.md | 8 ++- src/services/index.ts | 1 + src/services/wolfram-client.ts | 90 ++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 src/services/wolfram-client.ts diff --git a/bin/scratch.ts b/bin/scratch.ts index 5472bd0..aeb7926 100644 --- a/bin/scratch.ts +++ b/bin/scratch.ts @@ -10,7 +10,8 @@ import restoreCursor from 'restore-cursor' // import { PerigonClient } from '../src/index.js' // import { FirecrawlClient } from '../src/index.js' // import { ExaClient } from '../src/index.js' -import { DiffbotClient } from '../src/index.js' +// import { DiffbotClient } from '../src/index.js' +import { WolframClient } from '../src/index.js' /** * Scratch pad for testing. @@ -65,15 +66,24 @@ async function main() { // }) // console.log(JSON.stringify(res, null, 2)) - const diffbot = new DiffbotClient() + // const diffbot = new DiffbotClient() + // // const res = await diffbot.analyzeUrl({ + // // url: 'https://www.bbc.com/news/articles/cp4475gwny1o' + // // }) + // const res = await diffbot.enhanceEntity({ + // type: 'Person', + // name: 'Kevin Raheja' + // }) + // console.log(JSON.stringify(res, null, 2)) + + const wolfram = new WolframClient() // const res = await diffbot.analyzeUrl({ // url: 'https://www.bbc.com/news/articles/cp4475gwny1o' // }) - const res = await diffbot.enhanceEntity({ - type: 'Person', - name: 'Kevin Raheja' + const res = await wolfram.ask({ + input: 'population of new york city' }) - console.log(JSON.stringify(res, null, 2)) + console.log(res) } try { diff --git a/readme.md b/readme.md index 2f8efd1..36b4486 100644 --- a/readme.md +++ b/readme.md @@ -149,24 +149,22 @@ The SDK-specific imports are all isolated to keep the main `@agentic/stdlib` as ## TODO - rename this repo to agentic -- change license to MIT - sdks - - instructor-js - TODO - services - e2b - search-and-scrape - replicate - huggingface - - wolfram alpha + - [skyvern](https://github.com/Skyvern-AI/skyvern) - midjourney - unstructured - pull from [langchain](https://github.com/langchain-ai/langchainjs/tree/main/langchain) - provide a converter for langchain `DynamicStructuredTool` - pull from other libs - pull from [nango](https://docs.nango.dev/integrations/overview) -- tools - - calculator + - pull from [activepieces](https://github.com/activepieces/activepieces/tree/main/packages/pieces/community) + - general openapi support ala [workgpt](https://github.com/team-openpm/workgpt) - tools / chains / flows / runnables - market maps - https://github.com/causaly/zod-validation-error diff --git a/src/services/index.ts b/src/services/index.ts index d477b66..4c15b11 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -14,3 +14,4 @@ export * from './serper-client.js' export * from './twitter-client.js' export * from './weather-client.js' export * from './wikipedia-client.js' +export * from './wolfram-client.js' diff --git a/src/services/wolfram-client.ts b/src/services/wolfram-client.ts new file mode 100644 index 0000000..faf9210 --- /dev/null +++ b/src/services/wolfram-client.ts @@ -0,0 +1,90 @@ +import defaultKy, { type KyInstance } from 'ky' +import { z } from 'zod' + +import { aiFunction, AIFunctionsProvider } from '../fns.js' +import { assert, getEnv } from '../utils.js' + +export namespace wolfram { + export const API_BASE_URL = 'https://www.wolframalpha.com/api/' + + export const AskWolframAlphaOptionsSchema = z.object({ + input: z.string().describe('english query'), + maxchars: z + .number() + .int() + .positive() + .default(6000) + .optional() + .describe('max characters to generate in the response') + }) + export type AskWolframAlphaOptions = z.infer< + typeof AskWolframAlphaOptionsSchema + > +} + +/** + * Wolfram Alpha LLM API client for answering computational, mathematical, and + * scientific questions. + * + * @see https://products.wolframalpha.com/llm-api/documentation + */ +export class WolframClient extends AIFunctionsProvider { + readonly ky: KyInstance + readonly appId: string + readonly apiBaseUrl: string + + constructor({ + appId = getEnv('WOLFRAM_APP_ID'), + apiBaseUrl = wolfram.API_BASE_URL, + ky = defaultKy + }: { + appId?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + assert( + appId, + 'WolframClient missing required "appId" (defaults to "WOLFRAM_APP_ID")' + ) + super() + + this.appId = appId + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: apiBaseUrl, + headers: { + Authorization: `Bearer ${appId}` + } + }) + } + + @aiFunction({ + name: 'ask_wolfram_alpha', + description: ` +- WolframAlpha understands natural language queries about entities in chemistry, physics, geography, history, art, astronomy, and more. +- WolframAlpha performs mathematical calculations, date and unit conversions, formula solving, etc. +- Convert inputs to simplified keyword queries whenever possible (e.g. convert "how many people live in France" to "France population"). +- Send queries in English only; translate non-English queries before sending, then respond in the original language. +- ALWAYS use this exponent notation: \`6*10^14\`, NEVER \`6e14\`. +- ALWAYS use proper Markdown formatting for all math, scientific, and chemical formulas, symbols, etc.: '$$\n[expression]\n$$' for standalone cases and '( [expression] )' when inline. +- Use ONLY single-letter variable names, with or without integer subscript (e.g., n, n1, n_1). +- Use named physical constants (e.g., 'speed of light') without numerical substitution. +- Include a space between compound units (e.g., "Ω m" for "ohm*meter"). +- To solve for a variable in an equation with units, consider solving a corresponding equation without units; exclude counting units (e.g., books), include genuine units (e.g., kg). +- If a WolframAlpha result is not relevant to the query: + - If Wolfram provides multiple 'Assumptions' for a query, choose the more relevant one(s) without explaining the initial result. If you are unsure, ask the user to choose. + - Re-send the exact same 'input' with NO modifications, and add the 'assumption' parameter, formatted as a list, with the relevant values. + - ONLY simplify or rephrase the initial query if a more relevant 'Assumption' or other input suggestions are not provided. +`.trim(), + inputSchema: wolfram.AskWolframAlphaOptionsSchema + }) + async ask(queryOrOptions: string | wolfram.AskWolframAlphaOptions) { + const options = + typeof queryOrOptions === 'string' + ? { input: queryOrOptions } + : queryOrOptions + + return this.ky.get('v1/llm-api', { searchParams: { ...options } }).text() + } +}