Composio TypeScript SDK 修饰器(Modifiers)完全指南:Schema 转换与执行前后拦截
Composio TypeScript SDK 修饰器Modifiers完全指南Schema 转换与执行前后拦截【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composioComposio TypeScript SDK 通过修饰器Modifiers机制为工具Tool的获取与执行提供了一层强大的中间件能力你可以在工具被包装给 LLM Provider 之前改写其 Schema在工具执行前改写输入参数并在工具执行后转换输出结果。本文基于 ts/docs/advanced/modifiers.md 展开结合composio/core源码实现系统讲解三类修饰器的用法、Agentic / Non-Agentic Provider 的差异、可复用修饰器设计与实战场景缓存、认证注入、数据管道、会话级追踪读完即可在真实项目中落地。重要前提并非所有修饰器都被所有 Provider 支持。Schema 修饰器modifySchema适用于所有 Provider而执行类修饰器beforeExecute、afterExecute仅被 Agentic Provider 支持。详细差异见下文「Provider 对修饰器的支持差异」一节。什么是修饰器修饰器Modifier本质上是拦截并修改工具操作正常流程的函数。Composio SDK 支持三类修饰器Schema 修饰器Schema Modifiers在工具被包装给 Provider 之前转换工具 Schema所有 Provider 均支持执行前修饰器Before Execution Modifiers在工具执行之前修改输入参数仅 Agentic Provider 支持执行后修饰器After Execution Modifiers在工具执行完成之后转换输出仅 Agentic Provider 支持。从源码层面看这三种能力在 ts/packages/core/src/types/modifiers.types.ts 中有精确的类型定义SDK 的调用链则在 ts/packages/core/src/models/Tools.ts 中实现。Schema 修饰器改写工具的「说明书」Schema 修饰器允许你在工具被包装给 Provider 之前转换其 Schema常用于定制工具描述、参数定义或附加元数据。// 在获取工具时修改工具 Schema const tools await composio.tools.get( default, { toolkits: [github], }, { modifySchema: ({ toolSlug, toolkitSlug, schema }) { // 为所有工具描述添加前缀 if (tool.description) { tool.description [Enhanced] ${tool.description}; } // 针对特定工具修改参数 if (toolSlug GITHUB_GET_REPO) { if (tool.inputParameters?.properties?.owner) { // 补充更详细的描述 tool.inputParameters.properties.owner.description GitHub organization or user name (e.g., composio); } } return tool; }, } );注意原文示例中的tool.description在真实类型中对应 Schema 对象上的字段完整工具对象。修饰器回调接收{ toolSlug, toolkitSlug, schema }三个字段其中schema是完整的Tool对象。SDK 内部getRawComposioTools在拉取远程工具并做大小写转换后会先应用默认 Schema 修饰器再依次对你传入的modifySchema修饰器做Promise.all批量应用若传入的修饰器不是函数会抛出ComposioInvalidModifierError定义于 ts/packages/core/src/errors/ToolErrors.ts。仓库中的真实示例见 ts/examples/modifiers/src/index.ts它对HACKERNEWS_GET_USER的inputParameters做了一次整体替换为userId补上type: string与描述const tools await composio.tools.get(default, HACKERNEWS_GET_USER, { modifySchema: ({ toolSlug, toolkitSlug, schema }) { if (toolSlug HACKERNEWS_GET_USER) { schema { ...schema, inputParameters: { type: object, properties: { ...schema.inputParameters?.properties, userId: { type: string, description: The user ID to get the user for, }, }, }, }; } return schema; }, });执行前修饰器改写工具入参执行前修饰器允许你在工具真正执行前修改输入参数典型用途包括参数校验、格式转换、注入默认值、埋点日志等。// 在工具执行前修改参数 const result await composio.tools.execute( GITHUB_GET_REPO, { userId: default, arguments: { owner: composio, repo: sdk, }, }, { beforeExecute: ({ toolSlug, toolkitSlug, params }) { // 将 owner 统一转为小写 if (params.arguments.owner) { params.arguments.owner params.arguments.owner.toLowerCase(); } // 为指定工具补充默认分支参数 if (toolSlug GITHUB_GET_REPO !params.arguments.branch) { params.arguments.branch main; } // 调试用追踪日志 console.log(Executing ${toolSlug} from ${toolkitSlug} toolkit); return params; }, } );从源码看Tools类的applyBeforeExecuteModifiers方法ts/packages/core/src/models/Tools.ts会先处理文件上传修饰器再执行你提供的beforeExecute执行前会检查AbortSignal是否已中止中止则抛出ComposioRequestCancelledError非函数修饰器同样抛出ComposioInvalidModifierError。也就是说beforeExecute支持返回Promise可以安全地在其中做异步预处理。执行后修饰器转换工具输出执行后修饰器允许你在工具执行完成后转换输出适合做数据格式化、敏感字段过滤、结果富化等。// 在工具执行后修改结果 const result await composio.tools.execute( GITHUB_GET_REPO, { userId: default, arguments: { owner: composio, repo: sdk, }, }, { afterExecute: ({ toolSlug, toolkitSlug, result }) { // 仅在执行成功时处理 if (result.successful) { // 过滤敏感数据 if (result.data.token) { delete result.data.token; } // 添加时间戳 result.data.fetchedAt new Date().toISOString(); // 转换数据格式 if (result.data.created_at) { result.data.createdAt new Date(result.data.created_at).toLocaleString(); delete result.data.created_at; } } return result; }, } );源码中的applyAfterExecuteModifiersts/packages/core/src/models/Tools.ts在工具已执行、可能已改动外部状态之后运行即使请求中途被中止SDK 也不会跳过afterExecute后处理避免留下「半成品」的成功形态数据AbortSignal仍会转发给文件下载流程以优雅降级。若启用了dangerouslyAllowAutoUploadDownloadFiles文件下载修饰器会先于你的afterExecute运行因此你的修饰器看到的是文件已下载后的最终结果。组合使用多个修饰器三类修饰器可以同时使用但请牢记执行类修饰器beforeExecute、afterExecute只对 Agentic Provider 生效。// 使用 Agentic Provider如 Vercel、Langchain const agenticProvider new VercelProvider(); const composio new Composio({ apiKey: your-api-key, provider: agenticProvider, }); // Agentic Provider 下所有修饰器均生效 const tools await composio.tools.get( default, { toolkits: [github], }, { // Schema 修饰器 modifySchema: ({ toolSlug, toolkitSlug, schema }) { // 增强工具 Schema return schema; }, // 执行前修饰器仅 Agentic Provider 生效 beforeExecute: ({ toolSlug, toolkitSlug, params }) { // 修改执行参数 return params; }, // 执行后修饰器仅 Agentic Provider 生效 afterExecute: ({ toolSlug, toolkitSlug, result }) { // 转换执行结果 return result; }, } ); // 使用非 Agentic Provider如 OpenAI const nonAgenticProvider new OpenAIProvider(); const composioNonAgentic new Composio({ apiKey: your-api-key, provider: nonAgenticProvider, }); // 非 Agentic Provider 下仅 Schema 修饰器生效 const openaiTools await composioNonAgentic.tools.get( default, { toolkits: [github], }, { // Schema 修饰器所有 Provider 均支持 modifySchema: (toolSlug, toolkitSlug, tool) { // 增强工具 Schema return tool; }, // 非 Agentic Provider 会忽略以下修饰器 beforeExecute: () {}, // 对非 Agentic Provider 无效果 afterExecute: () {}, // 对非 Agentic Provider 无效果 } );创建可复用的修饰器为获得更好的代码组织你可以把修饰器抽成独立函数再按需组合// 定义可复用的修饰器 const addTimestamps ({ toolSlug, toolkitSlug, result }) { if (result.successful) { result.data.executedAt new Date().toISOString(); } return result; }; const logExecutions ({ toolSlug, toolkitSlug, params }) { console.log(Executing ${toolSlug} from ${toolkitSlug} at ${new Date().toISOString()}); return params; }; const sanitizeInputs ({ toolSlug, toolkitSlug, params }) { // 清洗输入参数防范注入攻击 if (params.arguments) { Object.keys(params.arguments).forEach(key { if (typeof params.arguments[key] string) { params.arguments[key] sanitizeString(params.arguments[key]); } }); } return params; }; // 组合使用可复用修饰器 const result await composio.tools.execute( GITHUB_GET_REPO, { userId: default, arguments: { owner: composio, repo: sdk, }, }, { beforeExecute: sanitizeInputs, afterExecute: addTimestamps, } );修饰器回调支持同步返回与Promise返回类型定义中beforeExecuteModifier/afterExecuteModifier均标注为PromiseT | T因此可复用的修饰器内部也可以做异步逻辑例如读取配置、调用内部服务完成参数富化。实战场景对比 Agentic 与非 Agentic Provider 的用法下面这段完整对照示例源自仓库 ts/examples/modifiers/src/index.ts展示了不同 Provider 下修饰器用法的差异// 非 Agentic Provider 示例OpenAI const openai new Composio({ apiKey: process.env.COMPOSIO_API_KEY, provider: new OpenAIProvider(), }); // Schema 修饰器对非 Agentic Provider 生效 const nonAgenticTools await openai.tools.get(default, HACKERNEWS_GET_USER, { // Schema 修饰器适用于所有 Provider modifySchema: (toolSlug, toolkitSlug, tool) { // 自定义输入参数 if (tool.inputParameters?.properties?.userId) { tool.inputParameters.properties.userId.description HackerNews username (e.g., pg); } return tool; }, }); // Agentic Provider 示例Vercel const vercel new Composio({ apiKey: process.env.COMPOSIO_API_KEY, provider: new VercelProvider(), }); // Agentic Provider 下所有修饰器均生效 const agenticTools await vercel.tools.get(default, HACKERNEWS_GET_USER, { // Schema 修饰器 modifySchema: ({ toolSlug, toolkitSlug, schema }) { if (tool.inputParameters?.properties?.userId) { tool.inputParameters.properties.userId.description HackerNews username (e.g., pg); } return schema; }, // 执行类修饰器仅 Agentic Provider 生效 beforeExecute: ({ toolSlug, toolkitSlug, params }) { console.log(Executing ${toolSlug} from ${toolkitSlug}); return params; }, afterExecute: ({ toolSlug, toolkitSlug, result }) { if (result.successful) { result.data.processedAt new Date().toISOString(); } return result; }, });非 Agentic Provider 下手动应用执行修饰器对于 OpenAI 这类非 Agentic Provider你可以借助 Provider 的辅助方法手动应用执行修饰器// 获取 OpenAI Provider 实例 const openaiProvider composio.provider as OpenAIProvider; // 处理来自 OpenAI 的工具调用时 const toolOutputs await openaiProvider.handleToolCalls( default, // userId completion, // OpenAI completion 对象 { connectedAccountId: account_123 }, // 选项 { // 手动应用执行修饰器 beforeExecute: ({ toolSlug, toolkitSlug, params }) { console.log(Executing ${toolSlug}); return params; }, afterExecute: ({ toolSlug, toolkitSlug, result }) { result.data.processedAt new Date().toISOString(); return result; }, } );缓存工具结果仅 Agentic Provider注意该示例使用了执行类修饰器因此只适用于 Agentic Provider。// 简易内存缓存 const cache new Map(); const cacheModifier ({ toolSlug, toolkitSlug, params }) { // 基于工具 slug 与参数构建缓存键 const cacheKey ${toolSlug}-${JSON.stringify(params.arguments)}; // 把缓存键附加到 params供执行后修饰器使用 params.__cacheKey cacheKey; // 检查是否已有缓存 if (cache.has(cacheKey)) { const cachedResult cache.get(cacheKey); if (Date.now() - cachedResult.timestamp 60000) { // 1 分钟缓存 // 抛出特殊错误以短路执行 throw { __cached: true, result: cachedResult.result }; } } return params; }; const cacheAfterModifier ({ toolSlug, toolkitSlug, result }) { if (result.successful result.__cacheKey) { // 写入缓存 cache.set(result.__cacheKey, { result, timestamp: Date.now(), }); // 从结果中移除缓存键 delete result.__cacheKey; } return result; }; // 使用 try/catch 处理缓存命中 try { const result await composio.tools.execute( GITHUB_GET_REPO, { userId: default, arguments: { owner: composio, repo: sdk, }, }, { beforeExecute: cacheModifier, afterExecute: cacheAfterModifier, } ); console.log(Fresh result:, result); } catch (error) { if (error.__cached) { console.log(Cached result:, error.result); } else { throw error; // 真实错误则重新抛出 } }注入认证请求头借助beforeExecute可以为特定 toolkit 的工具统一注入认证参数例如自定义 API 的请求头const authModifier ({ toolSlug, toolkitSlug, params }) { // 为特定 toolkit 添加认证参数 if (toolkitSlug custom-api) { if (!params.customAuthParams) { params.customAuthParams {}; } if (!params.customAuthParams.parameters) { params.customAuthParams.parameters []; } // 向请求头添加 API Key params.customAuthParams.parameters.push({ in: header, name: X-API-Key, value: process.env.CUSTOM_API_KEY, }); } return params; }; // 将认证修饰器应用到某个 toolkit 的全部工具 const tools await composio.tools.get( default, { toolkits: [custom-api], }, { beforeExecute: authModifier, } );这与 SDK 内部applyBeforeExecuteModifiers的实现思路一致SDK 本身也在执行前通过FileToolModifierts/packages/core/src/models/Tools.ts注入文件上传逻辑你可以在同一个环节注入业务所需的认证、追踪等上下文。数据转换管道可以把多个执行后修饰器串联成一个管道实现「元数据附加 → 日期格式化 → 空值清理」的分步转换// 创建修饰器管道 const pipeModifiers (...modifiers) { return ({ toolSlug, toolkitSlug, result }) { return modifiers.reduce((modifiedResult, modifier) { return modifier({ toolSlug, toolkitSlug, result: modifiedResult }); }, result); }; }; // 各转换步骤 const addMetadata ({ toolSlug, toolkitSlug, result }) { if (result.successful) { result.data.metadata { source: toolkitSlug, tool: toolSlug }; } return result; }; const formatDates ({ toolSlug, toolkitSlug, result }) { if (result.successful) { // 将所有日期统一格式 Object.keys(result.data).forEach(key { if (key.includes(date) || key.includes(time) || key.endsWith(At)) { if (result.data[key]) { result.data[key] new Date(result.data[key]).toLocaleString(); } } }); } return result; }; const removeNulls ({ toolSlug, toolkitSlug, result }) { if (result.successful) { // 移除 null 与 undefined 值 Object.keys(result.data).forEach(key { if (result.data[key] null || result.data[key] undefined) { delete result.data[key]; } }); } return result; }; // 应用管道 const result await composio.tools.execute( GITHUB_GET_REPO, { userId: default, arguments: { owner: composio, repo: sdk, }, }, { afterExecute: pipeModifiers(addMetadata, formatDates, removeNulls), } );Provider 对修饰器的支持差异Composio SDK 将 Provider 分为两类Agentic Provider对工具执行拥有控制权如 Vercel、LangchainNon-Agentic Provider只负责格式化工具、不控制执行如 OpenAI。技术差异两类 Provider 的关键技术区别决定了它们支持的修饰器范围Agentic Provider的wrapTool/wrapTools方法会接收一个ExecuteToolFn参数从而把执行修饰器直接嵌入被包装的工具中。因此它们支持全部三类修饰器schema、beforeExecute、afterExecute。Non-Agentic Provider只是把工具格式化为外部可消费的形式不控制执行。工具被这类 Provider 包装后上下文即丢失无法自动应用执行修饰器因此只支持 Schema 修饰器。这一设计可以直接在源码中印证在 ts/packages/core/src/provider/BaseProvider.ts 中BaseNonAgenticProvider约 L134 起与BaseAgenticProvider约 L160 起是两条不同的抽象基类后者要求实现带executeTool: ExecuteToolFn参数的wrapTool(tool, executeTool)与wrapTools(tools, executeTool)OpenAIProvider则继承自BaseNonAgenticProvider见 ts/packages/core/src/provider/OpenAIProvider.ts。此外基类通过_setExecuteToolFn注册全局执行函数SDK 会调用composio.tools.execute将其绑定到Tools实例ts/packages/core/src/models/Tools.ts。// 非 Agentic Provider如 OpenAI——没有 executeToolFn 参数 class OpenAIProvider extends BaseNonAgenticProviderOpenAiToolCollection, OpenAiTool { // No executeToolFn parameter override wrapTool(tool: Tool): OpenAiTool { // 仅为 OpenAI 格式化工具 return { type: function, function: { name: tool.slug, description: tool.description, parameters: tool.inputParameters, }, }; } } // Agentic Provider如 Vercel——接收 executeToolFn 参数 class VercelProvider extends BaseAgenticProviderVercelToolCollection, VercelTool { // Receives executeToolFn parameter wrapTool(tool: Tool, executeTool: ExecuteToolFn): VercelTool { return tool({ description: tool.description, parameters: jsonSchema(tool.inputParameters ?? {}), execute: async params { // 通过 executeTool 应用修饰器 return await executeTool(tool.slug, params); }, }); } }非 Agentic Provider 的手动修饰器应用对 OpenAI 这类非 Agentic Provider仍可通过其辅助方法手动应用执行修饰器// 获取 OpenAI Provider 实例 const openaiProvider composio.provider as OpenAIProvider; // 带修饰器执行工具调用 const result await openaiProvider.executeToolCall( default, // userId toolCall, // OpenAI 工具调用对象 { connectedAccountId: account_123 }, // 选项 { // 手动应用执行修饰器 beforeExecute: ({ toolSlug, toolkitSlug, params }) { console.log(Executing ${toolSlug}); return params; }, afterExecute: ({ toolSlug, toolkitSlug, result }) { result.data.processedAt new Date().toISOString(); return result; }, } );从类型定义看ts/packages/core/src/types/modifiers.types.tsProviderOptionsTProvider会根据 Provider 是否继承BaseAgenticProvider自动解析为AgenticToolOptions含执行修饰器或ToolOptions仅 Schema 修饰器与beforeFileUpload这正是「不同 Provider 可用的修饰器不同」的类型级体现。Tool Router 会话修饰器v0.4.0Tool Router 会话支持带会话上下文的增强修饰器类型。这类修饰器的回调会额外获得sessionId非常适合跨用户会话追踪与管理工具执行。在 Tool Router 中使用会话修饰器import { Composio } from composio/core; import { SessionExecuteMetaModifiers } from composio/core; const composio new Composio(); const session await composio.create(user_123, { toolkits: [gmail, slack], }); // 使用会话级修饰器 const tools await session.tools({ modifySchema: ({ toolSlug, toolkitSlug, schema }) { // 定制工具 Schema console.log(Modifying schema for ${toolSlug} from ${toolkitSlug}); return schema; }, beforeExecute: ({ toolSlug, toolkitSlug, sessionId, params }) { // 访问会话 ID 用于追踪 console.log([Session: ${sessionId}] Executing ${toolSlug} from ${toolkitSlug}); // 添加会话级元数据 params.sessionMetadata { sessionId, timestamp: new Date().toISOString(), }; return params; }, afterExecute: ({ toolSlug, toolkitSlug, sessionId, result }) { // 带会话上下文转换结果 console.log([Session: ${sessionId}] Completed ${toolSlug}); if (result.successful) { result.data.sessionId sessionId; result.data.executedAt new Date().toISOString(); } return result; }, });对应类型定义beforeExecuteMetaModifier/afterExecuteMetaModifier与SessionExecuteMetaModifiers均位于 ts/packages/core/src/types/modifiers.types.ts。其中参数类型为MetaToolArguments由ToolExecuteMetaParams[arguments]派生、保证非空且帮助类工具使用composio作为 toolkit slug预加载的应用工具则使用各自的 toolkit slug如有。会话级修饰器的价值会话追踪Session Tracking通过sessionId追踪哪个会话执行了哪些工具用户上下文User Context通过会话 ID 将工具执行与具体用户关联审计日志Audit Logging带会话上下文记录工具执行满足合规与调试需求性能监控Performance Monitoring按会话跟踪工具执行性能。直接获取元工具Meta Tools你还可以用getRawToolRouterMetaTools直接获取 Tool Router 的元工具并对其 Schema 应用修饰器import { Composio } from composio/core; const composio new Composio(); // 获取会话的原始元工具 const metaTools await composio.tools.getRawToolRouterMetaTools(session_123, { modifySchema: ({ toolSlug, toolkitSlug, schema }) { // 定制元工具 Schema if (toolSlug composio_authorize_toolkit) { schema.description Custom description for authorization; } return schema; } }); // 配合你的 AI 框架使用元工具 console.log(Available meta tools:, metaTools.map(t t.name));类型定义速查以下类型均可在 ts/packages/core/src/types/modifiers.types.ts 中找到完整实现// Schema 修饰器所有 Provider 支持 type TransformToolSchemaModifier (toolSlug: string, toolkitSlug: string, tool: Tool) Tool; // 执行前修饰器仅 Agentic Provider 支持 type beforeExecuteModifier ( toolSlug: string, toolkitSlug: string, params: ToolExecuteParams ) ToolExecuteParams; // 执行后修饰器仅 Agentic Provider 支持 type afterExecuteModifier ( toolSlug: string, toolkitSlug: string, result: ToolExecuteResponse ) ToolExecuteResponse; // 修饰器对象 interface ExecuteToolModifiers { beforeExecute?: beforeExecuteModifier; afterExecute?: afterExecuteModifier; } // Provider 选项 interface ProviderOptionsTProvider { modifySchema?: TransformToolSchemaModifier; beforeExecute?: beforeExecuteModifier; // 仅由 Agentic Provider 应用 afterExecute?: afterExecuteModifier; // 仅由 Agentic Provider 应用 } // 会话级修饰器v0.4.0 interface SessionExecuteMetaModifiers { modifySchema?: (context: { toolSlug: string; toolkitSlug: string; schema: any; }) any; beforeExecute?: (context: { toolSlug: string; toolkitSlug: string; sessionId: string; params: any; }) any; afterExecute?: (context: { toolSlug: string; toolkitSlug: string; sessionId: string; result: any; }) any; } // 会话元工具选项v0.4.0 interface SessionMetaToolOptions { modifySchema?: (context: { toolSlug: string; toolkitSlug: string; schema: any; }) any; }需要补充说明的是在最新源码中修饰器回调已统一为「接收单个 context 对象」的签名如({ toolSlug, toolkitSlug, params })并支持返回Promise见 ts/packages/core/src/types/modifiers.types.ts 中beforeExecuteModifier、afterExecuteModifier、TransformToolSchemaModifier的定义ExecuteToolModifiers与ToolOptions还额外包含beforeFileUpload钩子用于在上传文件前改写路径、重定向 URL 或中止上传返回false会触发ComposioFileUploadAbortedErrorAgenticToolOptions ToolOptions ExecuteToolModifiers则把 Schema 修饰与执行修饰统一到 Agentic 场景下。总结与选型建议所有 Provider都可以用modifySchema定制工具的「说明书」让 LLM 更准确地理解参数含义或隐藏/简化内部参数Agentic ProviderVercel、Langchain 等还能用beforeExecute/afterExecute实现参数归一化、默认值注入、认证头注入、结果格式化、缓存与审计且这类修饰器会被 SDK 嵌入包装后的工具自动触发Non-Agentic ProviderOpenAI 等需要借助executeToolCall/handleToolCalls等辅助方法手动应用执行修饰器Tool Router 会话v0.4.0提供了带sessionId的SessionExecuteMetaModifiers适合按会话追踪、审计与性能监控。编写修饰器时建议保持单一职责、复用与管道化如pipeModifiers并利用 TypeScript 的类型提示确保签名正确。更多完整可运行示例可继续参考 ts/examples/modifiers/src/index.ts 与核心类型定义 ts/packages/core/src/types/modifiers.types.ts。【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →