claude-code-cchui/src/services/tools/executor.ts
gaoziman d16f72c035 feat(工具): 添加有道智云翻译功能
- 新增 translate.ts: 实现有道翻译API调用
  - 支持100+种语言互译
  - 自动语言检测
  - SHA256签名验证
  - 完善的错误码处理

- executor.ts: 添加翻译工具执行器
  - 支持源语言/目标语言参数
  - 格式化翻译结果输出

- route.ts: 添加翻译工具定义
  - Claude/OpenAI/Codex三种格式支持
2025-12-23 14:33:00 +08:00

289 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 统一工具执行器
* 根据工具名称和输入参数执行对应的工具
*/
import {
webSearch,
formatSearchResults,
formatSearchResultsShort,
type WebSearchInput,
type WebSearchResponse,
} from './webSearch';
import {
executeCode,
formatExecutionResult,
formatExecutionResultShort,
type CodeExecutionInput,
type CodeExecutionResponse,
} from './codeExecution';
import {
webFetch,
formatFetchResult,
formatFetchResultShort,
type WebFetchInput,
type WebFetchResponse,
} from './webFetch';
import {
metasoSearch,
formatMetasoSearchResults,
formatMetasoSearchResultsShort,
getValidImages,
ImageValidationConfig,
type MetasoSearchInput,
type MetasoSearchResponse,
type MetasoImageResult,
type MetasoVideoResult,
} from './metasoSearch';
import {
metasoReader,
formatMetasoReaderResult,
formatMetasoReaderResultShort,
type MetasoReaderInput,
type MetasoReaderResponse,
} from './metasoReader';
import {
translate,
formatTranslateResult,
formatTranslateResultShort,
type TranslateInput,
type TranslateResponse,
} from './translate';
import { shouldUsePyodide, analyzeCode, type LoadingCallback } from './codeAnalyzer';
// 导出代码分析函数供外部使用
export { shouldUsePyodide, analyzeCode, type LoadingCallback } from './codeAnalyzer';
export interface ToolExecutionResult {
success: boolean;
/** 完整结果 - 发送给 AI */
fullResult: string;
/** 简短结果 - 显示给用户 */
displayResult: string;
/** 原始数据 */
rawData?: unknown;
/** Base64 编码的图片数组(代码执行时可能产生) */
images?: string[];
/** 搜索到的图片数组(图片搜索时产生) */
searchImages?: MetasoImageResult[];
/** 搜索到的视频数组(视频搜索时产生) */
searchVideos?: MetasoVideoResult[];
/** 是否需要浏览器端 Pyodide 执行 */
requiresPyodide?: boolean;
/** 代码内容(当 requiresPyodide 为 true 时) */
code?: string;
/** 语言(当 requiresPyodide 为 true 时) */
language?: string;
}
export interface ToolExecutionOptions {
/** Pyodide 加载进度回调 */
onProgress?: LoadingCallback;
/** 秘塔AI API Key */
metasoApiKey?: string;
}
/**
* 执行工具
* @param toolName 工具名称
* @param input 工具输入参数
* @param options 执行选项
* @returns 执行结果(包含完整版和简短版)
*/
export async function executeTool(
toolName: string,
input: Record<string, unknown>,
options?: ToolExecutionOptions
): Promise<ToolExecutionResult> {
const { onProgress, metasoApiKey } = options || {};
try {
switch (toolName) {
case 'web_search': {
const query = String(input.query || '');
const searchInput: WebSearchInput = { query };
const response: WebSearchResponse = await webSearch(searchInput);
return {
success: response.success,
fullResult: formatSearchResults(response),
displayResult: formatSearchResultsShort(response, query),
rawData: response,
};
}
case 'code_execution': {
const language = String(input.language || 'python');
const code = String(input.code || '');
// 检测是否需要浏览器端 Pyodide 执行Python + 图形代码)
if (shouldUsePyodide(code, language)) {
return {
success: true,
fullResult: '需要在浏览器端执行 Python 图形代码',
displayResult: '检测到图形绑制代码,正在准备浏览器端执行...',
requiresPyodide: true,
code,
language,
};
}
// 使用 Piston API 执行(服务端)
const codeInput: CodeExecutionInput = {
code,
language,
stdin: input.stdin ? String(input.stdin) : undefined,
onProgress, // 传递 Pyodide 加载进度回调
};
const response: CodeExecutionResponse = await executeCode(codeInput);
return {
success: response.success,
fullResult: formatExecutionResult(response),
displayResult: formatExecutionResultShort(response, language),
rawData: response,
images: response.images, // 传递图片数据
};
}
case 'web_fetch': {
const fetchInput: WebFetchInput = {
url: String(input.url || ''),
};
const response: WebFetchResponse = await webFetch(fetchInput);
return {
success: response.success,
fullResult: formatFetchResult(response),
displayResult: formatFetchResultShort(response),
rawData: response,
};
}
case 'mita_search': {
const query = String(input.query || '');
const scope = (input.scope as 'webpage' | 'image' | 'video') || 'webpage';
// 图片搜索时请求更多图片用于验证筛选,视频和网页搜索保持原有逻辑
const requestSize = scope === 'image'
? ImageValidationConfig.REQUEST_SIZE
: (input.size ? Number(input.size) : (scope === 'video' ? 5 : 10));
const searchInput: MetasoSearchInput = {
query,
scope,
size: requestSize,
includeSummary: Boolean(input.includeSummary),
};
const response: MetasoSearchResponse = await metasoSearch(searchInput, metasoApiKey || '');
// 如果是图片搜索且成功,验证图片有效性
let validatedImages: MetasoImageResult[] | undefined;
if (scope === 'image' && response.success && response.images && response.images.length > 0) {
// 验证图片,返回指定数量的有效图片
validatedImages = await getValidImages(
response.images,
ImageValidationConfig.TARGET_COUNT,
ImageValidationConfig.CONCURRENCY
);
// 更新 response 中的图片为验证后的有效图片
response.images = validatedImages;
}
return {
success: response.success,
fullResult: formatMetasoSearchResults(response),
displayResult: formatMetasoSearchResultsShort(response, query),
rawData: response,
// 如果是图片搜索,返回验证后的图片数据
searchImages: scope === 'image' ? validatedImages : undefined,
// 如果是视频搜索,返回视频数据
searchVideos: scope === 'video' ? response.videos : undefined,
};
}
case 'mita_reader': {
const url = String(input.url || '');
const readerInput: MetasoReaderInput = {
url,
format: 'Markdown',
};
const response: MetasoReaderResponse = await metasoReader(readerInput, metasoApiKey || '');
return {
success: response.success,
fullResult: formatMetasoReaderResult(response, url),
displayResult: formatMetasoReaderResultShort(response, url),
rawData: response,
};
}
case 'youdao_translate': {
const text = String(input.text || '');
const from = input.from ? String(input.from) : 'auto';
const to = input.to ? String(input.to) : 'zh-CHS';
const translateInput: TranslateInput = { text, from, to };
const response: TranslateResponse = await translate(translateInput);
return {
success: response.success,
fullResult: formatTranslateResult(response, text),
displayResult: formatTranslateResultShort(response, text),
rawData: response,
};
}
default:
return {
success: false,
fullResult: `未知的工具: ${toolName}`,
displayResult: `未知的工具: ${toolName}`,
};
}
} catch (error) {
console.error(`Tool execution error (${toolName}):`, error);
const errorMsg = error instanceof Error ? error.message : '未知错误';
return {
success: false,
fullResult: `工具执行错误: ${errorMsg}`,
displayResult: `工具执行错误: ${errorMsg}`,
};
}
}
/**
* 获取所有可用工具的信息
*/
export function getAvailableTools() {
return [
{
id: 'web_search',
name: '网络搜索',
description: '搜索互联网获取最新信息',
icon: '🔍',
},
{
id: 'web_fetch',
name: '网页获取',
description: '获取指定 URL 的网页内容',
icon: '🌐',
},
{
id: 'mita_search',
name: 'Metaso Search',
description: '秘塔AI智能搜索需要配置API Key',
icon: '🔎',
},
{
id: 'mita_reader',
name: 'Metaso Reader',
description: '秘塔AI网页读取返回Markdown格式',
icon: '📄',
},
{
id: 'youdao_translate',
name: '翻译',
description: '有道智云高质量多语言翻译支持100+种语言',
icon: '🌐',
},
];
}