feat(core): 将 Tavily API Key 从环境变量迁移到系统配置

- 新增 ServiceConfig 类型和 services 配置字段
- 添加 getServiceApiKey/saveServiceConfig/deleteServiceConfig API
- 更新 web_search 和 web_extract 工具使用配置系统
- 新增 /api/services REST 端点管理第三方服务配置
This commit is contained in:
2025-12-17 13:01:29 +08:00
parent bb3d42e6bf
commit 2afa7bb103
9 changed files with 238 additions and 10 deletions
+1
View File
@@ -14,5 +14,6 @@ export { hooksRouter } from './hooks.js';
export { agentsRouter } from './agents.js';
export { checkpointsRouter } from './checkpoints.js';
export { providersRouter } from './providers.js';
export { servicesRouter } from './services.js';
export { contextRouter } from './context.js';
export { lspRouter } from './lsp.js';
+155
View File
@@ -0,0 +1,155 @@
/**
* Services API Routes
*
* 第三方服务配置管理 REST API(如 Tavily
*/
import { Hono } from 'hono';
import type { ServiceConfig, ServiceType } from '@ai-assistant/core';
import {
loadProvidersConfig,
getServiceConfig,
saveServiceConfig,
deleteServiceConfig,
} from '@ai-assistant/core';
export const servicesRouter = new Hono();
/** 服务元信息 */
interface ServiceInfo {
id: ServiceType;
name: string;
description: string;
website: string;
}
/** 已知服务列表 */
const KNOWN_SERVICES: ServiceInfo[] = [
{
id: 'tavily',
name: 'Tavily',
description: '网络搜索和网页内容提取 API',
website: 'https://tavily.com',
},
];
/**
* GET /services - List all services
*/
servicesRouter.get('/', async (c) => {
try {
const config = await loadProvidersConfig();
const services = config.services ?? {};
const data = KNOWN_SERVICES.map((info) => {
const serviceConfig = services[info.id];
return {
...info,
enabled: serviceConfig?.enabled ?? false,
hasApiKey: !!serviceConfig?.apiKey,
};
});
return c.json({
success: true,
data,
});
} catch (error) {
return c.json(
{
success: false,
error: error instanceof Error ? error.message : 'Failed to list services',
},
500
);
}
});
/**
* GET /services/:id - Get service config
*/
servicesRouter.get('/:id', async (c) => {
const id = c.req.param('id') as ServiceType;
try {
const info = KNOWN_SERVICES.find((s) => s.id === id);
if (!info) {
return c.json({ success: false, error: `Unknown service: ${id}` }, 404);
}
const serviceConfig = await getServiceConfig(id);
return c.json({
success: true,
data: {
...info,
enabled: serviceConfig?.enabled ?? false,
hasApiKey: !!serviceConfig?.apiKey,
},
});
} catch (error) {
return c.json(
{
success: false,
error: error instanceof Error ? error.message : 'Failed to get service',
},
500
);
}
});
/**
* PUT /services/:id - Update service config
*/
servicesRouter.put('/:id', async (c) => {
const id = c.req.param('id') as ServiceType;
try {
const info = KNOWN_SERVICES.find((s) => s.id === id);
if (!info) {
return c.json({ success: false, error: `Unknown service: ${id}` }, 404);
}
const body = await c.req.json();
const { apiKey, enabled } = body as { apiKey?: string; enabled?: boolean };
await saveServiceConfig(id, { apiKey, enabled });
return c.json({
success: true,
message: `Service ${id} config updated`,
});
} catch (error) {
return c.json(
{
success: false,
error: error instanceof Error ? error.message : 'Failed to update service config',
},
400
);
}
});
/**
* DELETE /services/:id - Delete service config
*/
servicesRouter.delete('/:id', async (c) => {
const id = c.req.param('id') as ServiceType;
try {
await deleteServiceConfig(id);
return c.json({
success: true,
message: `Service ${id} config removed`,
});
} catch (error) {
return c.json(
{
success: false,
error: error instanceof Error ? error.message : 'Failed to remove service config',
},
400
);
}
});