Skip to content

feat(kit): support runtime tool providers - #365

Merged
SonyLeo merged 3 commits into
opentiny:developfrom
gene9831:codex/skill-tool-runtime
Jun 29, 2026
Merged

SonyLeo merged 3 commits into
opentiny:developfrom
gene9831:codex/skill-tool-runtime

Conversation

@gene9831

@gene9831 gene9831 commented Jun 17, 2026 •

Copy link
Copy Markdown
Collaborator

PR 描述

为 message 工具调用增加 runtime tool provider 能力,让插件可以在请求前动态提供工具 schema,并可选绑定本地 handler 直接处理对应 tool call。

主要改动:

  • toolPlugin 支持 RuntimeTool,通过 function.name 路由到对应 handler。
  • 支持其他插件通过 provideTools(context) 暴露工具,由 toolPlugin 统一收集。
  • 为 tool call context 增加 toolSource,标记工具来自 toolPlugin、外部 provider 或未知来源。
  • 请求体支持合并已有 tools,并检测重复工具名。
  • 增加 toolPlugin 关键流程测试。
  • 更新 Vue message mock/tool plugin 适配和 message 文档。

验证:

pnpm -F @opentiny/tiny-robot-kit test -- --run src/message/test/toolPlugin.test.ts
pnpm build

Summary by CodeRabbit

Summary by CodeRabbit

Release Notes

  • Documentation
    • Updated toolPlugin documentation with tool aggregation, tool source tracking, and updated callback/context guidance.
  • New Features
    • Added support for runtime tools with custom handlers.
    • Added tool source labeling and propagation through tool discovery and tool-call lifecycle.
    • Added dynamic tool injection via tool providers.
  • Improvements
    • Plugin hooks now receive access to the active plugin list.
    • Tool arrays are merged into existing request tools.
    • Unified streaming/asynchronous result typing for response providers and tool calls.
  • Tests
    • Added end-to-end coverage for runtime tool execution, conflicts, and fallback behavior.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 679f04d0-f757-4eb7-80ad-bf31b1911824

📥 Commits

Reviewing files that changed from the base of the PR and between 10dfcb8 and 380e550.

📒 Files selected for processing (4)
  • docs/src/tools/message.md
  • packages/kit/src/message/plugins/toolPlugin.ts
  • packages/kit/src/message/test/toolPlugin.test.ts
  • packages/kit/src/message/types.ts
 _______________________________________________
< Don't count your bugs before they're patched. >
 -----------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

Walkthrough

toolPlugin gains runtime tool support: new ToolSource, RuntimeTool, ToolProvider, and ToolProviderItem types enable plugins to expose executable tools via provideTools. A new resolveTools routine aggregates and deduplicates tools from all plugins, builds dispatch maps, and wires toolSource into every tool-call context. The Vue wrapper, tests, and docs are updated accordingly.

Changes

Runtime Tool Provisioning and toolSource Tracking

Layer / File(s) Summary
Async/streaming result type abstraction
packages/kit/src/types.ts, packages/kit/src/message/types.ts, packages/kit/src/message/utils.ts, packages/kit/src/vue/message/types.ts
Introduces AsyncStreamableResult<T> (union of Promise, AsyncGenerator, and Promise of AsyncGenerator) and redefines MaybeStreamableResult<T> to include direct values T. Updates ResponseProvider return types in core and Vue to use the unified abstraction; updates normalizeToAsyncGenerator to accept the broader result type.
Tool type contracts and BasePluginContext.plugins
packages/kit/src/message/plugins/toolPlugin.ts, packages/kit/src/message/types.ts, packages/kit/src/message/core/engine.ts, packages/kit/src/message/plugins/index.ts
Introduces ToolSource, ToolCallContext (extended with toolSource), RuntimeTool (with executable handler), ToolProviderItem, and ToolProvider types. Extends MessageRequestBody with optional tools field and BasePluginContext with plugins: readonly MessageEnginePlugin[]. Updates OpenAI import paths and re-exports new types via plugins index.
resolveTools routine and per-call dispatch
packages/kit/src/message/plugins/toolPlugin.ts
Updates getTools to accept BasePluginContext and return ToolProviderItem[]; updates callTool return type to MaybeStreamableResult<string | Record<string, any>>. Implements resolveTools to aggregate tools from plugins via provideTools(context) and own getTools(context), enforce tool-name uniqueness, and build runtimeToolMap/toolSourceMap. Updates onBeforeRequest to merge with existing requestBody.tools; onAfterRequest to re-resolve; and per-call execution to route to runtime handler or callTool fallback based on toolSourceMap.
Vue wrapper: toolSource threading and getTools context
packages/kit/src/vue/message/plugins/toolPlugin.ts
Extends UseMessageToolActionContext with optional toolSource and UseMessageToolCallContext with required toolSource. Updates getTools option to accept BasePluginContext and return MaybePromise<ToolProviderItem[]>. Forwards Vue base context to getTools and passes toolSource: context.toolSource into callTool, onToolCallStart, and onToolCallEnd handlers.
toolPlugin test suite
packages/kit/src/message/test/toolPlugin.test.ts
Adds Vitest suite with four scenarios: runtime tool execution suppresses fallback callTool; duplicate tool-name detection within provided definitions; conflicts with requestBody.tools; and cross-plugin tool loading with correct toolSource metadata.
Documentation and import path updates
docs/src/tools/message.md, packages/kit/src/message/test/mockResponseProvider.ts, packages/kit/src/vue/message/mockResponseProvider.ts
Updates toolPlugin docs with ToolProvider protocol, toolSource descriptions, callback context tables, and updated example signature. Fixes ChatCompletionChunk import paths to openai/resources.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐇 A rabbit hops into the plugin bazaar,
Each tool now knows from whence and where it are—
toolSource stamped on every call's context,
Runtime handlers leap before the rest.
provideTools, resolveTools, maps all aligned,
No duplicate names shall tangle your mind!
✨ The warren of tools is tidy and kind.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and accurately describes the main change: introducing runtime tool provider support to the kit module.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/src/tools/message.md`:
- Around line 256-257: Update the type signatures in the options table for both
getTools and callTool methods to match the actual plugin contract. For getTools,
add the BasePluginContext parameter to the function signature and change the
return type from Promise<Array<Tool | RuntimeTool>> to
MaybePromise<ToolProviderItem[]> to correctly indicate that it can return either
a synchronous or asynchronous result. For callTool, update the return type to
reflect that it can return either a synchronous value (string | Record<string,
any>) or an async/streaming value, rather than only showing the async variant.
These changes will ensure the documentation accurately represents what the
actual plugin implementation expects and returns.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4530f256-4fdc-43db-821b-fbe8351c3f94

📥 Commits

Reviewing files that changed from the base of the PR and between 045d26b and c5b3db3.

📒 Files selected for processing (10)
  • docs/src/tools/message.md
  • packages/kit/src/message/core/engine.ts
  • packages/kit/src/message/plugins/index.ts
  • packages/kit/src/message/plugins/toolPlugin.ts
  • packages/kit/src/message/test/mockResponseProvider.ts
  • packages/kit/src/message/test/toolPlugin.test.ts
  • packages/kit/src/message/types.ts
  • packages/kit/src/message/utils.ts
  • packages/kit/src/vue/message/mockResponseProvider.ts
  • packages/kit/src/vue/message/plugins/toolPlugin.ts

Comment thread docs/src/tools/message.md Resolved
@github-actions

github-actions Bot commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

✅ Preview build completed successfully!

Click the image above to preview.
Preview will be automatically removed when this PR is closed.

@github-actions

github-actions Bot commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

@SonyLeo

SonyLeo commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

问题 1:runtime tool 解析两次,执行映射可能不一致

涉及文件:

  • packages/kit/src/message/plugins/toolPlugin.ts
  • packages/kit/src/message/types.ts
  • packages/kit/src/message/test/toolPlugin.test.ts
  • docs/src/tools/message.md

问题点:

  • toolPlugin.ts:318-324 在 onBeforeRequest 中调用 resolveTools(),把工具列表写入 requestBody.tools。
  • toolPlugin.ts:351 在 onAfterRequest 中再次调用 resolveTools(),重新生成 runtimeToolMap/toolSourceMap。
  • toolPlugin.ts:382-386 根据第二次生成的 runtimeToolMap 决定走 runtime handler 还是 fallback callTool。

风险:

  • 模型看到的是第一次解析的工具。
  • 实际执行时用的是第二次解析的 handler map。
  • 如果 getTools/provideTools 是动态的,两次结果不一致,模型返回的 tool call 可能找不到对应 runtime handler,错误地 fallback 到 callTool。

实际场景:

  • 根据用户权限动态提供工具。
  • 根据 skill/MCP 当前状态动态暴露工具。
  • runtime handler 闭包持有本轮临时资源。
  • provider 从远端注册表、缓存、feature flag 获取工具列表。

验证方法:

在 packages/kit/src/message/test/toolPlugin.test.ts 添加测试:

it('keeps runtime tool handlers stable for the tool list sent to the model', async () => {
  const runtimeCall = vi.fn(() => 'runtime result')
  const fallbackCall = vi.fn(() => 'fallback result')
  const runtimeTool: RuntimeTool = {
    tool: {
      type: 'function',
      function: {
        name: 'volatile_runtime_tool',
        description: 'Runtime tool that is only available during request preparation',
      },
    },
    handler: runtimeCall,
  }

  let getToolsCalls = 0

  const engine = createTestMessageEngine({
    plugins: [
      ...silentDefaultPlugins,
      toolPlugin({
        getTools: async () => {
          getToolsCalls++
          return getToolsCalls === 1 ? [runtimeTool] : []
        },
        callTool: fallbackCall,
      }),
    ],
    responseProvider,
  })

  await engine.sendMessage('call volatile tool')

  expect(runtimeCall).toHaveBeenCalledOnce()
  expect(fallbackCall).not.toHaveBeenCalled()
})

当前结果:

  • 失败:runtimeCall 实际 0 次。
  • 说明:第二次 resolveTools() 返回空数组,导致 runtimeToolMap 没有 volatile_runtime_tool。

建议修复方向:

  • onBeforeRequest 解析后缓存本次 runtimeToolMap/toolSourceMap。
  • onAfterRequest 处理本次模型返回的 tool_calls 时复用该快照。
  • 不要在执行阶段重新解析工具集合。

问题 2:OpenAI tool 类型被收窄,custom tool 会类型不兼容且运行时报错

涉及文件:

  • packages/kit/src/message/types.ts
  • packages/kit/src/message/plugins/toolPlugin.ts
  • packages/kit/src/message/test/toolPlugin.test.ts

问题点:

  • message/types.ts:36 定义为:
tools?: Array<ChatCompletionFunctionTool>

但 OpenAI 类型实际是:

ChatCompletionTool = ChatCompletionFunctionTool | ChatCompletionCustomTool
tools?: Array<ChatCompletionTool>
  • toolPlugin.ts:244 的 existingTools 也被当成 ChatCompletionFunctionTool[]。
  • toolPlugin.ts:274-286 的 registerToolName() 直接读取:
tool.function.name

custom tool 没有 function 字段,因此运行时报错。

验证方法 1:类型验证

在测试中构造合法 ChatCompletionTool custom tool:

const customTool = {
  type: 'custom',
  custom: {
    name: 'custom_formatter',
    description: 'Format with custom grammar',
    format: {
      type: 'grammar',
      grammar: {
        syntax: 'lark',
        definition: 'start: "ok"',
      },
    },
  },
} satisfies ChatCompletionTool

然后赋值:

context.requestBody.tools = [customTool]

运行:

pnpm.cmd -F @opentiny/tiny-robot-kit exec tsc --noEmit

当前结果:

Type 'ChatCompletionTool' is not assignable to type 'ChatCompletionFunctionTool'.
Property 'function' is missing in type 'ChatCompletionCustomTool'

验证方法 2:运行时验证

添加测试:

it('keeps custom tools already present on the request body', async () => {
  const customTool = {
    type: 'custom',
    custom: {
      name: 'custom_formatter',
      description: 'Format with custom grammar',
      format: {
        type: 'grammar',
        grammar: {
          syntax: 'lark',
          definition: 'start: "ok"',
        },
      },
    },
  } satisfies ChatCompletionTool

  const engine = createTestMessageEngine({
    plugins: [
      ...silentDefaultPlugins,
      {
        name: 'custom-tool-plugin',
        onBeforeRequest: (context) => {
          context.requestBody.tools = [customTool]
        },
      },
      toolPlugin({
        getTools: async () => [],
        callTool: async () => 'fallback',
      }),
    ],
    responseProvider,
  })

  await engine.sendMessage('use custom tool')
})

当前结果:

Cannot read properties of undefined (reading 'name')

定位:

packages/kit/src/message/plugins/toolPlugin.ts:275

建议修复方向:

  • MessageRequestBody.tools 改回兼容 OpenAI 的 ChatCompletionTool[]。
  • existingTools 也使用 ChatCompletionTool[]。
  • duplicate name 检查和 runtime routing 只处理 function tool。
  • custom tool 保留在 requestBody.tools,但不进入 runtimeToolMap。

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@SonyLeo
SonyLeo merged commit c8f9549 into opentiny:develop Jun 29, 2026
4 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🧹 Preview Cleaned Up

The preview deployment has been removed.

@gene9831
gene9831 deleted the codex/skill-tool-runtime branch September 9, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants