feat: 更新依赖项,重构 fetch 逻辑,添加工具路由和代理功能
This commit is contained in:
83
src/routes/ai.ts
Normal file
83
src/routes/ai.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { generateText, tool, type ModelMessage, type ToolApprovalResponse } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import { cnb, proxyCnb, models, showMore } from '../common.ts';
|
||||
|
||||
import { app } from './app.ts';
|
||||
import type { App } from '@kevisual/router';
|
||||
|
||||
const createTool = async (app: App, message: { path: string, key: string }) => {
|
||||
const route = app.findRoute({ path: message.path, key: message.key });
|
||||
if (!route) {
|
||||
console.error(`未找到路径 ${message.path} 和 key ${message.key} 的路由`);
|
||||
return null;
|
||||
}
|
||||
const _tool = tool({
|
||||
description: route.description || '无描述',
|
||||
inputSchema: z.object({
|
||||
...route.metadata?.args
|
||||
}), // 这里可以根据实际需要定义输入参数的 schema
|
||||
execute: async (args: any) => {
|
||||
console.log(`执行工具 ${message.path} ${message.key},输入参数:`, args);
|
||||
// 这里可以根据实际需要调用对应的路由处理函数
|
||||
const res = await app.run({ path: message.path, key: message.key, payload: args });
|
||||
// 假设路由处理函数返回一个字符串结果
|
||||
console.log(`工具 ${message.path} ${message.key} 执行结果:`, res);
|
||||
// return '任务列表:' + JSON.stringify(res);
|
||||
return res;
|
||||
}
|
||||
});
|
||||
return _tool;
|
||||
}
|
||||
|
||||
const createTools = async (app: App) => {
|
||||
const tools: Record<string, any> = {};
|
||||
for (const route of app.routes) {
|
||||
const id = route.id!;
|
||||
const _tool = await createTool(app, { path: route.path!, key: route.key! });
|
||||
if (_tool && id) {
|
||||
tools[id] = _tool;
|
||||
}
|
||||
}
|
||||
return tools;
|
||||
}
|
||||
|
||||
const tools = await createTools(app);
|
||||
|
||||
let messages: ModelMessage[] = [
|
||||
{
|
||||
role: 'user',
|
||||
content: '任务步骤:第一步获取今天的任务列表,第二步完成id为1的任务。'
|
||||
}
|
||||
]
|
||||
let result = await generateText({
|
||||
model: proxyCnb(models['auto']),
|
||||
tools,
|
||||
messages
|
||||
// prompt: '今天的任务是什么? 如果有id为1的任务,请完成他。'
|
||||
// prompt: '完成今天的任务1。'
|
||||
});
|
||||
console.log('生成结果:', result.text);
|
||||
console.log('=== 生成内容详情 ===');
|
||||
console.log(showMore(result));
|
||||
|
||||
messages.push(...result.response.messages);
|
||||
|
||||
const result2 = await generateText({
|
||||
model: cnb(models['auto']),
|
||||
tools,
|
||||
messages
|
||||
});
|
||||
console.log('生成结果2:', result2.text);
|
||||
console.log('=== 生成内容详情 ===');
|
||||
console.log(showMore(result2));
|
||||
|
||||
messages.push(...result2.response.messages);
|
||||
|
||||
const result3 = await generateText({
|
||||
model: cnb(models['auto']),
|
||||
tools,
|
||||
messages
|
||||
});
|
||||
console.log('生成结果3:', result3.text);
|
||||
console.log('=== 生成内容详情 ===');
|
||||
console.log(showMore(result3));
|
||||
20
src/routes/ai2.ts
Normal file
20
src/routes/ai2.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { app } from './app.ts';
|
||||
import { cnb, proxyCnb, models, showMore } from '../common.ts';
|
||||
import { runAgent } from './lib.ts';
|
||||
|
||||
const result = await runAgent({
|
||||
app,
|
||||
query:"WHERE path='task' AND key='today'",
|
||||
languageModel: proxyCnb(models['auto']),
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: '任务步骤:第一步获取今天的任务列表,第二步完成id为1的任务。'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
console.log('最终结果:', result.text);
|
||||
|
||||
console.log('=== 生成内容详情 ===');
|
||||
console.log(showMore(result.steps));
|
||||
67
src/routes/app.ts
Normal file
67
src/routes/app.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { App } from '@kevisual/router'
|
||||
import z from 'zod';
|
||||
|
||||
export const app = new App();
|
||||
|
||||
const tasks = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Task 1',
|
||||
description: 'This is the first task.',
|
||||
completed: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Task 2',
|
||||
description: 'This is the second task.',
|
||||
completed: true,
|
||||
},
|
||||
]
|
||||
app.route({
|
||||
path: 'task',
|
||||
key: 'today',
|
||||
description: '获取今日待办任务列表',
|
||||
}).define(async (ctx) => {
|
||||
|
||||
const list = tasks.map(task => ({
|
||||
id: task.id,
|
||||
title: task.title,
|
||||
description: task.description,
|
||||
completed: task.completed,
|
||||
}))
|
||||
ctx.body = { list }
|
||||
}).addTo(app)
|
||||
|
||||
app.route({
|
||||
path: 'task',
|
||||
key: 'done',
|
||||
description: '完成任务',
|
||||
metadata: {
|
||||
args: {
|
||||
id: z.number().describe('任务ID'),
|
||||
}
|
||||
}
|
||||
}).define(async (ctx) => {
|
||||
const { id } = ctx.args as { id: number };
|
||||
const task = tasks.find(t => t.id === id);
|
||||
if (task) {
|
||||
task.completed = true;
|
||||
ctx.body = { message: `任务 ${id} 已完成` };
|
||||
} else {
|
||||
ctx.body = { message: `未找到任务 ${id}` };
|
||||
}
|
||||
}).addTo(app)
|
||||
|
||||
app.route({
|
||||
path: 'task',
|
||||
key: 'list',
|
||||
description: '获取所有任务列表',
|
||||
}).define(async (ctx) => {
|
||||
const list = tasks.map(task => ({
|
||||
id: task.id,
|
||||
title: task.title,
|
||||
description: task.description,
|
||||
completed: task.completed,
|
||||
}))
|
||||
ctx.body = { list }
|
||||
}).addTo(app)
|
||||
66
src/routes/lib.ts
Normal file
66
src/routes/lib.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { App, type RouteInfo } from '@kevisual/router'
|
||||
import { generateText, tool, type ModelMessage, type LanguageModel, type GenerateTextResult } from 'ai';
|
||||
import z from 'zod';
|
||||
import { filter } from '@kevisual/js-filter'
|
||||
const createTool = async (app: App, message: { path: string, key: string }) => {
|
||||
const route = app.findRoute({ path: message.path, key: message.key });
|
||||
if (!route) {
|
||||
console.error(`未找到路径 ${message.path} 和 key ${message.key} 的路由`);
|
||||
return null;
|
||||
}
|
||||
const _tool = tool({
|
||||
description: route.description || '无描述',
|
||||
inputSchema: z.object({
|
||||
...route.metadata?.args
|
||||
}), // 这里可以根据实际需要定义输入参数的 schema
|
||||
execute: async (args: any) => {
|
||||
console.log(`执行工具 ${message.path} ${message.key},输入参数:`, args);
|
||||
// 这里可以根据实际需要调用对应的路由处理函数
|
||||
const res = await app.run({ path: message.path, key: message.key, payload: args });
|
||||
// 假设路由处理函数返回一个字符串结果
|
||||
console.log(`工具 ${message.path} ${message.key} 执行结果:`, res);
|
||||
// return '任务列表:' + JSON.stringify(res);
|
||||
return res;
|
||||
}
|
||||
});
|
||||
return _tool;
|
||||
}
|
||||
|
||||
const createTools = async (app: App) => {
|
||||
const tools: Record<string, any> = {};
|
||||
for (const route of app.routes) {
|
||||
const id = route.id!;
|
||||
const _tool = await createTool(app, { path: route.path!, key: route.key! });
|
||||
if (_tool && id) {
|
||||
tools[id] = _tool;
|
||||
}
|
||||
}
|
||||
return tools;
|
||||
}
|
||||
type Route = Partial<RouteInfo>
|
||||
|
||||
export const reCallAgent = async (opts: { messages?: ModelMessage[], tools?: Record<string, any>, languageModel: LanguageModel }): Promise<GenerateTextResult<Record<string, any>, any>> => {
|
||||
const { messages = [], tools = {}, languageModel } = opts;
|
||||
const result = await generateText({
|
||||
model: languageModel,
|
||||
messages,
|
||||
tools,
|
||||
});
|
||||
const step = result.steps[0]!;
|
||||
if (step.finishReason === 'tool-calls') {
|
||||
messages.push(...result.response.messages);
|
||||
return reCallAgent({ messages, tools, languageModel });
|
||||
}
|
||||
return result
|
||||
}
|
||||
export const runAgent = async (opts: { app: App, messages?: ModelMessage[], routes?: Route[], query?: string, languageModel: LanguageModel }) => {
|
||||
const { app, languageModel } = opts;
|
||||
let messages = opts.messages || [];
|
||||
|
||||
let routes = app.routes;
|
||||
if (opts.query) {
|
||||
routes = filter(routes, opts.query);
|
||||
};
|
||||
const tools = await createTools(app);
|
||||
return await reCallAgent({ messages, tools, languageModel });
|
||||
}
|
||||
Reference in New Issue
Block a user