Update example with the last version of arcade-js (#396)
Update our example with the latest version of `arcade-js`. This means we can delete all the utility functions we created here, since we now have first-class `Zod` support and they are no longer needed. > [!WARNING] > Don't merge until this [PR](https://github.com/ArcadeAI/arcade-js/pull/127) is merged.
This commit is contained in:
parent
419a0ac717
commit
dfe3005fe6
6 changed files with 147 additions and 149 deletions
|
|
@ -1,8 +1,10 @@
|
|||
<h3 align="center">
|
||||
<a name="readme-top"></a>
|
||||
<img
|
||||
src="https://docs.arcade.dev/images/logo/arcade-logo.png"
|
||||
>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_white.svg" width="300">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_black.svg" width="300">
|
||||
<img alt="Fallback image description" src="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_black.svg" width="300" >
|
||||
</picture>
|
||||
</h3>
|
||||
<div align="center">
|
||||
<h3>Arcade AI SDK Example</h3>
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
import { Arcade } from "@arcadeai/arcadejs"
|
||||
import { PermissionDeniedError } from "@arcadeai/arcadejs"
|
||||
import { ToolExecutionError } from "ai"
|
||||
import { jsonSchema } from "ai"
|
||||
|
||||
const arcadeClient = new Arcade({
|
||||
baseURL: "http://localhost:9099",
|
||||
})
|
||||
|
||||
/**
|
||||
* Retrieves and formats tools from Arcade.dev to the format required by the AI SDK.
|
||||
* @param {Object} options - The options object
|
||||
* @param {string} [options.toolkit] - Optional toolkit name to filter tools (e.g. "google", "slack")
|
||||
* @param {string} options.user_id - The user ID from your application (e.g. an email, UUID, etc.)
|
||||
*/
|
||||
export const getArcadeTools = async ({ toolkit, user_id }) => {
|
||||
const tools = await arcadeClient.tools.formatted.list({
|
||||
...(toolkit && { toolkit }),
|
||||
format: "openai",
|
||||
})
|
||||
|
||||
return tools.items.reduce((acc, item) => {
|
||||
if (!item.function.name) return acc
|
||||
acc[item.function.name] = {
|
||||
parameters: jsonSchema(item.function.parameters),
|
||||
description: item.description,
|
||||
execute: async (input) =>
|
||||
await arcadeClient.tools.execute({
|
||||
tool_name: item.function.name,
|
||||
input,
|
||||
user_id,
|
||||
}),
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the error indicates that user authorization is needed for the tool
|
||||
* @param {Error} error - The error caught during tool execution
|
||||
* @returns {boolean} True if the error indicates authorization is required
|
||||
*/
|
||||
export const isAuthorizationRequiredError = (error) => {
|
||||
return error instanceof ToolExecutionError && error.cause instanceof PermissionDeniedError
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the authorization response for a tool that requires authentication
|
||||
* @param {string} toolName - The name of the tool that needs authorization
|
||||
* @param {string} user_id - The user ID from your application
|
||||
* @returns {Promise<{url: string}>} The authorization response
|
||||
*/
|
||||
export const getAuthorizationResponse = async (toolName, user_id) => {
|
||||
return await arcadeClient.tools.authorize({
|
||||
tool_name: toolName,
|
||||
user_id,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles authorization errors by returning the authorization URL if needed, otherwise rethrows the error
|
||||
* @param {Error} error - The error to handle
|
||||
* @param {string} user_id - The user ID from your application
|
||||
* @returns {Promise<string>} The authorization URL if needed, otherwise the error is rethrown
|
||||
*/
|
||||
export const handleAuthorizationError = async (error, user_id) => {
|
||||
if (isAuthorizationRequiredError(error)) {
|
||||
const response = await getAuthorizationResponse(error.toolName, user_id)
|
||||
return response.url
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
48
examples/ai-sdk/generateText.js
Normal file
48
examples/ai-sdk/generateText.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { openai } from "@ai-sdk/openai"
|
||||
import { Arcade } from "@arcadeai/arcadejs"
|
||||
import { executeOrAuthorizeZodTool, toZodToolSet } from "@arcadeai/arcadejs/lib"
|
||||
import { generateText } from "ai"
|
||||
|
||||
const arcade = new Arcade()
|
||||
|
||||
/**
|
||||
* Get the Google toolkit.
|
||||
*/
|
||||
const googleToolkit = await arcade.tools.list({
|
||||
limit: 25,
|
||||
toolkit: "google",
|
||||
})
|
||||
|
||||
/**
|
||||
* The Vercel AI SDK requires tools to be defined using Zod, a TypeScript-first schema validation library
|
||||
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
|
||||
* - Provides runtime type safety and validation
|
||||
* - Offers excellent TypeScript integration with automatic type inference
|
||||
* - Has a simple, declarative API for defining schemas
|
||||
* - Is widely adopted in the TypeScript ecosystem
|
||||
*
|
||||
* Arcade provides `toZodToolSet` to convert our tools into Zod format, making them compatible
|
||||
* with the AI SDK.
|
||||
*
|
||||
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
|
||||
* It checks if the tool requires authorization: if so, it returns an authorization URL,
|
||||
* otherwise, it runs the tool directly without extra boilerplate.
|
||||
*
|
||||
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
|
||||
*/
|
||||
const googleTools = toZodToolSet({
|
||||
tools: googleToolkit.items,
|
||||
client: arcade,
|
||||
userId: "<YOUR_USER_ID>", // Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
|
||||
executeFactory: executeOrAuthorizeZodTool, // Checks if tool is authorized and executes it, or returns authorization URL if needed
|
||||
})
|
||||
|
||||
const result = await generateText({
|
||||
model: openai("gpt-4o-mini"),
|
||||
prompt: "Read my last email and summarize it in a few sentences",
|
||||
tools: googleTools,
|
||||
maxSteps: 5,
|
||||
})
|
||||
|
||||
// Log the result
|
||||
console.log(result.text)
|
||||
|
|
@ -1,31 +1,50 @@
|
|||
import { openai } from "@ai-sdk/openai";
|
||||
import { generateText } from "ai";
|
||||
import { getArcadeTools, handleAuthorizationError } from "./arcade.js";
|
||||
import { openai } from "@ai-sdk/openai"
|
||||
import { Arcade } from "@arcadeai/arcadejs"
|
||||
import { executeOrAuthorizeZodTool, toZodToolSet } from "@arcadeai/arcadejs/lib"
|
||||
import { streamText } from "ai"
|
||||
|
||||
// Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
|
||||
const USER_ID = "USER_ID";
|
||||
const arcade = new Arcade()
|
||||
|
||||
const model = openai("gpt-4o-mini");
|
||||
const tools = await getArcadeTools({ toolkit: "google", user_id: USER_ID });
|
||||
/**
|
||||
* Get the Google toolkit.
|
||||
*/
|
||||
const googleToolkit = await arcade.tools.list({
|
||||
limit: 25,
|
||||
toolkit: "google",
|
||||
})
|
||||
|
||||
export const answerMyQuestion = async (prompt) => {
|
||||
try {
|
||||
const result = await generateText({
|
||||
model,
|
||||
prompt,
|
||||
tools,
|
||||
maxSteps: 5,
|
||||
});
|
||||
/**
|
||||
* The Vercel AI SDK requires tools to be defined using Zod, a TypeScript-first schema validation library
|
||||
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
|
||||
* - Provides runtime type safety and validation
|
||||
* - Offers excellent TypeScript integration with automatic type inference
|
||||
* - Has a simple, declarative API for defining schemas
|
||||
* - Is widely adopted in the TypeScript ecosystem
|
||||
*
|
||||
* Arcade provides `toZodToolSet` to convert our tools into Zod format, making them compatible
|
||||
* with the AI SDK.
|
||||
*
|
||||
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
|
||||
* It checks if the tool requires authorization: if so, it returns an authorization URL,
|
||||
* otherwise, it runs the tool directly without extra boilerplate.
|
||||
*
|
||||
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
|
||||
*/
|
||||
const googleTools = toZodToolSet({
|
||||
tools: googleToolkit.items,
|
||||
client: arcade,
|
||||
userId: "<YOUR_USER_ID>", // Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
|
||||
executeFactory: executeOrAuthorizeZodTool,
|
||||
})
|
||||
|
||||
return result.text;
|
||||
} catch (error) {
|
||||
const url = await handleAuthorizationError(error, USER_ID);
|
||||
return `Authorization Required: Please visit this link to connect your Google account: ${url}`;
|
||||
}
|
||||
};
|
||||
const { textStream } = streamText({
|
||||
model: openai("gpt-4o-mini"),
|
||||
prompt: "Read my last email and summarize it in a few sentences",
|
||||
tools: googleTools,
|
||||
maxSteps: 5,
|
||||
})
|
||||
|
||||
const answer = await answerMyQuestion(
|
||||
"Read my last email and summarize it in a few sentences"
|
||||
);
|
||||
|
||||
console.log(answer);
|
||||
// Stream the response to the console
|
||||
for await (const chunk of textStream) {
|
||||
process.stdout.write(chunk)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@
|
|||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node --env-file=.env index.js"
|
||||
"dev": "node --env-file=.env index.js",
|
||||
"generateText": "node --env-file=.env generateText.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"packageManager": "pnpm@10.6.5",
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^1.3.6",
|
||||
"@arcadeai/arcadejs": "^1.2.1",
|
||||
"ai": "^4.2.11"
|
||||
"@ai-sdk/openai": "^1.3.22",
|
||||
"@arcadeai/arcadejs": "latest",
|
||||
"ai": "^4.3.15"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,35 +9,35 @@ importers:
|
|||
.:
|
||||
dependencies:
|
||||
'@ai-sdk/openai':
|
||||
specifier: ^1.3.6
|
||||
version: 1.3.6(zod@3.24.2)
|
||||
specifier: ^1.3.22
|
||||
version: 1.3.22(zod@3.24.2)
|
||||
'@arcadeai/arcadejs':
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
specifier: latest
|
||||
version: 1.4.2
|
||||
ai:
|
||||
specifier: ^4.2.11
|
||||
version: 4.2.11(react@19.1.0)(zod@3.24.2)
|
||||
specifier: ^4.3.15
|
||||
version: 4.3.15(react@19.1.0)(zod@3.24.2)
|
||||
|
||||
packages:
|
||||
|
||||
'@ai-sdk/openai@1.3.6':
|
||||
resolution: {integrity: sha512-Lyp6W6dg+ERMJru3DI8/pWAjXLB0GbMMlXh4jxA3mVny8CJHlCAjlEJRuAdLg1/CFz4J1UDN2/4qBnIWtLFIqw==}
|
||||
'@ai-sdk/openai@1.3.22':
|
||||
resolution: {integrity: sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.0.0
|
||||
|
||||
'@ai-sdk/provider-utils@2.2.3':
|
||||
resolution: {integrity: sha512-o3fWTzkxzI5Af7U7y794MZkYNEsxbjLam2nxyoUZSScqkacb7vZ3EYHLh21+xCcSSzEC161C7pZAGHtC0hTUMw==}
|
||||
'@ai-sdk/provider-utils@2.2.8':
|
||||
resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.23.8
|
||||
|
||||
'@ai-sdk/provider@1.1.0':
|
||||
resolution: {integrity: sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew==}
|
||||
'@ai-sdk/provider@1.1.3':
|
||||
resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@ai-sdk/react@1.2.5':
|
||||
resolution: {integrity: sha512-0jOop3S2WkDOdO4X5I+5fTGqZlNX8/h1T1eYokpkR9xh8Vmrxqw8SsovqGvrddTsZykH8uXRsvI+G4FTyy894A==}
|
||||
'@ai-sdk/react@1.2.12':
|
||||
resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
|
|
@ -46,14 +46,14 @@ packages:
|
|||
zod:
|
||||
optional: true
|
||||
|
||||
'@ai-sdk/ui-utils@1.2.4':
|
||||
resolution: {integrity: sha512-wLTxEZrKZRyBmlVZv8nGXgLBg5tASlqXwbuhoDu0MhZa467ZFREEnosH/OC/novyEHTQXko2zC606xoVbMrUcA==}
|
||||
'@ai-sdk/ui-utils@1.2.11':
|
||||
resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.23.8
|
||||
|
||||
'@arcadeai/arcadejs@1.2.1':
|
||||
resolution: {integrity: sha512-7ir38ylle6zmiM5nX1ENoIiDOp8stYEqEPbE/YDMVF7BvooD3N8ltMN9ZVmMj8DA8x0iubR/M3ewzA1nam7XMg==}
|
||||
'@arcadeai/arcadejs@1.4.2':
|
||||
resolution: {integrity: sha512-SpXPtqqS2djBDD4pujFc1xr/BgOhgZYmMDyjuQnq8B6e92j4mMHvrCHj8DQkqa1SNJaPxPzETwZ6kSfhHK0Ftw==}
|
||||
|
||||
'@opentelemetry/api@1.9.0':
|
||||
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
|
||||
|
|
@ -65,8 +65,8 @@ packages:
|
|||
'@types/node-fetch@2.6.12':
|
||||
resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==}
|
||||
|
||||
'@types/node@18.19.86':
|
||||
resolution: {integrity: sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==}
|
||||
'@types/node@18.19.100':
|
||||
resolution: {integrity: sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA==}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
|
|
@ -76,8 +76,8 @@ packages:
|
|||
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
|
||||
ai@4.2.11:
|
||||
resolution: {integrity: sha512-XvYFz+I2MNpkNeso/cEvm2q22cuU7B3EdUxGyhpa94gHbb3HEk73d42+UPJqweSBmoXA52mZ9qvb6jt3P4TITQ==}
|
||||
ai@4.3.15:
|
||||
resolution: {integrity: sha512-TYKRzbWg6mx/pmTadlAEIhuQtzfHUV0BbLY72+zkovXwq/9xhcH24IlQmkyBpElK6/4ArS0dHdOOtR1jOPVwtg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
|
|
@ -265,49 +265,50 @@ packages:
|
|||
|
||||
snapshots:
|
||||
|
||||
'@ai-sdk/openai@1.3.6(zod@3.24.2)':
|
||||
'@ai-sdk/openai@1.3.22(zod@3.24.2)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.1.0
|
||||
'@ai-sdk/provider-utils': 2.2.3(zod@3.24.2)
|
||||
'@ai-sdk/provider': 1.1.3
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@3.24.2)
|
||||
zod: 3.24.2
|
||||
|
||||
'@ai-sdk/provider-utils@2.2.3(zod@3.24.2)':
|
||||
'@ai-sdk/provider-utils@2.2.8(zod@3.24.2)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.1.0
|
||||
'@ai-sdk/provider': 1.1.3
|
||||
nanoid: 3.3.11
|
||||
secure-json-parse: 2.7.0
|
||||
zod: 3.24.2
|
||||
|
||||
'@ai-sdk/provider@1.1.0':
|
||||
'@ai-sdk/provider@1.1.3':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@ai-sdk/react@1.2.5(react@19.1.0)(zod@3.24.2)':
|
||||
'@ai-sdk/react@1.2.12(react@19.1.0)(zod@3.24.2)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider-utils': 2.2.3(zod@3.24.2)
|
||||
'@ai-sdk/ui-utils': 1.2.4(zod@3.24.2)
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@3.24.2)
|
||||
'@ai-sdk/ui-utils': 1.2.11(zod@3.24.2)
|
||||
react: 19.1.0
|
||||
swr: 2.3.3(react@19.1.0)
|
||||
throttleit: 2.1.0
|
||||
optionalDependencies:
|
||||
zod: 3.24.2
|
||||
|
||||
'@ai-sdk/ui-utils@1.2.4(zod@3.24.2)':
|
||||
'@ai-sdk/ui-utils@1.2.11(zod@3.24.2)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.1.0
|
||||
'@ai-sdk/provider-utils': 2.2.3(zod@3.24.2)
|
||||
'@ai-sdk/provider': 1.1.3
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@3.24.2)
|
||||
zod: 3.24.2
|
||||
zod-to-json-schema: 3.24.5(zod@3.24.2)
|
||||
|
||||
'@arcadeai/arcadejs@1.2.1':
|
||||
'@arcadeai/arcadejs@1.4.2':
|
||||
dependencies:
|
||||
'@types/node': 18.19.86
|
||||
'@types/node': 18.19.100
|
||||
'@types/node-fetch': 2.6.12
|
||||
abort-controller: 3.0.0
|
||||
agentkeepalive: 4.6.0
|
||||
form-data-encoder: 1.7.2
|
||||
formdata-node: 4.4.1
|
||||
node-fetch: 2.7.0
|
||||
zod: 3.24.2
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
|
|
@ -317,10 +318,10 @@ snapshots:
|
|||
|
||||
'@types/node-fetch@2.6.12':
|
||||
dependencies:
|
||||
'@types/node': 18.19.86
|
||||
'@types/node': 18.19.100
|
||||
form-data: 4.0.2
|
||||
|
||||
'@types/node@18.19.86':
|
||||
'@types/node@18.19.100':
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
|
|
@ -332,12 +333,12 @@ snapshots:
|
|||
dependencies:
|
||||
humanize-ms: 1.2.1
|
||||
|
||||
ai@4.2.11(react@19.1.0)(zod@3.24.2):
|
||||
ai@4.3.15(react@19.1.0)(zod@3.24.2):
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.1.0
|
||||
'@ai-sdk/provider-utils': 2.2.3(zod@3.24.2)
|
||||
'@ai-sdk/react': 1.2.5(react@19.1.0)(zod@3.24.2)
|
||||
'@ai-sdk/ui-utils': 1.2.4(zod@3.24.2)
|
||||
'@ai-sdk/provider': 1.1.3
|
||||
'@ai-sdk/provider-utils': 2.2.8(zod@3.24.2)
|
||||
'@ai-sdk/react': 1.2.12(react@19.1.0)(zod@3.24.2)
|
||||
'@ai-sdk/ui-utils': 1.2.11(zod@3.24.2)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
jsondiffpatch: 0.6.0
|
||||
zod: 3.24.2
|
||||
|
|
|
|||
Loading…
Reference in a new issue