Skip to content

ToolComponentResolver 工具组件解析器

ToolComponentResolver 是一个用于动态解析和管理工具组件的核心类,它提供了灵活的组件注册、解析和管理机制,主要用于聊天应用中的工具组件动态渲染。

安装和导入

typescript
import {
  ToolComponentResolver,
  defaultToolResolver,
  resolveToolComponent,
  type ToolComponentMap,
} from "@pt/common-ui/utils";

类型定义

ToolComponentMap

工具组件映射接口,定义了工具名称到 Vue 组件的映射关系。

typescript
interface ToolComponentMap {
  [key: string]: Component;
}

API 文档

ToolComponentResolver 类

工具组件解析器的主要类,提供完整的组件管理功能。

构造函数

typescript
constructor(
  customComponents: ToolComponentMap = {},
  fallbackComponent: Component = UnmatchedTool
)
参数
  • customComponents (ToolComponentMap, 可选): 自定义工具组件映射,默认为空对象
  • fallbackComponent (Component, 可选): 当找不到匹配组件时使用的回退组件,默认为 UnmatchedTool
示例
typescript
import { ToolComponentResolver } from "@pt/common-ui/utils";
import MyCustomTool from "./MyCustomTool.vue";
import MyFallback from "./MyFallback.vue";

// 使用默认配置
const resolver = new ToolComponentResolver();

// 使用自定义组件
const customResolver = new ToolComponentResolver(
  {
    MyCustomTool,
    AnotherTool: MyCustomTool,
  },
  MyFallback
);

register(name, component)

注册单个工具组件。

typescript
register(name: string, component: Component): void
参数
  • name (string): 工具名称,用作组件标识符
  • component (Component): Vue 组件
示例
typescript
import CustomSearchTool from "./CustomSearchTool.vue";

const resolver = new ToolComponentResolver();
resolver.register("CustomSearch", CustomSearchTool);

registerBatch(components)

批量注册多个工具组件。

typescript
registerBatch(components: ToolComponentMap): void
参数
  • components (ToolComponentMap): 包含多个工具组件的映射对象
示例
typescript
import SearchTool from "./SearchTool.vue";
import CalculatorTool from "./CalculatorTool.vue";
import WeatherTool from "./WeatherTool.vue";

const resolver = new ToolComponentResolver();
resolver.registerBatch({
  Search: SearchTool,
  Calculator: CalculatorTool,
  Weather: WeatherTool,
});

unregister(name)

注销指定的工具组件。

typescript
unregister(name: string): void
参数
  • name (string): 要注销的工具名称
示例
typescript
const resolver = new ToolComponentResolver();
resolver.register("TempTool", SomeTempComponent);

// 稍后注销
resolver.unregister("TempTool");

resolve(toolPart)

解析工具组件,根据工具部分对象返回对应的 Vue 组件。

typescript
resolve(toolPart: any): Component
参数
  • toolPart (any): 工具部分对象,包含工具的名称和类别信息
返回值
  • Component: 匹配的 Vue 组件,如果找不到匹配的组件则返回回退组件
解析规则
  1. 如果 toolPart.category === 'workflow',返回 Workflow 组件
  2. 如果 toolPart.category === 'external',返回 ExternalSkill 组件
  3. 否则使用 toolPart.name 作为组件名称进行匹配
示例
typescript
const resolver = new ToolComponentResolver();

// 普通工具
const toolPart1 = {
  name: "WebFetch",
  input: { url: "https://example.com" },
};
const component1 = resolver.resolve(toolPart1); // 返回 WebFetch 组件

// 工作流工具
const toolPart2 = {
  category: "workflow",
  name: "MyWorkflow",
};
const component2 = resolver.resolve(toolPart2); // 返回 Workflow 组件

// 外部技能工具
const toolPart3 = {
  category: "external",
  name: "CustomSkill",
};
const component3 = resolver.resolve(toolPart3); // 返回 ExternalSkill 组件

// 未知工具
const toolPart4 = {
  name: "UnknownTool",
};
const component4 = resolver.resolve(toolPart4); // 返回回退组件 (UnmatchedTool)

getRegisteredTools()

获取所有已注册的工具组件名称。

typescript
getRegisteredTools(): string[]
返回值
  • string[]: 包含所有已注册工具名称的数组
示例
typescript
const resolver = new ToolComponentResolver();
resolver.register("CustomTool", SomeComponent);

const tools = resolver.getRegisteredTools();
console.log(tools); // ['AdvancedSearch', 'PythonCodeSandbox', 'TodoRead', ..., 'CustomTool']

isRegistered(name)

检查指定的工具组件是否已注册。

typescript
isRegistered(name: string): boolean
参数
  • name (string): 要检查的工具名称
返回值
  • boolean: 如果工具已注册返回 true,否则返回 false
示例
typescript
const resolver = new ToolComponentResolver();

console.log(resolver.isRegistered("WebFetch")); // true (默认工具)
console.log(resolver.isRegistered("CustomTool")); // false

resolver.register("CustomTool", SomeComponent);
console.log(resolver.isRegistered("CustomTool")); // true

默认工具组件

系统预置了以下默认工具组件:

  • AdvancedSearch: 高级搜索工具
  • PythonCodeSandbox: Python 代码沙箱
  • TodoRead: 待办事项读取工具
  • TodoWrite: 待办事项写入工具
  • Task: 任务管理工具
  • WebFetch: 网页抓取工具
  • SqlExecute: SQL 执行工具
  • ExternalSkill: 外部技能工具
  • ExitPlan: 退出计划工具
  • Think: 思考工具
  • Thinking: 思考过程工具

默认解析器实例

typescript
export const defaultToolResolver: ToolComponentResolver;

预配置的默认工具解析器实例,包含所有默认工具组件。

示例
typescript
import { defaultToolResolver } from "@pt/common-ui/utils";

// 直接使用默认解析器
const component = defaultToolResolver.resolve({
  name: "WebFetch",
  input: { url: "https://example.com" },
});

// 扩展默认解析器
defaultToolResolver.register("MyCustomTool", MyCustomComponent);

resolveToolComponent 便捷函数

快速解析工具组件的便捷函数,内部使用默认解析器。

typescript
function resolveToolComponent(toolPart: any): Component;
参数
  • toolPart (any): 工具部分对象
返回值
  • Component: 解析得到的 Vue 组件
示例
typescript
import { resolveToolComponent } from "@pt/common-ui/utils";

const toolPart = {
  name: "PythonCodeSandbox",
  input: { code: "print('Hello, World!')" },
};

const component = resolveToolComponent(toolPart);

使用场景

1. 聊天应用中的工具渲染

typescript
// ChatMessage.vue
<template>
  <div class="chat-message">
    <div v-for="part in message.parts" :key="part.id">
      <component
        v-if="part.type === 'tool_use'"
        :is="resolveToolComponent(part)"
        v-bind="getToolProps(part, message.parts, { message_id: message.id })"
      />
      <div v-else-if="part.type === 'text'" class="text-content">
        {{ part.text }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { resolveToolComponent, getToolProps } from "@pt/common-ui/utils";

interface Props {
  message: {
    id: string;
    parts: any[];
  };
}

defineProps<Props>();
</script>

2. 自定义工具组件管理

typescript
// ToolManager.ts
import { ToolComponentResolver } from "@pt/common-ui/utils";
import CustomAnalyticsTool from "./tools/CustomAnalyticsTool.vue";
import CustomReportTool from "./tools/CustomReportTool.vue";

class ToolManager {
  private resolver: ToolComponentResolver;

  constructor() {
    this.resolver = new ToolComponentResolver();
    this.initializeCustomTools();
  }

  private initializeCustomTools() {
    // 注册自定义工具
    this.resolver.registerBatch({
      Analytics: CustomAnalyticsTool,
      Report: CustomReportTool,
    });
  }

  // 动态加载工具
  async loadTool(toolName: string, toolModule: string) {
    try {
      const module = await import(`./tools/${toolModule}.vue`);
      this.resolver.register(toolName, module.default);
      return true;
    } catch (error) {
      console.error(`Failed to load tool ${toolName}:`, error);
      return false;
    }
  }

  // 获取工具组件
  getToolComponent(toolPart: any) {
    return this.resolver.resolve(toolPart);
  }

  // 获取可用工具列表
  getAvailableTools() {
    return this.resolver.getRegisteredTools();
  }
}

export const toolManager = new ToolManager();

3. 插件化工具系统

typescript
// PluginSystem.ts
import { ToolComponentResolver } from "@pt/common-ui/utils";

interface ToolPlugin {
  name: string;
  component: Component;
  version: string;
  dependencies?: string[];
}

class PluginSystem {
  private resolver: ToolComponentResolver;
  private plugins: Map<string, ToolPlugin> = new Map();

  constructor() {
    this.resolver = new ToolComponentResolver();
  }

  // 安装插件
  installPlugin(plugin: ToolPlugin): boolean {
    try {
      // 检查依赖
      if (plugin.dependencies) {
        for (const dep of plugin.dependencies) {
          if (!this.plugins.has(dep)) {
            throw new Error(`Missing dependency: ${dep}`);
          }
        }
      }

      // 注册组件
      this.resolver.register(plugin.name, plugin.component);
      this.plugins.set(plugin.name, plugin);

      console.log(
        `Plugin ${plugin.name} v${plugin.version} installed successfully`
      );
      return true;
    } catch (error) {
      console.error(`Failed to install plugin ${plugin.name}:`, error);
      return false;
    }
  }

  // 卸载插件
  uninstallPlugin(pluginName: string): boolean {
    try {
      // 检查是否有其他插件依赖此插件
      for (const [name, plugin] of this.plugins) {
        if (plugin.dependencies?.includes(pluginName)) {
          throw new Error(
            `Cannot uninstall ${pluginName}: ${name} depends on it`
          );
        }
      }

      this.resolver.unregister(pluginName);
      this.plugins.delete(pluginName);

      console.log(`Plugin ${pluginName} uninstalled successfully`);
      return true;
    } catch (error) {
      console.error(`Failed to uninstall plugin ${pluginName}:`, error);
      return false;
    }
  }

  // 获取插件信息
  getPluginInfo(pluginName: string): ToolPlugin | undefined {
    return this.plugins.get(pluginName);
  }

  // 列出所有插件
  listPlugins(): ToolPlugin[] {
    return Array.from(this.plugins.values());
  }

  // 解析工具组件
  resolveComponent(toolPart: any) {
    return this.resolver.resolve(toolPart);
  }
}

// 使用示例
const pluginSystem = new PluginSystem();

// 安装自定义插件
pluginSystem.installPlugin({
  name: "WeatherTool",
  component: WeatherToolComponent,
  version: "1.0.0",
  dependencies: ["HttpClient"],
});

4. 条件工具加载

typescript
// ConditionalToolLoader.ts
import { ToolComponentResolver } from "@pt/common-ui/utils";

class ConditionalToolLoader {
  private resolver: ToolComponentResolver;
  private userPermissions: Set<string>;

  constructor(permissions: string[] = []) {
    this.resolver = new ToolComponentResolver();
    this.userPermissions = new Set(permissions);
    this.loadToolsBasedOnPermissions();
  }

  private loadToolsBasedOnPermissions() {
    // 根据权限加载不同的工具
    if (this.userPermissions.has("admin")) {
      this.loadAdminTools();
    }

    if (this.userPermissions.has("developer")) {
      this.loadDeveloperTools();
    }

    if (this.userPermissions.has("analyst")) {
      this.loadAnalystTools();
    }
  }

  private async loadAdminTools() {
    const AdminPanel = await import("./tools/AdminPanel.vue");
    const SystemMonitor = await import("./tools/SystemMonitor.vue");

    this.resolver.registerBatch({
      AdminPanel: AdminPanel.default,
      SystemMonitor: SystemMonitor.default,
    });
  }

  private async loadDeveloperTools() {
    const CodeEditor = await import("./tools/CodeEditor.vue");
    const APITester = await import("./tools/APITester.vue");

    this.resolver.registerBatch({
      CodeEditor: CodeEditor.default,
      APITester: APITester.default,
    });
  }

  private async loadAnalystTools() {
    const DataVisualizer = await import("./tools/DataVisualizer.vue");
    const ReportGenerator = await import("./tools/ReportGenerator.vue");

    this.resolver.registerBatch({
      DataVisualizer: DataVisualizer.default,
      ReportGenerator: ReportGenerator.default,
    });
  }

  // 检查用户是否有权限使用某个工具
  canUseTool(toolName: string): boolean {
    return this.resolver.isRegistered(toolName);
  }

  // 获取用户可用的工具列表
  getAvailableTools(): string[] {
    return this.resolver.getRegisteredTools();
  }

  // 解析工具组件
  resolveComponent(toolPart: any) {
    const component = this.resolver.resolve(toolPart);

    // 如果返回的是回退组件,说明用户没有权限或工具不存在
    if (component === this.resolver["fallbackComponent"]) {
      console.warn(`Tool ${toolPart.name} not available for current user`);
    }

    return component;
  }
}

// 使用示例
const userPermissions = ["developer", "analyst"];
const conditionalLoader = new ConditionalToolLoader(userPermissions);

// 检查工具可用性
console.log(conditionalLoader.canUseTool("CodeEditor")); // true
console.log(conditionalLoader.canUseTool("AdminPanel")); // false

高级用法

1. 工具组件懒加载

typescript
// LazyToolResolver.ts
import { ToolComponentResolver, type ToolComponentMap } from "@pt/common-ui/utils";

class LazyToolResolver extends ToolComponentResolver {
  private lazyComponents: Map<string, () => Promise<any>> = new Map();

  // 注册懒加载组件
  registerLazy(name: string, loader: () => Promise<any>) {
    this.lazyComponents.set(name, loader);
  }

  // 重写 resolve 方法以支持懒加载
  async resolveAsync(toolPart: any): Promise<Component> {
    const name = this.getToolName(toolPart);

    // 检查是否已经加载
    if (this.isRegistered(name)) {
      return this.resolve(toolPart);
    }

    // 检查是否有懒加载定义
    const loader = this.lazyComponents.get(name);
    if (loader) {
      try {
        const module = await loader();
        const component = module.default || module;
        this.register(name, component);
        return component;
      } catch (error) {
        console.error(`Failed to load lazy component ${name}:`, error);
        return this.fallbackComponent;
      }
    }

    return this.fallbackComponent;
  }

  // 预加载指定工具
  async preload(toolNames: string[]): Promise<void> {
    const promises = toolNames.map(async (name) => {
      const loader = this.lazyComponents.get(name);
      if (loader && !this.isRegistered(name)) {
        try {
          const module = await loader();
          const component = module.default || module;
          this.register(name, component);
        } catch (error) {
          console.error(`Failed to preload component ${name}:`, error);
        }
      }
    });

    await Promise.all(promises);
  }
}

// 使用示例
const lazyResolver = new LazyToolResolver();

// 注册懒加载组件
lazyResolver.registerLazy('HeavyAnalyticsTool', () =>
  import('./tools/HeavyAnalyticsTool.vue')
);

lazyResolver.registerLazy('ComplexVisualization', () =>
  import('./tools/ComplexVisualization.vue')
);

// 异步解析
const component = await lazyResolver.resolveAsync({
  name: 'HeavyAnalyticsTool',
  input: { data: [...] }
});

// 预加载关键工具
await lazyResolver.preload(['HeavyAnalyticsTool', 'ComplexVisualization']);

2. 工具组件版本管理

typescript
// VersionedToolResolver.ts
interface VersionedComponent {
  component: Component;
  version: string;
  deprecated?: boolean;
  migration?: string;
}

class VersionedToolResolver extends ToolComponentResolver {
  private versionedComponents: Map<string, Map<string, VersionedComponent>> = new Map();
  private defaultVersions: Map<string, string> = new Map();

  // 注册版本化组件
  registerVersioned(
    name: string,
    version: string,
    component: Component,
    options: { deprecated?: boolean; migration?: string } = {}
  ) {
    if (!this.versionedComponents.has(name)) {
      this.versionedComponents.set(name, new Map());
    }

    const versions = this.versionedComponents.get(name)!;
    versions.set(version, {
      component,
      version,
      deprecated: options.deprecated,
      migration: options.migration,
    });

    // 如果是第一个版本或者不是废弃版本,设为默认版本
    if (!this.defaultVersions.has(name) || !options.deprecated) {
      this.defaultVersions.set(name, version);
      this.register(name, component);
    }
  }

  // 获取特定版本的组件
  resolveVersion(toolPart: any, version?: string): Component {
    const name = this.getToolName(toolPart);
    const versions = this.versionedComponents.get(name);

    if (!versions) {
      return this.resolve(toolPart);
    }

    const targetVersion = version || this.defaultVersions.get(name);
    if (!targetVersion) {
      return this.fallbackComponent;
    }

    const versionedComponent = versions.get(targetVersion);
    if (!versionedComponent) {
      console.warn(`Version ${targetVersion} not found for tool ${name}`);
      return this.fallbackComponent;
    }

    if (versionedComponent.deprecated) {
      console.warn(
        `Tool ${name} v${targetVersion} is deprecated. ${versionedComponent.migration || ''}`
      );
    }

    return versionedComponent.component;
  }

  // 获取工具的所有版本
  getVersions(toolName: string): string[] {
    const versions = this.versionedComponents.get(toolName);
    return versions ? Array.from(versions.keys()) : [];
  }

  // 获取工具的默认版本
  getDefaultVersion(toolName: string): string | undefined {
    return this.defaultVersions.get(toolName);
  }

  // 迁移到新版本
  migrateToVersion(toolName: string, newVersion: string): boolean {
    const versions = this.versionedComponents.get(toolName);
    if (!versions || !versions.has(

newVersion)) { return false; }

const newVersionComponent = versions.get(newVersion)!;
this.defaultVersions.set(toolName, newVersion);
this.register(toolName, newVersionComponent.component);

return true;

} }

// 使用示例 const versionedResolver = new VersionedToolResolver();

// 注册不同版本的组件 versionedResolver.registerVersioned('DataChart', '1.0.0', DataChartV1); versionedResolver.registerVersioned('DataChart', '2.0.0', DataChartV2); versionedResolver.registerVersioned('DataChart', '1.5.0', DataChartV15, { deprecated: true, migration: 'Please upgrade to version 2.0.0 for better performance' });

// 使用特定版本 const chartV1 = versionedResolver.resolveVersion({ name: 'DataChart' }, '1.0.0'); const chartLatest = versionedResolver.resolveVersion({ name: 'DataChart' }); // 使用默认版本

// 迁移到新版本 versionedResolver.migrateToVersion('DataChart', '2.0.0');


## 最佳实践

### 1. 类型安全

建议在 TypeScript 项目中定义明确的类型接口:

```typescript
// types/tool.ts
import type { Component } from 'vue';

export interface ToolPart {
  name: string;
  id?: string;
  category?: 'workflow' | 'external' | 'default';
  input?: Record<string, any>;
  type?: string;
}

export interface ToolComponentMap {
  [key: string]: Component;
}

export interface ToolResolver {
  register(name: string, component: Component): void;
  resolve(toolPart: ToolPart): Component;
  isRegistered(name: string): boolean;
}

// 使用类型化的解析器
class TypedToolResolver implements ToolResolver {
  private resolver: ToolComponentResolver;

  constructor() {
    this.resolver = new ToolComponentResolver();
  }

  register(name: string, component: Component): void {
    this.resolver.register(name, component);
  }

  resolve(toolPart: ToolPart): Component {
    return this.resolver.resolve(toolPart);
  }

  isRegistered(name: string): boolean {
    return this.resolver.isRegistered(name);
  }
}

2. 错误处理和日志

typescript
// SafeToolResolver.ts
import { ToolComponentResolver } from "@pt/common-ui/utils";

class SafeToolResolver extends ToolComponentResolver {
  private logger: (message: string, level: "info" | "warn" | "error") => void;

  constructor(
    customComponents = {},
    fallbackComponent = UnmatchedTool,
    logger = console.log
  ) {
    super(customComponents, fallbackComponent);
    this.logger = logger;
  }

  register(name: string, component: Component): void {
    try {
      if (!name || typeof name !== "string") {
        throw new Error("Tool name must be a non-empty string");
      }

      if (!component) {
        throw new Error("Component cannot be null or undefined");
      }

      super.register(name, component);
      this.logger(`Tool "${name}" registered successfully`, "info");
    } catch (error) {
      this.logger(
        `Failed to register tool "${name}": ${error.message}`,
        "error"
      );
      throw error;
    }
  }

  resolve(toolPart: any): Component {
    try {
      if (!toolPart) {
        this.logger("Tool part is null or undefined", "warn");
        return this.fallbackComponent;
      }

      const component = super.resolve(toolPart);
      const toolName = this.getToolName(toolPart);

      if (component === this.fallbackComponent) {
        this.logger(
          `Tool "${toolName}" not found, using fallback component`,
          "warn"
        );
      } else {
        this.logger(`Tool "${toolName}" resolved successfully`, "info");
      }

      return component;
    } catch (error) {
      this.logger(`Error resolving tool: ${error.message}`, "error");
      return this.fallbackComponent;
    }
  }

  unregister(name: string): void {
    try {
      if (this.isRegistered(name)) {
        super.unregister(name);
        this.logger(`Tool "${name}" unregistered successfully`, "info");
      } else {
        this.logger(`Tool "${name}" was not registered`, "warn");
      }
    } catch (error) {
      this.logger(
        `Failed to unregister tool "${name}": ${error.message}`,
        "error"
      );
      throw error;
    }
  }
}

3. 性能优化

typescript
// PerformantToolResolver.ts
class PerformantToolResolver extends ToolComponentResolver {
  private resolveCache: Map<string, Component> = new Map();
  private cacheEnabled: boolean = true;

  // 启用/禁用缓存
  setCacheEnabled(enabled: boolean): void {
    this.cacheEnabled = enabled;
    if (!enabled) {
      this.clearCache();
    }
  }

  // 清除缓存
  clearCache(): void {
    this.resolveCache.clear();
  }

  // 重写 resolve 方法以支持缓存
  resolve(toolPart: any): Component {
    if (!this.cacheEnabled) {
      return super.resolve(toolPart);
    }

    const toolName = this.getToolName(toolPart);
    const cacheKey = `${toolName}_${toolPart.category || "default"}`;

    if (this.resolveCache.has(cacheKey)) {
      return this.resolveCache.get(cacheKey)!;
    }

    const component = super.resolve(toolPart);
    this.resolveCache.set(cacheKey, component);

    return component;
  }

  // 重写 register 方法以清除相关缓存
  register(name: string, component: Component): void {
    super.register(name, component);

    if (this.cacheEnabled) {
      // 清除相关的缓存条目
      const keysToDelete = Array.from(this.resolveCache.keys()).filter((key) =>
        key.startsWith(`${name}_`)
      );

      keysToDelete.forEach((key) => this.resolveCache.delete(key));
    }
  }

  // 重写 unregister 方法以清除相关缓存
  unregister(name: string): void {
    super.unregister(name);

    if (this.cacheEnabled) {
      // 清除相关的缓存条目
      const keysToDelete = Array.from(this.resolveCache.keys()).filter((key) =>
        key.startsWith(`${name}_`)
      );

      keysToDelete.forEach((key) => this.resolveCache.delete(key));
    }
  }

  // 获取缓存统计信息
  getCacheStats(): { size: number; hitRate: number } {
    return {
      size: this.resolveCache.size,
      hitRate: 0, // 可以实现命中率统计
    };
  }
}

4. 测试支持

typescript
// MockToolResolver.ts
class MockToolResolver extends ToolComponentResolver {
  private mockComponents: Map<string, Component> = new Map();

  // 添加模拟组件
  addMock(name: string, mockComponent: Component): void {
    this.mockComponents.set(name, mockComponent);
    this.register(name, mockComponent);
  }

  // 移除模拟组件
  removeMock(name: string): void {
    this.mockComponents.delete(name);
    this.unregister(name);
  }

  // 清除所有模拟组件
  clearMocks(): void {
    for (const name of this.mockComponents.keys()) {
      this.unregister(name);
    }
    this.mockComponents.clear();
  }

  // 检查是否为模拟组件
  isMock(name: string): boolean {
    return this.mockComponents.has(name);
  }

  // 获取所有模拟组件
  getMocks(): string[] {
    return Array.from(this.mockComponents.keys());
  }
}

// 测试示例
describe("ToolComponentResolver", () => {
  let mockResolver: MockToolResolver;
  let MockComponent: Component;

  beforeEach(() => {
    mockResolver = new MockToolResolver();
    MockComponent = {
      name: "MockComponent",
      template: "<div>Mock Tool</div>",
    };
  });

  afterEach(() => {
    mockResolver.clearMocks();
  });

  test("should resolve mock component", () => {
    mockResolver.addMock("TestTool", MockComponent);

    const resolved = mockResolver.resolve({ name: "TestTool" });
    expect(resolved).toBe(MockComponent);
  });

  test("should return fallback for unknown tool", () => {
    const resolved = mockResolver.resolve({ name: "UnknownTool" });
    expect(resolved).toBe(mockResolver["fallbackComponent"]);
  });
});

故障排除

常见问题

1. 组件未找到

问题: 调用 resolve() 时返回 UnmatchedTool 组件

原因:

  • 工具名称不匹配
  • 组件未正确注册
  • 工具部分对象格式不正确

解决方案:

typescript
// 检查工具是否已注册
console.log(resolver.isRegistered("MyTool")); // false

// 检查已注册的工具列表
console.log(resolver.getRegisteredTools());

// 确保正确注册组件
resolver.register("MyTool", MyToolComponent);

// 检查工具部分对象格式
const toolPart = {
  name: "MyTool", // 确保名称正确
  // category: 'workflow', // 如果是工作流工具
  input: {
    /* ... */
  },
};

2. 组件加载失败

问题: 异步组件加载失败

原因:

  • 组件文件路径错误
  • 组件导出格式不正确
  • 网络或模块加载错误

解决方案:

typescript
// 使用 try-catch 处理加载错误
try {
  const module = await import("./tools/MyTool.vue");
  const component = module.default || module;
  resolver.register("MyTool", component);
} catch (error) {
  console.error("Failed to load component:", error);
  // 使用备用组件或显示错误信息
}

// 检查组件导出格式
// 正确的导出方式:
export default defineComponent({
  name: "MyTool",
  // ...
});

3. 类型错误

问题: TypeScript 类型检查失败

解决方案:

typescript
// 确保正确的类型定义
import type { Component } from "vue";
import type { ToolComponentMap } from "@pt/common-ui/utils";

// 使用类型断言
const myComponent = MyToolComponent as Component;
resolver.register("MyTool", myComponent);

// 或者定义明确的接口
interface MyToolPart {
  name: string;
  category?: string;
  input?: any;
}

const toolPart: MyToolPart = {
  name: "MyTool",
  input: { data: "test" },
};

调试技巧

1. 启用详细日志

typescript
// 创建带日志的解析器
const resolver = new SafeToolResolver({}, UnmatchedTool, (message, level) => {
  console.log(`[${level.toUpperCase()}] ${message}`);
});

2. 检查组件状态

typescript
// 检查解析器状态
function debugResolver(resolver: ToolComponentResolver) {
  console.log("Registered tools:", resolver.getRegisteredTools());

  // 测试特定工具
  const testTools = ["WebFetch", "PythonCodeSandbox", "CustomTool"];
  testTools.forEach((tool) => {
    console.log(`${tool}: ${resolver.isRegistered(tool) ? "✓" : "✗"}`);
  });
}

debugResolver(myResolver);

3. 组件解析追踪

typescript
// 创建追踪解析器
class TracingResolver extends ToolComponentResolver {
  resolve(toolPart: any): Component {
    const toolName = this.getToolName(toolPart);
    console.log(`Resolving tool: ${toolName}`, toolPart);

    const component = super.resolve(toolPart);
    console.log(`Resolved to:`, component.name || "Anonymous Component");

    return component;
  }
}

注意事项

  1. 组件注册时机: 确保在使用前注册所有需要的组件
  2. 内存管理: 在不需要时及时注销组件,避免内存泄漏
  3. 异步加载: 对于大型组件,考虑使用懒加载以提高性能
  4. 错误处理: 始终为组件加载和解析添加适当的错误处理
  5. 类型安全: 在 TypeScript 项目中使用明确的类型定义
  6. 测试覆盖: 为自定义解析器和组件编写充分的测试

性能考虑

  1. 缓存策略: 对于频繁解析的组件,考虑实现缓存机制
  2. 懒加载: 使用 defineAsyncComponent 进行按需加载
  3. 批量操作: 使用 registerBatch() 进行批量注册
  4. 内存优化: 定期清理不需要的组件注册

相关文档

Released under the MIT License.