From b5aac360484bb62ed54bc6045b633e28ddc6fde7 Mon Sep 17 00:00:00 2001 From: nicolas-angelo Date: Thu, 9 Jan 2025 14:09:21 -0500 Subject: [PATCH] updated vercel toolkit to include custom actions in tools --- js/src/frameworks/vercel.spec.ts | 51 ++++++++++++++++++++++++++++++++ js/src/frameworks/vercel.ts | 28 ++++++++---------- js/src/sdk/actionRegistry.ts | 28 ++++++++++++++---- js/src/sdk/base.toolset.ts | 16 +++++----- 4 files changed, 94 insertions(+), 29 deletions(-) diff --git a/js/src/frameworks/vercel.spec.ts b/js/src/frameworks/vercel.spec.ts index 560584e6e31..9090e00e2a3 100644 --- a/js/src/frameworks/vercel.spec.ts +++ b/js/src/frameworks/vercel.spec.ts @@ -1,9 +1,12 @@ import { beforeAll, describe, expect, it } from "@jest/globals"; +import { z } from "zod"; import { getTestConfig } from "../../config/getTestConfig"; import { VercelAIToolSet } from "./vercel"; +import type { CreateActionOptions } from "../sdk/actionRegistry"; describe("Apps class tests", () => { let vercelAIToolSet: VercelAIToolSet; + beforeAll(() => { vercelAIToolSet = new VercelAIToolSet({ apiKey: getTestConfig().COMPOSIO_API_KEY, @@ -25,4 +28,52 @@ describe("Apps class tests", () => { }); expect(Object.keys(tools).length).toBe(1); }); + + describe("custom actions", () => { + let customAction: Awaited>; + let tools: Awaited>; + + beforeAll(async () => { + const params = z.object({ + owner: z.string().describe("The owner of the repository"), + repo: z.string().describe("The name of the repository without the `.git` extension."), + }) + + + customAction = await vercelAIToolSet.createAction({ + actionName: "starRepositoryCustomAction", + toolName: "github", + description: "Star A Github Repository For Given `Repo` And `Owner`", + inputParams: params, + callback: async ( + inputParams, + ) => ({ successful: true, data: inputParams }) + }) + + tools = await vercelAIToolSet.getTools({ + actions: ["starRepositoryCustomAction"], + }); + + }); + + it("check if custom actions are coming", async () => { + expect(Object.keys(tools).length).toBe(1); + expect(tools).toHaveProperty(customAction.name, tools[customAction.name]); + }); + + it("check if custom actions are executing", async () => { + const res = await vercelAIToolSet.executeAction({ + action: customAction.name, + params: { + owner: "composioHQ", + repo: "composio" + }, + }) + expect(res.successful).toBe(true); + expect(res.data).toHaveProperty("owner", "composioHQ"); + expect(res.data).toHaveProperty("repo", "composio"); + }); + }) + + }); diff --git a/js/src/frameworks/vercel.ts b/js/src/frameworks/vercel.ts index 75a3b7c9ed2..6236e358bdd 100644 --- a/js/src/frameworks/vercel.ts +++ b/js/src/frameworks/vercel.ts @@ -1,4 +1,4 @@ -import { jsonSchema, tool } from "ai"; +import { jsonSchema, tool, CoreTool } from "ai"; import { z } from "zod"; import { ComposioToolSet as BaseComposioToolSet } from "../sdk/base.toolset"; import { TELEMETRY_LOGGER } from "../sdk/utils/telemetry"; @@ -62,7 +62,7 @@ export class VercelAIToolSet extends BaseComposioToolSet { useCase?: Optional; usecaseLimit?: Optional; filterByAvailableApps?: Optional; - }): Promise<{ [key: string]: RawActionData }> { + }): Promise<{ [key: string]: CoreTool }> { TELEMETRY_LOGGER.manualTelemetry(TELEMETRY_EVENTS.SDK_METHOD_INVOKED, { method: "getTools", file: this.fileName, @@ -78,21 +78,19 @@ export class VercelAIToolSet extends BaseComposioToolSet { actions, } = ZExecuteToolCallParams.parse(filters); - const actionsList = await this.client.actions.list({ - ...(apps && { apps: apps?.join(",") }), - ...(tags && { tags: tags?.join(",") }), - ...(useCase && { useCase: useCase }), - ...(actions && { actions: actions?.join(",") }), - ...(usecaseLimit && { usecaseLimit: usecaseLimit }), - filterByAvailableApps: filterByAvailableApps ?? undefined, - }); + const actionsList = await this.getToolsSchema({ + apps, + actions, + tags, + useCase, + useCaseLimit: usecaseLimit, + filterByAvailableApps + }) - const tools = {}; - actionsList.items?.forEach((actionSchema) => { - // @ts-ignore + const tools: { [key: string]: CoreTool } = {}; + actionsList.forEach((actionSchema) => { tools[actionSchema.name!] = this.generateVercelTool( - // @ts-ignore - actionSchema as ActionData + actionSchema ); }); diff --git a/js/src/sdk/actionRegistry.ts b/js/src/sdk/actionRegistry.ts index 3d13ab6b537..5eff37e3393 100644 --- a/js/src/sdk/actionRegistry.ts +++ b/js/src/sdk/actionRegistry.ts @@ -18,13 +18,25 @@ type RawExecuteRequestParam = { }; }; -export type CreateActionOptions = { + +type ValidParameters = ZodObject<{ [key: string]: ZodString | ZodOptional }> +export type Parameters = ValidParameters | z.ZodObject<{}> + +type inferParameters = + PARAMETERS extends ValidParameters + ? z.infer + : z.infer> + + +export type CreateActionOptions< + P extends Parameters = z.ZodObject<{}> +> = { actionName?: string; toolName?: string; description?: string; - inputParams: ZodObject<{ [key: string]: ZodString | ZodOptional }>; + inputParams?: P; callback: ( - inputParams: Record, + inputParams: inferParameters

, authCredentials: Record | undefined, executeRequest: ( data: RawExecuteRequestParam @@ -50,7 +62,10 @@ export class ActionRegistry { client: Composio; customActions: Map< string, - { metadata: CreateActionOptions; schema: Record } + { + metadata: CreateActionOptions; + schema: Record + } >; constructor(client: Composio) { @@ -58,7 +73,8 @@ export class ActionRegistry { this.customActions = new Map(); } - async createAction(options: CreateActionOptions): Promise { + + async createAction

>(options: CreateActionOptions

): Promise { const { callback } = options; if (typeof callback !== "function") { throw new Error("Callback must be a function"); @@ -67,7 +83,7 @@ export class ActionRegistry { throw new Error("You must provide actionName for this action"); } if (!options.inputParams) { - options.inputParams = z.object({}); + options.inputParams = z.object({}) as P } const params = options.inputParams; const actionName = options.actionName || callback.name || ""; diff --git a/js/src/sdk/base.toolset.ts b/js/src/sdk/base.toolset.ts index b7740f6e3bd..454f5e7e869 100644 --- a/js/src/sdk/base.toolset.ts +++ b/js/src/sdk/base.toolset.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z, ZodObject } from "zod"; import { Composio } from "../sdk"; import { RawActionData, @@ -10,7 +10,7 @@ import { } from "../types/base_toolset"; import type { Optional, Sequence } from "../types/util"; import { getEnvVariable } from "../utils/shared"; -import { ActionRegistry, CreateActionOptions } from "./actionRegistry"; +import { ActionRegistry, CreateActionOptions, Parameters } from "./actionRegistry"; import { ActionExecutionResDto } from "./client/types.gen"; import { ActionExecuteResponse, Actions } from "./models/actions"; import { ActiveTriggers } from "./models/activeTriggers"; @@ -54,10 +54,10 @@ export class ComposioToolSet { post: TPostProcessor[]; schema: TSchemaProcessor[]; } = { - pre: [fileInputProcessor], - post: [fileResponseProcessor], - schema: [fileSchemaProcessor], - }; + pre: [fileInputProcessor], + post: [fileResponseProcessor], + schema: [fileSchemaProcessor], + }; private userDefinedProcessors: { pre?: TPreProcessor; @@ -172,8 +172,8 @@ export class ComposioToolSet { }); } - async createAction(options: CreateActionOptions) { - return this.userActionRegistry.createAction(options); + async createAction

>(options: CreateActionOptions

) { + return this.userActionRegistry.createAction

(options); } private isCustomAction(action: string) {