diff --git a/examples/ai-sdk/README.md b/examples/ai-sdk/README.md
index d0ab68fe..7cf26c44 100644
--- a/examples/ai-sdk/README.md
+++ b/examples/ai-sdk/README.md
@@ -1,8 +1,10 @@
-
+
+
+
+
+
Arcade AI SDK Example
diff --git a/examples/ai-sdk/arcade.js b/examples/ai-sdk/arcade.js
deleted file mode 100644
index c1257d82..00000000
--- a/examples/ai-sdk/arcade.js
+++ /dev/null
@@ -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} 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
-}
diff --git a/examples/ai-sdk/generateText.js b/examples/ai-sdk/generateText.js
new file mode 100644
index 00000000..f4e5291a
--- /dev/null
+++ b/examples/ai-sdk/generateText.js
@@ -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 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)
diff --git a/examples/ai-sdk/index.js b/examples/ai-sdk/index.js
index 64b314f6..7ed6a5d6 100644
--- a/examples/ai-sdk/index.js
+++ b/examples/ai-sdk/index.js
@@ -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 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)
+}
diff --git a/examples/ai-sdk/package.json b/examples/ai-sdk/package.json
index 36828938..c5da36c2 100644
--- a/examples/ai-sdk/package.json
+++ b/examples/ai-sdk/package.json
@@ -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"
}
}
diff --git a/examples/ai-sdk/pnpm-lock.yaml b/examples/ai-sdk/pnpm-lock.yaml
index cb394c4f..eac69a4e 100644
--- a/examples/ai-sdk/pnpm-lock.yaml
+++ b/examples/ai-sdk/pnpm-lock.yaml
@@ -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