refactor: repartition server-side and client-side code

This commit is contained in:
LIlGG
2025-10-11 18:26:07 +08:00
parent 7acc4949fb
commit e9b573a276
309 changed files with 631 additions and 962 deletions

View File

@@ -0,0 +1,17 @@
import type { Tool, ToolSet } from 'ai';
import { serperTool } from './serper';
import { weatherTool } from './weather';
export const tools: () => ToolSet = () => {
const tools: Record<string, Tool> = {};
if (process.env.SERPER_API_KEY) {
tools.serper = serperTool;
}
if (process.env.WEATHER_API_KEY) {
tools.weather = weatherTool;
}
return tools;
};

View File

@@ -0,0 +1,54 @@
/**
* 由于 agentic 暂时不支持 AI SDK v5因此使用自定义的 Serper 工具。
* @see https://docs.agentic.so/marketplace/ts-sdks/ai-sdk
*/
import { tool } from 'ai';
import { z } from 'zod';
const API_BASE_URL = 'https://google.serper.dev';
const searchParamsSchema = z.object({
q: z.string().describe('搜索查询词'),
autocorrect: z.boolean().optional().default(true).describe('是否自动纠正拼写错误'),
gl: z.string().optional().default('us').describe('地理位置代码,如"us"表示美国'),
hl: z.string().optional().default('en').describe('语言代码,如"en"表示英语'),
page: z.number().optional().default(1).describe('页码'),
num: z.number().optional().default(10).describe('结果数量'),
type: z
.enum(['search', 'images', 'videos', 'places', 'news', 'shopping'])
.optional()
.default('search')
.describe('搜索类型'),
});
export const serperTool = tool({
description: '使用Google搜索获取最新信息。适用于查找新闻、事实、数据和当前事件等实时信息。',
inputSchema: searchParamsSchema,
execute: async ({ q, ...params }) => {
const apiKey = process.env.SERPER_API_KEY || '';
if (!apiKey) {
throw new Error('Missing SERPER_API_KEY');
}
try {
const response = await fetch(`${API_BASE_URL}/search`, {
method: 'POST',
headers: {
'X-API-KEY': apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({ q, ...params }),
});
if (!response.ok) {
throw new Error(`Serper API responded with status: ${response.status}`);
}
return await response.json();
} catch (error: unknown) {
console.error('Serper API error:', error);
const errorMessage = error instanceof Error ? error.message : '未知错误';
throw new Error(`搜索失败: ${errorMessage}`);
}
},
});

View File

@@ -0,0 +1,43 @@
/**
* 由于 agentic 暂时不支持 AI SDK v5因此使用自定义的 Weather 工具。
* @see https://docs.agentic.so/marketplace/ts-sdks/ai-sdk
*/
import { tool } from 'ai';
import { z } from 'zod';
const API_BASE_URL = 'https://api.weatherapi.com/v1';
const weatherParamsSchema = z.object({
q: z
.string()
.describe('位置查询可以是城市名称、邮政编码、IP地址或经纬度坐标。必须使用英语或拼音。例如"London"、"Beijing"'),
});
export const weatherTool = tool({
description: '获取指定位置的天气信息',
inputSchema: weatherParamsSchema,
execute: async ({ q }) => {
const apiKey = process.env.WEATHER_API_KEY || '';
if (!apiKey) {
throw new Error('Missing WEATHER_API_KEY');
}
try {
const url = new URL(`${API_BASE_URL}/current.json`);
url.searchParams.append('key', apiKey);
url.searchParams.append('q', q);
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error(`Weather API responded with status: ${response.status}`);
}
return await response.json();
} catch (error: unknown) {
console.error('Weather API error:', error);
const errorMessage = error instanceof Error ? error.message : '未知错误';
throw new Error(`获取天气信息失败: ${errorMessage}`);
}
},
});