npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zyw-aitools-manager

v0.2.93

Published

AI工具管理器 - 为Next.js项目提供AI工具管理和动态加载功能,添加了工具的创建、更新、删除、搜索等客户端功能,解决客户端导入服务器端工具的错误,继续优化客户端功能,添加分页搜索,参数去掉shape。自定义库取代@ai-sdk/react,解决swr问题,添加tool的模拟运行。添加一键生成工具。

Readme

zyw-aitools-manager

AI工具管理器 - 为Next.js项目提供AI工具管理和动态加载功能

简介

zyw-aitools-manager 是一个专为Next.js项目设计的AI工具管理器,支持PostgreSQL向量数据库存储和检索。该库允许您管理、存储和动态加载AI工具,提供相似性搜索功能,让您轻松找到合适的工具来满足用户需求。

主要特点:

  • 基于PostgreSQL + pgvector的向量存储和相似性查询
  • 支持工具的创建、更新、删除和搜索
  • 动态加载工具并执行
  • 与OpenAI兼容的AI接口集成
  • 支持用户权限管理(公开/私有工具)

安装

npm install zyw-aitools-manager
# 或者
yarn add zyw-aitools-manager

数据库配置

本库依赖PostgreSQL数据库和pgvector扩展,请确保您的Prisma配置中包含以下内容:

generator client {
  provider = "prisma-client-js"
  previewFeatures = ["postgresqlExtensions"]
}

datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_LOCAL_URL")
  directUrl = env("DATABASE_LOCAL_URL")
  extensions = [pgvector(map: "vector", schema: "public")]
}

// AI工具模型
model tool {
  id                   String      @id @default(uuid())
  name                 String
  userId               String
  createdAt            DateTime    @default(now())
  updatedAt            DateTime    @updatedAt
  description          String
  embeddingDescription Unsupported("vector(1536)")
  isPublic             Boolean     @default(false)
  vectorDimension      Int         @default(1536)
  code                 String      @db.Text
  parameters           Json?
  executeCode          String?     @db.Text
  version              String?     @default("1.0.0")
  usageCount           Int         @default(0)
  lastUsedAt           DateTime?
  likes                Int         @default(0) // 点赞数量
  toolLikes            toolLike[]  // 点赞记录关系
}

// 工具点赞记录模型
model toolLike {
  id       String @id @default(uuid())
  toolId   String
  userId   String
  createdAt DateTime @default(now())
  
  tool     tool   @relation(fields: [toolId], references: [id], onDelete: Cascade)
  
  @@unique([toolId, userId]) // 确保一个用户对一个工具只能点赞一次
}

快速开始

1. 初始化配置

在项目的入口文件(如app/api/ai/route.ts)中配置库:

import { PrismaClient } from '@prisma/client';
import { config } from 'zyw-aitools-manager';
import { OpenAI } from '@ai-sdk/openai';

// 初始化Prisma客户端
const prisma = new PrismaClient();

// 初始化OpenAI客户端
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// 配置zyw-aitools-manager
config({
  prisma,
  ai: openai,
  toolsDir: 'zyw/aitools', // 可选,默认为'zyw/aitools'
  debug: true, // 可选,默认为false
  
  // 必须提供:自定义嵌入向量生成方法
  generateEmbedding: async (text, dimension = 1536) => {
    // 使用OpenAI生成嵌入向量示例
    try {
      const response = await openai.embeddings.create({
        model: 'text-embedding-3-small',
        input: text,
      });
      return response.data[0].embedding;
    } catch (error) {
      console.error('生成嵌入向量失败:', error);
      throw new Error(`生成嵌入向量失败: ${error.message}`);
    }
  },
  
  // 可选:设置向量维度,默认为1536
  vectorDimension: 1536
});

注意: 必须提供generateEmbedding方法,该方法用于生成文本的嵌入向量。这允许您使用任何嵌入模型或服务,而不仅限于OpenAI。

1.1 其他AI服务的嵌入方法示例

使用Hugging Face的示例

import { HfInference } from '@huggingface/inference';

// 初始化Hugging Face客户端
const hf = new HfInference(process.env.HF_API_KEY);

// 配置zyw-aitools-manager
config({
  prisma,
  toolsDir: 'zyw/aitools',
  debug: true,
  
  // 使用Hugging Face提供的嵌入方法
  generateEmbedding: async (text, dimension = 1536) => {
    try {
      // 使用Hugging Face的嵌入模型
      const response = await hf.featureExtraction({
        model: 'sentence-transformers/all-MiniLM-L6-v2',
        inputs: text
      });
      
      // 返回嵌入向量
      return response;
    } catch (error) {
      console.error('生成嵌入向量失败:', error);
      throw new Error(`生成嵌入向量失败: ${error.message}`);
    }
  },
  
  // 根据模型调整向量维度
  vectorDimension: 384 // sentence-transformers/all-MiniLM-L6-v2的向量维度为384
});

使用Azure OpenAI的示例

import { OpenAI } from 'openai';

// 初始化Azure OpenAI客户端
const azureOpenAI = new OpenAI({
  apiKey: process.env.AZURE_OPENAI_API_KEY,
  baseURL: `${process.env.AZURE_OPENAI_ENDPOINT}/openai/deployments/${process.env.AZURE_OPENAI_EMBEDDING_DEPLOYMENT}`,
  defaultQuery: { 'api-version': '2023-05-15' },
  defaultHeaders: { 'api-key': process.env.AZURE_OPENAI_API_KEY }
});

// 配置zyw-aitools-manager
config({
  prisma,
  toolsDir: 'zyw/aitools',
  debug: true,
  
  // 使用Azure OpenAI的嵌入方法
  generateEmbedding: async (text, dimension = 1536) => {
    try {
      const response = await azureOpenAI.embeddings.create({
        model: process.env.AZURE_OPENAI_EMBEDDING_DEPLOYMENT,
        input: text,
      });
      
      return response.data[0].embedding;
    } catch (error) {
      console.error('生成嵌入向量失败:', error);
      throw new Error(`生成嵌入向量失败: ${error.message}`);
    }
  }
});

使用ai 库的示例

import { embedMany } from 'ai';
import { openai,createOpenAI } from '@ai-sdk/openai';

const embeddingModel = openai.embedding('text-embedding-ada-002',);

const myOpenai = createOpenAI({
    baseURL: 'https://',
    apiKey: 'Your apiKey',
    compatibility: 'strict',
  });
const generateEmbedding = async (
    input: string,
    dimensions: number = 1536
  ): Promise<number[]> => {
    const embeddingModel = myOpenai.embedding(AISettings.OPENAI.embeddingModel, {
        dimensions: dimensions,
    });
    const { embeddings } = await embedMany({
      model: embeddingModel,
      values: [input],
    });
    return embeddings[0];
  };

2. 创建工具

import { createTool } from 'zyw-aitools-manager/server';

// 创建一个新工具
async function handleCreateTool(req, res) {
  const { name, description, code, userId } = req.body;
  
  try {
    const toolId = await createTool({
      name,
      description,
      code,
      userId,
      isPublic: true,
    });
    
    return { success: true, toolId };
  } catch (error) {
    console.error('创建工具失败:', error);
    return { success: false, error: error.message };
  }
}

3. 搜索工具

import { searchTools } from 'zyw-aitools-manager/server';

// 搜索工具
async function handleSearchTools(req, res) {
  const { query, userId } = req.body;
  
  try {
    const tools = await searchTools({
      query,
      userId,
      includePrivate: true,
      limit: 5,
    });
    
    return { success: true, tools };
  } catch (error) {
    console.error('搜索工具失败:', error);
    return { success: false, error: error.message };
  }
}

4. 加载工具

import { loadTools } from 'zyw-aitools-manager/server';

// 加载工具并执行
async function handleExecuteTool(req, res) {
  const { toolIds, userId, params } = req.body;
  
  try {
    // 加载工具
    const toolFunctions = await loadTools(toolIds, userId);
    
    if (Object.keys(toolFunctions).length === 0) {
      return { success: false, error: '没有找到可用的工具' };
    }
    
    // 执行第一个工具
    const [toolName, tool] = Object.entries(toolFunctions)[0];
    const result = await tool.execute(params);
    
    return { success: true, toolName, result };
  } catch (error) {
    console.error('执行工具失败:', error);
    return { success: false, error: error.message };
  }
}

5. 与AI集成

import { searchTools, loadTools } from 'zyw-aitools-manager/server';
import { OpenAI } from '@ai-sdk/openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// 搜索并准备工具
async function getToolsForPrompt(query, userId) {
  // 搜索相关工具
  const tools = await searchTools({
    query,
    userId,
    includePrivate: true,
    limit: 3,
  });
  
  // 加载工具
  const toolFunctions = await loadTools(
    tools.map(tool => tool.id),
    userId
  );
  
  // 准备工具描述
  const toolDescriptions = Object.entries(toolFunctions).map(([name, tool]) => ({
    type: 'function',
    function: {
      name,
      description: tool.description,
      parameters: tool.parameters.shape,
    }
  }));
  
  return { toolFunctions, toolDescriptions };
}

// 集成到AI对话中
async function handleChatRequest(req, res) {
  const { messages, userId } = req.body;
  const userMessage = messages[messages.length - 1].content;
  
  // 获取相关工具
  const { toolFunctions, toolDescriptions } = await getToolsForPrompt(userMessage, userId);
  
  // 调用AI
  const response = await openai.chat.completions.create({
    model: 'gpt-4-turbo',
    messages,
    tools: toolDescriptions,
  });
  
  // 处理工具调用
  if (response.choices[0]?.message?.tool_calls?.length > 0) {
    const toolCall = response.choices[0].message.tool_calls[0];
    const toolName = toolCall.function.name;
    const toolParams = JSON.parse(toolCall.function.arguments);
    
    // 执行工具
    if (toolFunctions[toolName]) {
      const result = await toolFunctions[toolName].execute(toolParams);
      
      // 继续与AI对话,将工具结果传回
      const finalResponse = await openai.chat.completions.create({
        model: 'gpt-4-turbo',
        messages: [
          ...messages,
          response.choices[0].message,
          {
            role: 'tool',
            tool_call_id: toolCall.id,
            content: JSON.stringify(result),
          },
        ],
      });
      
      return finalResponse.choices[0].message;
    }
  }
  
  return response.choices[0].message;
}

AI生成方法实现

该库支持AI辅助功能,可以根据工具名称生成描述、根据描述生成参数定义、根据描述和参数生成执行代码。

为了使用这些功能,宿主项目必须提供相应的实现方法。详细说明和示例请参考 examples/ai-implementation.md

重要提示:如果启用了AI辅助功能(默认启用),但未提供实现方法,将会在控制台输出警告信息。

// 提供AI生成方法的实现
import { setClientConfig } from 'zyw-aitools-manager/client';
import { createOpenAI } from '@ai-sdk/openai';
import { streamText } from 'ai';

// 配置AI实现
setClientConfig({
  ai: {
    generateDescription: {
      implementation: async (name, onProgress) => {
        // 实现根据名称生成描述的逻辑
        // 支持流式响应
        return "生成的描述";
      }
    },
    // 其他实现...
  }
});

引入方式

为避免在客户端错误引入服务器端代码(如fs/promises),提供三种导入方式:

// 1. 导入配置和类型:
import { config, getConfig } from 'zyw-aitools-manager';
// 仅包含配置功能和基础类型,不包含fs/promises等Node.js模块

// 2. 导入客户端组件和类型:
import { ToolForm, ToolList, ToolSearch } from 'zyw-aitools-manager/client';
// 仅包含React组件和客户端功能,可以安全在浏览器中使用

// 3. 导入服务器端功能:
import { createTool, searchTools } from 'zyw-aitools-manager/server';
// 包含需要在服务器端运行的功能,依赖Node.js模块如fs/promises

API参考

配置

import { config } from 'zyw-aitools-manager';

// 设置配置
config({
  // 数据库相关
  prisma: prismaClient,
  
  // AI相关
  ai: aiClient,
  embeddingModel: 'text-embedding-3-small',
  
  // 文件系统相关
  toolsDir: 'zyw/aitools',
  
  // Auth相关
  auth: authConfig, // 传入从 NextAuth 导出的 auth 对象
  
  // 其他配置
  debug: false,
});

Auth.js v5 集成

该库支持与 Auth.js v5 集成,允许宿主项目通过 config() 函数配置身份验证:

import { config } from 'zyw-aitools-manager';
import { auth } from './auth'; // 宿主项目的 Auth.js 配置

// 配置 zyw-aitools-manager
config({
  // 其他配置...
  auth, // 传入 auth 对象
});

关于完整的 Auth.js v5 集成指南,请参阅 examples/auth-integration.md

🛡️ 安全执行机制

系统具备完善的安全执行机制,确保工具代码错误不会影响整体项目运行:

多层错误保护

  1. 模块加载安全

    • 文件存在性检查
    • 语法错误捕获
    • 模块结构验证
    • require缓存管理
  2. 执行时安全

    • 30秒超时保护
    • 参数验证错误处理
    • 运行时异常捕获
    • 结果统一封装
  3. 系统级保护

    • 配置状态验证
    • 数据库连接检查
    • 顶层错误兜底
    • 详细错误日志

错误结果格式

所有工具执行都返回统一的结果格式:

// 成功结果
{
  success: true,
  data: any, // 工具执行结果
  functionIndex?: number
}

// 错误结果
{
  success: false,
  error: string, // 具体错误信息
  message: string, // 用户友好的错误描述
  functionIndex?: number
}

安全执行示例

import { executeTool } from 'zyw-aitools-manager/server';

// 安全执行工具
const result = await executeTool(toolId, params, userId);

// 检查执行结果
if (result.success === false) {
  console.error('工具执行失败:', result.error);
  // 处理错误情况,向用户显示友好错误信息
  return res.status(400).json({ 
    error: result.message,
    details: result.error // 开发环境可选
  });
}

// 处理成功结果
return res.json({ data: result.data });

错误类型说明

  • 语法错误: 工具代码包含JavaScript语法错误时返回安全的错误工具
  • 运行时错误: 工具执行过程中抛出异常被捕获并封装为错误结果
  • 超时错误: 工具执行超过30秒被自动终止
  • 参数验证错误: 传入参数不符合Zod定义时返回详细验证错误
  • 文件缺失错误: 工具文件不存在时返回安全的错误提示

完整的安全执行演示请参阅 examples/safe-tool-execution-example.tsx

服务器端API

import {
  createTool,
  updateTool,
  deleteTool,
  searchTools,
  loadTools,
  loadToolModule,
  getToolMetadata,
  tool,
} from 'zyw-aitools-manager/server';

// 创建工具
const toolId = await createTool({
  name: '温度转换器',
  description: '将华氏温度转换为摄氏温度',
  code: 'export default {...}',
  userId: 'user123',
  isPublic: true,
});

// 更新工具
await updateTool({
  id: 'tool123',
  name: '高级温度转换器',
  description: '转换各种温度单位',
});

// 删除工具
await deleteTool('tool123');

// 搜索工具
const tools = await searchTools({
  query: '温度转换',
  userId: 'user123',
  includePrivate: true,
});

// 加载工具
const toolFunctions = await loadTools(['tool123'], 'user123');

// 直接从文件系统加载工具模块(不查询数据库)
const toolFunction = await loadToolModule('tool123');
if (toolFunction) {
  const result = await toolFunction.execute({ param: 'value' });
}

// 获取工具元数据
const metadata = await getToolMetadata('tool123');

// 创建工具函数
const myTool = tool({
  description: '示例工具',
  parameters: z.object({
    input: z.string(),
  }),
  execute: async ({ input }) => {
    return { output: input.toUpperCase() };
  },
});

工具格式

工具需要遵循以下格式:

import { z } from 'zod';
import { tool } from 'zyw-aitools-manager/server';

export default tool({
  description: '工具描述',
  parameters: z.object({
    // 定义参数
    param1: z.string().describe('参数1描述'),
    param2: z.number().describe('参数2描述'),
  }),
  execute: async (params) => {
    // 执行工具逻辑
    const result = doSomething(params.param1, params.param2);
    
    // 返回结果
    return {
      output: result,
    };
  },
});

许可证

MIT

客户端使用方法

客户端组件

zyw-aitools-manager提供了一套完整的客户端组件,用于创建、管理和搜索工具。这些组件使用Mantine v8构建,支持响应式布局和国际化。

import { 
  // 工具表单组件
  ToolForm, 
  
  // 工具列表组件
  ToolList, 
  ToolCard, 
  
  // 工具搜索组件
  ToolSearch, 
  ToolDetail 
} from 'zyw-aitools-manager/client';

安装依赖

客户端组件依赖以下库:

npm install @mantine/core @mantine/notifications @tabler/icons-react next-intl next-auth react-hook-form @hookform/resolvers

工具表单组件

ToolForm组件用于创建和编辑工具,支持多步骤表单和AI辅助生成。

import { ToolForm } from 'zyw-aitools-manager/client';
import { useState } from 'react';

export default function CreateToolPage() {
  const handleSuccess = (toolId: string) => {
    console.log(`工具创建成功: ${toolId}`);
    // 导航到工具列表页
  };
  
  const handleCancel = () => {
    // 处理取消操作
  };
  
  return (
    <div>
      <ToolForm 
        onSuccess={handleSuccess}
        onCancel={handleCancel}
      />
    </div>
  );
}

编辑工具时,可以提供toolIdinitialData

<ToolForm 
  toolId="tool123"
  initialData={{
    name: "现有工具",
    description: "这是一个现有工具",
    isPublic: true,
    parameters: "...",
    executeCode: "..."
  }}
  onSuccess={handleSuccess}
  onCancel={handleCancel}
/>

工具列表组件

ToolList组件用于显示和管理工具列表,支持筛选、分页和搜索。

import { ToolList } from 'zyw-aitools-manager/client';
import { useSession } from 'next-auth/react';
import { useState, useEffect } from 'react';

export default function ToolsPage() {
  const { data: session } = useSession();
  const [tools, setTools] = useState([]);
  
  useEffect(() => {
    // 加载用户的工具
    async function loadTools() {
      // 从API获取工具
      const response = await fetch('/api/tools');
      const data = await response.json();
      setTools(data.tools);
    }
    
    if (session?.user) {
      loadTools();
    }
  }, [session]);
  
  const handleEditTool = (tool) => {
    // 导航到编辑页面
  };
  
  const handleViewTool = (tool) => {
    // 导航到详情页面
  };
  
  const handleDeleteTool = async (toolId) => {
    // 删除工具
    await fetch(`/api/tools/${toolId}`, { method: 'DELETE' });
    // 刷新列表
    setTools(tools.filter(tool => tool.id !== toolId));
  };
  
  return (
    <div>
      <ToolList 
        tools={tools}
        currentUserId={session?.user?.id || ''}
        onEditTool={handleEditTool}
        onViewTool={handleViewTool}
        onDeleteTool={handleDeleteTool}
      />
    </div>
  );
}

高级用法

隐藏筛选工具栏

当您已经有自己的筛选界面时,可以隐藏ToolList的内置筛选工具栏:

<ToolList 
  tools={tools}
  currentUserId={session?.user?.id || ''}
  showFilters={false}  // 隐藏筛选工具栏
  onEditTool={handleEditTool}
  onViewTool={handleViewTool}
  onDeleteTool={handleDeleteTool}
/>

服务器端分页

对于大量工具,您可以使用服务器端分页:

const [pagination, setPagination] = useState({
  page: 1,
  pageSize: 10,
  total: 0,
  totalPages: 0
});

const handlePageChange = async (page: number) => {
  // 从服务器获取指定页的数据
  const response = await fetch(`/api/tools?page=${page}&pageSize=${pagination.pageSize}`);
  const data = await response.json();
  setTools(data.tools);
  setPagination({
    page: data.page,
    pageSize: data.pageSize,
    total: data.total,
    totalPages: data.totalPages
  });
};

return (
  <ToolList 
    tools={tools}
    currentUserId={session?.user?.id || ''}
    useServerPagination={true}
    pagination={{
      total: pagination.total,
      page: pagination.page,
      pageSize: pagination.pageSize,
      totalPages: pagination.totalPages,
      onPageChange: handlePageChange
    }}
    onEditTool={handleEditTool}
    onViewTool={handleViewTool}
    onDeleteTool={handleDeleteTool}
  />
);

自定义搜索

您可以提供自定义搜索函数来处理搜索和筛选:

const handleSearchTools = async (query: string, filter: string) => {
  const response = await fetch(`/api/tools/search?q=${query}&filter=${filter}`);
  const data = await response.json();
  setTools(data.tools);
};

return (
  <ToolList 
    tools={tools}
    currentUserId={session?.user?.id || ''}
    onSearchTools={handleSearchTools}
    onEditTool={handleEditTool}
    onViewTool={handleViewTool}
    onDeleteTool={handleDeleteTool}
  />
);

工具搜索组件

ToolSearch组件用于搜索和浏览工具,支持筛选和排序。

import { ToolSearch } from 'zyw-aitools-manager/client';
import { useState } from 'react';

export default function BrowseToolsPage() {
  return (
    <div>
      <ToolSearch />
    </div>
  );
}

如果需要提供初始工具列表:

<ToolSearch initialTools={preloadedTools} />

筛选选项

ToolSearch组件提供以下筛选选项:

  • 全部 (All): 显示所有用户可以访问的工具(公开工具 + 自己的私有工具)
  • 公开 (Public): 只显示所有用户都可以访问的公开工具
  • 私有 (Private): 只显示当前用户自己的私有工具(需要登录)
  • 我的 (Mine): 显示当前用户创建的所有工具(包括公开和私有,需要登录)

注意: "私有"和"我的"选项只会在用户登录后显示。"私有"选项专门用于查看用户自己创建的私有工具。

工具详情组件

ToolDetail组件用于查看工具详情,测试工具功能,以及查看工具代码。

import { ToolDetail } from 'zyw-aitools-manager/client';
import { useRouter } from 'next/navigation';

export default function ToolDetailPage({ params }) {
  const router = useRouter();
  const { id } = params;
  
  const handleBack = () => {
    router.back();
  };
  
  const handleImport = (tool) => {
    // 导入工具到用户的工具列表
  };
  
  return (
    <div>
      <ToolDetail 
        toolId={id}
        onBack={handleBack}
        onImport={handleImport}
      />
    </div>
  );
}

国际化配置

该库支持国际化,提供英文和中文翻译文件。您可以通过Next.js的国际化支持使用这些翻译。

/locales
  /en
    tool.json
  /zh-CN
    tool.json

在组件中使用:

import { useTranslations } from 'next-intl';

export function MyComponent() {
  const t = useTranslations('tool');
  
  return (
    <div>
      <h1>{t('form.title')}</h1>
      {/* 其他内容 */}
    </div>
  );
}

客户端配置

您可以通过导入setClientConfig函数来自定义客户端配置,包括UI设置、API路径和AI提示等:

import { setClientConfig } from 'zyw-aitools-manager/client';

// 自定义配置
setClientConfig({
  // UI配置
  ui: {
    toolsPerPage: 9, // 每页显示工具数量
    formSteps: {
      enableAiAssistance: true, // 启用AI辅助
    },
    routes: {
      createTool: '/custom/create', // 自定义创建工具页面路径
      manageTool: '/custom/manage', // 自定义管理工具页面路径
      browseTool: '/custom/browse', // 自定义浏览工具页面路径
    }
  },
  
  // 工具导入配置 - 每个工具自动导入的模块
  toolImportList: [
    "import { z } from 'zod';",
    "import axios from 'axios';",
    "import { formatResponse } from '../utils/formatters';"
  ],
  
  // 注意:导入语句智能合并功能
  // 系统会自动将用户自定义的导入语句与 toolImportList 中的默认导入语句合并,
  // 并自动去除重复项,确保所有必要的依赖都被包含在最终生成的工具代码中。
  
  // 服务器API配置
  server: {
    api: {
      // API端点配置
      createTool: '/api/custom/tools/create',  // 创建工具API
      updateTool: '/api/custom/tools/update',  // 更新工具API
      deleteTool: '/api/custom/tools/delete',  // 删除工具API
      searchTool: '/api/custom/tools/search',  // 搜索工具API
      getTool: '/api/custom/tools/get',        // 获取工具详情API
      loadTool: '/api/custom/tools/load',      // 加载工具API
      executeTool: '/api/custom/tools/execute', // 执行工具API
      listTool: '/api/custom/tools/list',      // 列出工具API
      getToolWithFunction: '/api/tools/combined',  // 添加合并API路径
      likeTool: '/api/custom/tools/like'       // 点赞工具API
    }
  },
  
  // AI辅助配置
  ai: {
    generateDescription: {
      prompt: '根据以下代码生成一个简短明确的工具描述:\n', // 自定义AI生成描述的提示词
    },
    generateParameters: {
      prompt: '根据以下代码生成zod参数定义:\n', // 自定义AI生成参数的提示词
    },
    generateExecute: {
      prompt: '根据以下描述和参数生成execute函数代码:\n', // 自定义AI生成执行代码的提示词
    }
  }
});

完整的配置类型

下面是完整的配置类型定义,您可以根据需要自定义所有这些选项:

export interface ClientConfig {
  // 界面配置
  ui: {
    // 每页显示工具数量
    toolsPerPage: number;
    // 表单步骤配置
    formSteps: {
      // 是否启用AI辅助
      enableAiAssistance: boolean;
    };
    // 路由配置
    routes: {
      // 创建工具页面路径
      createTool: string;
      // 管理工具页面路径
      manageTool: string;
      // 浏览工具页面路径
      browseTool: string;
    };
  };
  
  // 工具导入配置
  toolImportList?: string[];
  
  // 服务器配置
  server: {
    // 工具相关API路径
    api: {
      // 创建工具
      createTool: string;
      // 更新工具
      updateTool: string;
      // 删除工具
      deleteTool: string;
      // 搜索工具
      searchTool: string;
      // 获取工具
      getTool: string;
      // 加载工具
      loadTool: string;
      // 执行工具
      executeTool: string;
      // 列出工具
      listTool: string;
      // 获取工具并功能
      getToolWithFunction: string;
      // 点赞工具
      likeTool: string;
    };
  };
  
  // AI辅助配置
  ai: {
    // 生成描述
    generateDescription: {
      // 提示文本
      prompt: string;
    };
    // 生成参数
    generateParameters: {
      // 提示文本
      prompt: string;
    };
    // 生成执行代码
    generateExecute: {
      // 提示文本
      prompt: string;
    };
  };
}

获取当前配置

您也可以通过getClientConfig函数获取当前的配置:

import { getClientConfig } from 'zyw-aitools-manager/client';

// 获取当前配置
const currentConfig = getClientConfig();
console.log(currentConfig.server.api.createTool); // 输出当前创建工具API路径

实现API路由

为了使客户端组件正常工作,您需要在Next.js项目中实现相应的API路由。下面是每个API的基本实现示例:

1. 创建工具API

// app/api/tools/create/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { createTool } from 'zyw-aitools-manager/server';
import { auth } from '@/auth';

export async function POST(request: NextRequest) {
  const session = await auth();
  if (!session?.user) {
    return NextResponse.json({ error: '未授权' }, { status: 401 });
  }

  try {
    const data = await request.json();
    const toolId = await createTool(data);
    return NextResponse.json({ success: true, toolId });
  } catch (error) {
    console.error('创建工具失败:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : '创建工具失败' },
      { status: 500 }
    );
  }
}

2. 搜索工具API

// app/api/tools/search/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { searchTools } from 'zyw-aitools-manager/server';
import { auth } from '@/auth';

export async function POST(request: NextRequest) {
  const session = await auth();
  
  try {
    const data = await request.json();
    const userId = session?.user ? (session.user as any).id : undefined;
    
    // 如果需要,确保只有已登录用户可以搜索私有工具
    if (data.includePrivate && userId !== data.userId) {
      data.includePrivate = false;
    }
    
    const tools = await searchTools(data);
    return NextResponse.json(tools);
  } catch (error) {
    console.error('搜索工具失败:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : '搜索工具失败' },
      { status: 500 }
    );
  }
}

3. 删除工具API

// app/api/tools/delete/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { deleteTool } from 'zyw-aitools-manager/server';
import { auth } from '@/auth';

export async function POST(request: NextRequest) {
  const session = await auth();
  if (!session?.user) {
    return NextResponse.json({ error: '未授权' }, { status: 401 });
  }

  try {
    const { toolId, userId } = await request.json();
    const currentUserId = (session.user as any).id;
    
    // 确保用户只能删除自己的工具
    if (userId && userId !== currentUserId) {
      return NextResponse.json({ error: '无权删除其他用户的工具' }, { status: 403 });
    }
    
    const success = await deleteTool(toolId, currentUserId);
    return NextResponse.json({ success });
  } catch (error) {
    console.error('删除工具失败:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : '删除工具失败' },
      { status: 500 }
    );
  }
}

4. 点赞工具API

// app/api/tools/like/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { likeTool, checkUserLiked } from 'zyw-aitools-manager/server';
import { auth } from '@/auth';

export async function POST(request: NextRequest) {
  const session = await auth();
  if (!session?.user) {
    return NextResponse.json({ error: '未授权' }, { status: 401 });
  }

  try {
    const { toolId } = await request.json();
    const userId = (session.user as any).id;
    
    const result = await likeTool(toolId, userId);
    return NextResponse.json(result);
  } catch (error) {
    console.error('点赞操作失败:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : '点赞操作失败' },
      { status: 500 }
    );
  }
}

export async function GET(request: NextRequest) {
  const session = await auth();
  if (!session?.user) {
    return NextResponse.json({ error: '未授权' }, { status: 401 });
  }

  try {
    const url = new URL(request.url);
    const toolId = url.searchParams.get('toolId');
    
    if (!toolId) {
      return NextResponse.json({ error: '缺少toolId参数' }, { status: 400 });
    }
    
    const userId = (session.user as any).id;
    const isLiked = await checkUserLiked(toolId, userId);
    
    return NextResponse.json({ isLiked });
  } catch (error) {
    console.error('检查点赞状态失败:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : '检查点赞状态失败' },
      { status: 500 }
    );
  }
}

对于其他API路由的实现,可以遵循类似的模式,确保:

  1. 针对需要授权的操作检查用户身份
  2. 调用相应的服务器函数处理请求
  3. 返回适当的响应和状态码
  4. 实现错误处理

您还可以根据自己的需求自定义API的路径和实现方式,只需在客户端配置中相应地更新API路径即可。

与Next.js App Router集成

在Next.js项目中创建页面:

// app/tools/page.tsx
'use client';

import { ToolList } from 'zyw-aitools-manager/client';
import { useSession } from 'next-auth/react';

export default function ToolsPage() {
  const { data: session } = useSession();
  
  // ...获取工具数据
  
  return (
    <main style={{ paddingTop: 100 }}>
      <ToolList 
        tools={tools}
        currentUserId={session?.user?.id || ''}
        // ...其他属性
      />
    </main>
  );
}
// app/tools/create/page.tsx
'use client';

import { ToolForm } from 'zyw-aitools-manager/client';
import { useRouter } from 'next/navigation';

export default function CreateToolPage() {
  const router = useRouter();
  
  const handleSuccess = () => {
    router.push('/tools');
  };
  
  return (
    <main style={{ paddingTop: 100 }}>
      <ToolForm 
        onSuccess={handleSuccess}
        onCancel={() => router.back()}
      />
    </main>
  );
}
// app/tools/browse/page.tsx
'use client';

import { ToolSearch } from 'zyw-aitools-manager/client';

export default function BrowseToolsPage() {
  return (
    <main style={{ paddingTop: 100 }}>
      <ToolSearch />
    </main>
  );
}
// app/tools/browse/[id]/page.tsx
'use client';

import { ToolDetail } from 'zyw-aitools-manager/client';
import { useRouter } from 'next/navigation';

export default function ToolDetailPage({ params }) {
  const router = useRouter();
  
  return (
    <main style={{ paddingTop: 100 }}>
      <ToolDetail 
        toolId={params.id}
        onBack={() => router.back()}
      />
    </main>
  );
}

AI辅助集成

要支持AI辅助生成功能,需要实现以下server actions:

// app/actions/ai-tools.ts
'use server';

import { generateDescription, generateParameters, generateExecute } from 'zyw-aitools-manager/client';
import { OpenAI } from '@ai-sdk/openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// 实现AI描述生成
export async function aiGenerateDescription({ code }) {
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4-turbo',
      messages: [
        {
          role: 'system',
          content: '生成简洁明确的工具描述'
        },
        {
          role: 'user',
          content: `根据以下代码生成一个简短明确的工具描述:\n${code}`
        }
      ]
    });
    
    return response.choices[0].message.content || '';
  } catch (error) {
    console.error('生成描述失败:', error);
    throw new Error('AI描述生成失败');
  }
}

// 实现AI参数生成
export async function aiGenerateParameters({ code, description }) {
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4-turbo',
      messages: [
        {
          role: 'system',
          content: '生成Zod参数定义'
        },
        {
          role: 'user',
          content: `根据以下工具描述和代码生成Zod参数定义:\n描述:${description}\n代码:${code}`
        }
      ]
    });
    
    return response.choices[0].message.content || '';
  } catch (error) {
    console.error('生成参数失败:', error);
    throw new Error('AI参数生成失败');
  }
}

// 实现AI执行代码生成
export async function aiGenerateExecute({ description, parameters }) {
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4-turbo',
      messages: [
        {
          role: 'system',
          content: '生成工具执行代码'
        },
        {
          role: 'user',
          content: `根据以下描述和参数生成execute函数代码:\n描述:${description}\n参数:${parameters}`
        }
      ]
    });
    
    return response.choices[0].message.content || '';
  } catch (error) {
    console.error('生成执行代码失败:', error);
    throw new Error('AI执行代码生成失败');
  }
}

这种方式保持了流式生成的能力,同时确保了API密钥的安全,因为所有与OpenAI的直接交互都在您的服务器端完成,客户端只与您的API交互。 建议您为每个生成功能创建单独的API端点,并在这些端点中实现与OpenAI的交互。客户端只负责UI交互和调用您的API端点。

高级使用指南

性能优化

使用合并 API 减少数据库访问

为了减少对数据库的访问次数,提高前端性能,您可以实现一个合并的 API 端点来同时获取工具详情和功能定义:

// 在服务器端实现合并 API
// app/api/tools/combined/[id]/route.ts

import { NextRequest, NextResponse } from 'next/server';
import { getTool, loadTools } from 'zyw-aitools-manager/server';

export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  try {
    const toolId = params.id;
    const searchParams = request.nextUrl.searchParams;
    const userId = searchParams.get('userId');
    
    // 并行获取工具详情和功能
    const [tool, functions] = await Promise.all([
      getTool(toolId, userId),
      loadTools([toolId], userId)
    ]);
    
    return NextResponse.json({
      tool,
      functions
    });
  } catch (error) {
    console.error('获取合并工具数据失败:', error);
    return NextResponse.json(
      { error: '获取工具详情和功能失败' },
      { status: 500 }
    );
  }
}

然后在客户端配置中添加新的API路径:

// 客户端配置
setClientConfig({
  server: {
    api: {
      // ... 其他API路径
      getToolWithFunction: '/api/tools/combined'  // 添加合并API路径
    }
  }
});

这样,ToolDetail 组件将自动使用合并的API端点来减少对数据库的访问,提高性能。如果合并API不可用,它会回退到使用单独的API调用。

APP Route 模拟运行Tool

以下为通过app 路由运行tool的例子:

// app/api/tools/execute/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { executeTool } from 'zyw-aitools-manager/server';
import { authOptions } from '@/app/api/auth/[...nextauth]/route';

export async function POST(request: NextRequest) {
  try {
    // 获取会话信息,用于权限验证
    const session = await getServerSession(authOptions);
    
    // 如果需要身份验证但用户未登录,返回401错误
    if (!session?.user) {
      return NextResponse.json(
        { error: '未授权访问,请先登录' }, 
        { status: 401 }
      );
    }
    
    // 解析请求体
    const { toolId, params } = await request.json();
    
    // 验证必要参数
    if (!toolId) {
      return NextResponse.json(
        { error: '缺少工具ID' }, 
        { status: 400 }
      );
    }
    
    // 获取用户ID(如果有)
    const userId = session.user.id;
    
    // 执行工具
    const result = await executeTool(toolId, params, userId);
    
    // 返回执行结果
    return NextResponse.json(result);
  } catch (error) {
    console.error('执行工具失败:', error);
    
    // 返回错误信息
    return NextResponse.json(
      { error: error instanceof Error ? error.message : '执行工具失败' }, 
      { status: 500 }
    );
  }
}

// 在前端组件中调用API
async function executeToolOnServer(toolId: string, params: any) {
  try {
    const response = await fetch('/api/tools/execute', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ toolId, params })
    });
    
    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(errorData.error || '执行工具失败');
    }
    
    const result = await response.json();
    return result;
  } catch (error) {
    console.error('调用工具API失败:', error);
    throw error;
  }
}

点赞功能配置

数据库准备

要使用点赞功能,请确保您的Prisma schema中包含了以下模型:

// AI工具模型(在现有tool模型中添加likes字段和关系)
model tool {
  // ... 现有字段 ...
  likes                Int         @default(0) // 点赞数量
  toolLikes            toolLike[]  // 点赞记录关系
}

// 工具点赞记录模型(新增)
model toolLike {
  id       String @id @default(uuid())
  toolId   String
  userId   String
  createdAt DateTime @default(now())
  
  tool     tool   @relation(fields: [toolId], references: [id], onDelete: Cascade)
  
  @@unique([toolId, userId]) // 确保一个用户对一个工具只能点赞一次
}

运行数据库迁移:

npx prisma migrate dev --name add-like-feature
npx prisma generate

API路由配置

在您的Next.js项目中创建点赞API路由(如上面第4点所示):

// app/api/tools/like/route.ts - 参考上面的完整实现示例

客户端配置

配置点赞API端点:

import { setClientConfig } from 'zyw-aitools-manager/client';

setClientConfig({
  server: {
    api: {
      // ... 其他API配置 ...
      likeTool: '/api/tools/like', // 点赞API端点
    }
  }
});

在工具列表中使用点赞功能

import { ToolList } from 'zyw-aitools-manager/client';
import { useSession } from 'next-auth/react';

export default function ToolsPage() {
  const { data: session } = useSession();
  
  // 实现点赞处理函数
  const handleLike = async (toolId: string) => {
    try {
      const response = await fetch('/api/tools/like', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ toolId }),
      });
      
      if (!response.ok) {
        throw new Error('点赞请求失败');
      }
      
      const result = await response.json();
      return result; // 返回 { success: boolean, likes: number }
    } catch (error) {
      throw error;
    }
  };
  
  // 检查用户是否已点赞
  const checkUserLiked = async (toolId: string) => {
    try {
      const response = await fetch(`/api/tools/like?toolId=${toolId}`);
      const result = await response.json();
      return result.isLiked;
    } catch (error) {
      return false;
    }
  };
  
  return (
    <ToolList 
      tools={tools}
      currentUserId={session?.user?.id || ''}
      onLike={handleLike}
      checkUserLiked={checkUserLiked}
      // ... 其他属性
    />
  );
}

服务器端方法

zyw-aitools-manager/server 提供了以下点赞相关的方法:

  • likeTool(toolId: string, userId: string): 切换点赞状态(点赞/取消点赞)
  • checkUserLiked(toolId: string, userId: string): 检查用户是否已点赞

这些方法会自动处理点赞数量的增减和数据库事务,确保数据一致性。

注意事项

  1. 点赞功能需要用户认证,确保您的应用已配置认证系统
  2. 点赞记录使用toolIduserId的组合作为唯一索引,防止重复点赞
  3. 当工具被删除时,相关的点赞记录会自动删除(通过数据库的级联删除)
  4. 点赞操作使用数据库事务确保数据一致性

AI Generate API samples:


//api/ai/[generateType]/route.ts


import { createOpenAI } from '@ai-sdk/openai';
import { generateId, createDataStreamResponse, streamText, generateObject } from 'ai';
import { z } from 'zod';
import { AISettings } from '@/zyw/config';
import { createQwen } from 'qwen-ai-provider';

export async function POST(req: Request, context: { params: { generateType: string } }) {
  const { generateType } = await context.params;
  const body = await req.json();
  const { messages } = body;
  

  // 三个模型实例(如果只用 OpenAI,也只需保留 myOpenai)
  // const myOpenai = createOpenAI({
  //   baseURL: AISettings.OPENAI.baseURL,
  //   apiKey:  AISettings.OPENAI.apiKey,
  //   compatibility: 'strict',
  // });
  const myOpenai = createOpenAI({
    baseURL: AISettings.OPENAI.baseURL,
    apiKey:  AISettings.OPENAI.apiKey,
    compatibility: 'compatible',
  });
  const model = AISettings.OPENAI.modelChat;
  const msg =messages?messages[messages.length - 1].content :null;
  // return new Response(JSON.stringify({ error: '缺少工具描述' }), { status: 400 });
  // ─────────────── 1. DESCRIPTION ───────────────
  
  if (generateType === 'description') {
    // 启动流式调用,拿到纯文本增量流
    const { textStream } = streamText({
      model: myOpenai(model, {}),
      system: '',
      messages: [
        { role: 'system',  content: `You are an AI tool function description assistant. AI tool description is used to tell the purpose and usage of AI tools, which is convenient for AI calls. Please strictly follow the following requirements to generate descriptions:\n\n1. Please first determine the language used by the user based on the language of the tool name provided by the user;\n\n2. Then use the language used by the user to generate a clear AI tool description based on the tool name, without any other content.\n\n3. In order to make the user understand this description, please use the same language as the tool name provided by the user.` },
        { role: 'user',  content: `tool name:${body.name || msg}` }
      ],
    });

    // 用 ReadableStream 包装,只输出原始的 delta 文本
    const encoder = new TextEncoder();
    const stream = new ReadableStream({
      async start(controller) {
        try {
          for await (const delta of textStream) {
            controller.enqueue(encoder.encode(delta));
          }
        } catch (err) {
          controller.error(err);
        } finally {
          controller.close();
        }
      }
    });

    return new Response(stream, {
      headers: {
        'Content-Type': 'text/plain; charset=utf-8',
        // 根据需要添加 CORS、缓存等 header
      }
    });

    return new Response('Not Found', { status: 404 });
  } 


  // ─────────────── 2. PARAMETERS ───────────────
  if (generateType === 'parameters') {
    if (!body.description) {
      return new Response(JSON.stringify({ error: '缺少工具描述' }), { status: 400 });
    }

    // 定义 Zod schema(只是为了类型,下面 generateObject 会使用它)
    const paramSchema = z.object({
      parameters: z.array(z.object({
        name: z.string(),
        type: z.enum(['string','number','boolean','array','object','file','date']),
        description: z.string(),
        required: z.boolean().optional(),
      }))
    });

    try {
      const result = await generateObject({
        model: myOpenai(model, {}),
        schema: paramSchema,
        system: '你是一个 AI 工具函数参数生成助手,请根据用户提供的ai工具函数描述生成这个工具函数的执行时需要的参数结构,请按照schema的要求提供结构化数据,请根据需要提供,不要提供不需要的参数,并且也要注明该参数是必须的还是可选的,请按照要求提供结构化的数据。',
        messages: [
          { role: 'user', content: body.description }
        ]
      });

      // 只返回 parameters 数组
      return new Response(JSON.stringify(result.object), {
        status: 200,
        headers: { 'Content-Type': 'application/json' }
      });
    } catch (e) {
      console.error('生成参数失败', e);
      return new Response(JSON.stringify({ error: '生成参数结构失败' }), { status: 500 });
    }
  }

  // ─────────────── 3. EXECUTION CODE ───────────────
  if (generateType === 'executionCode') {
    if (!body.description || !body.parameters) {
      return new Response(JSON.stringify({ error: '缺少描述或参数' }), { status: 400 });
    }

    const codeSchema = z.object({
      code: z.string(),
      imports: z.array(z.string()).optional().describe("complete import statement could be used in the js code."),
      exampleUsage: z.string().optional(),
    });

    try {
      const result = await generateObject({
        model: myOpenai(model, {}),
        schema: codeSchema,
        system: `你是一个 AI 工具函数生成助手,并按照schema的要求提供结构化数据。
        请根据用户提供的ai工具函数描述和参数生成可执行的 JavaScript 代码,代码放在一个异步函数中,函数名称为用户提供的工具名称,函数参数应该采用fun({param1,param1})的方式并且和提供的参数结构一致,函数返回值为执行结果。
        该函数需要具备防止计算量过大导致系统长时间停止响应的机制,如果计算量过大,该函数应该返回一个提示信息,告知用户计算量超出系统能力。
        请尽量完善代码的错误捕获机制,防止执行过程中出现错误导致系统停止响应。
        如果函数运行需要导入其他模块,请在imports中添加完整的导入语句,但是code中不要添加import语句,如果需要用到辅助函数,可以将辅助函数添加到code中。`,
        messages: [
          {
            role: 'user',
            content: `描述:\n${body.description}\n\n参数:\n${JSON.stringify(body.parameters, null, 2)}`
          }
        ]
      });

      // 只返回 { code: '...' }
      return new Response(JSON.stringify(result.object), {
        status: 200,
        headers: { 'Content-Type': 'application/json' }
      });
    } catch (e) {
      console.error('生成代码失败', e);
      return new Response(JSON.stringify({ error: '生成执行代码失败' }), { status: 500 });
    }
  }

  // 不支持的类型
  return new Response(JSON.stringify({ error: '不支持的生成类型' }), { status: 400 });
}