Skip to content

Bug: wrapAISDK breaks class based tools #2048

@jgeurts

Description

@jgeurts

Overview

The _wrapTools function in wrapAISDK is incompatible with class-based tools because it uses the spread operator ({ ...tool }), which loses prototype methods and breaks the this context.

// Current implementation in _wrapTools
wrappedTools[key] = { ...tool };  // Loses prototype methods
if ("execute" in wrappedTools[key]) {
  wrappedTools[key].execute = traceable(
    wrappedTools[key].execute.bind(wrappedTools[key]),  // Rebinds to plain object
    // ...
  );
}

Problems:

  1. Spread operator creates a shallow copy with only own properties, losing prototype methods
  2. Rebinding execute to the spread object breaks this context for class instances

Reproducible Example

class MyTool {
  async execute(params: any) {
    return this.helperMethod();  // `this.helperMethod` is undefined after wrapping
  }

  helperMethod() {
    return "result";
  }
}

const { streamText } = wrapAISDK(ai);
await streamText({
  tools: { myTool: new MyTool() }  // Breaks
});

Suggested Fix

Create a non-mutating copy that preserves class instances while wrapping execute with tracing functionality.

function _wrapTools(tools, toolConfig) {
  const wrappedTools = {};
  if (tools) {
    for (const [key, tool] of Object.entries(tools)) {
      // Create new object with same prototype chain and own properties
      wrappedTools[key] = Object.assign(
        Object.create(Object.getPrototypeOf(tool)),
        tool
      );

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions