feat: 更新依赖项,重构 fetch 逻辑,添加工具路由和代理功能
This commit is contained in:
14
package.json
14
package.json
@@ -4,18 +4,20 @@
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@kevisual/js-filter": "^0.0.5",
|
||||
"@kevisual/router": "^0.1.0",
|
||||
"@types/bun": "latest",
|
||||
"dotenv": "^17.2.3",
|
||||
"dotenv": "^17.3.1",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "^3.0.35",
|
||||
"@ai-sdk/openai": "^3.0.25",
|
||||
"@ai-sdk/openai-compatible": "^2.0.26",
|
||||
"@kevisual/ai": "^0.0.24",
|
||||
"ai": "^6.0.67"
|
||||
"@ai-sdk/anthropic": "^3.0.58",
|
||||
"@ai-sdk/openai": "^3.0.41",
|
||||
"@ai-sdk/openai-compatible": "^2.0.35",
|
||||
"@kevisual/ai": "^0.0.26",
|
||||
"ai": "^6.0.116"
|
||||
}
|
||||
}
|
||||
|
||||
146
pnpm-lock.yaml
generated
146
pnpm-lock.yaml
generated
@@ -9,81 +9,93 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@ai-sdk/anthropic':
|
||||
specifier: ^3.0.35
|
||||
version: 3.0.35(zod@4.3.6)
|
||||
specifier: ^3.0.58
|
||||
version: 3.0.58(zod@4.3.6)
|
||||
'@ai-sdk/openai':
|
||||
specifier: ^3.0.25
|
||||
version: 3.0.25(zod@4.3.6)
|
||||
specifier: ^3.0.41
|
||||
version: 3.0.41(zod@4.3.6)
|
||||
'@ai-sdk/openai-compatible':
|
||||
specifier: ^2.0.26
|
||||
version: 2.0.26(zod@4.3.6)
|
||||
specifier: ^2.0.35
|
||||
version: 2.0.35(zod@4.3.6)
|
||||
'@kevisual/ai':
|
||||
specifier: ^0.0.24
|
||||
version: 0.0.24
|
||||
specifier: ^0.0.26
|
||||
version: 0.0.26
|
||||
ai:
|
||||
specifier: ^6.0.67
|
||||
version: 6.0.67(zod@4.3.6)
|
||||
specifier: ^6.0.116
|
||||
version: 6.0.116(zod@4.3.6)
|
||||
typescript:
|
||||
specifier: ^5
|
||||
version: 5.9.3
|
||||
devDependencies:
|
||||
'@kevisual/js-filter':
|
||||
specifier: ^0.0.5
|
||||
version: 0.0.5
|
||||
'@kevisual/router':
|
||||
specifier: ^0.1.0
|
||||
version: 0.1.0
|
||||
'@types/bun':
|
||||
specifier: latest
|
||||
version: 1.3.8
|
||||
dotenv:
|
||||
specifier: ^17.2.3
|
||||
version: 17.2.3
|
||||
specifier: ^17.3.1
|
||||
version: 17.3.1
|
||||
zod:
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6
|
||||
|
||||
packages:
|
||||
|
||||
'@ai-sdk/anthropic@3.0.35':
|
||||
resolution: {integrity: sha512-Y3g/5uVj621XSB9lGF7WrD7qR+orhV5xpaYkRF8kfj2j4W7e7BBGIvxcdsCf85FjJbc6tKQdNTZ84ZEqT3Y5TQ==}
|
||||
'@ai-sdk/anthropic@3.0.58':
|
||||
resolution: {integrity: sha512-/53SACgmVukO4bkms4dpxpRlYhW8Ct6QZRe6sj1Pi5H00hYhxIrqfiLbZBGxkdRvjsBQeP/4TVGsXgH5rQeb8Q==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/gateway@3.0.32':
|
||||
resolution: {integrity: sha512-7clZRr07P9rpur39t1RrbIe7x8jmwnwUWI8tZs+BvAfX3NFgdSVGGIaT7bTz2pb08jmLXzTSDbrOTqAQ7uBkBQ==}
|
||||
'@ai-sdk/gateway@3.0.66':
|
||||
resolution: {integrity: sha512-SIQ0YY0iMuv+07HLsZ+bB990zUJ6S4ujORAh+Jv1V2KGNn73qQKnGO0JBk+w+Res8YqOFSycwDoWcFlQrVxS4A==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/openai-compatible@2.0.26':
|
||||
resolution: {integrity: sha512-l6jdFjI1C2eDAEm7oo+dnRn0oG1EkcyqfbEZ7ozT0TnYrah6amX2JkftYMP1GRzNtAeCB3WNN8XspXdmi6ZNlQ==}
|
||||
'@ai-sdk/openai-compatible@2.0.35':
|
||||
resolution: {integrity: sha512-g3wA57IAQFb+3j4YuFndgkUdXyRETZVvbfAWM+UX7bZSxA3xjes0v3XKgIdKdekPtDGsh4ZX2byHD0gJIMPfiA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/openai@3.0.25':
|
||||
resolution: {integrity: sha512-DsaN46R98+D1W3lU3fKuPU3ofacboLaHlkAwxJPgJ8eup1AJHmPK1N1y10eJJbJcF6iby8Tf/vanoZxc9JPUfw==}
|
||||
'@ai-sdk/openai@3.0.41':
|
||||
resolution: {integrity: sha512-IZ42A+FO+vuEQCVNqlnAPYQnnUpUfdJIwn1BEDOBywiEHa23fw7PahxVtlX9zm3/zMvTW4JKPzWyvAgDu+SQ2A==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/provider-utils@4.0.13':
|
||||
resolution: {integrity: sha512-HHG72BN4d+OWTcq2NwTxOm/2qvk1duYsnhCDtsbYwn/h/4zeqURu1S0+Cn0nY2Ysq9a9HGKvrYuMn9bgFhR2Og==}
|
||||
'@ai-sdk/provider-utils@4.0.19':
|
||||
resolution: {integrity: sha512-3eG55CrSWCu2SXlqq2QCsFjo3+E7+Gmg7i/oRVoSZzIodTuDSfLb3MRje67xE9RFea73Zao7Lm4mADIfUETKGg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/provider@3.0.7':
|
||||
resolution: {integrity: sha512-VkPLrutM6VdA924/mG8OS+5frbVTcu6e046D2bgDo00tehBANR1QBJ/mPcZ9tXMFOsVcm6SQArOregxePzTFPw==}
|
||||
'@ai-sdk/provider@3.0.8':
|
||||
resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@kevisual/ai@0.0.24':
|
||||
resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==}
|
||||
'@kevisual/ai@0.0.26':
|
||||
resolution: {integrity: sha512-lhaMpxi+vgqPdyBKiuNbSil4hy13tNLbDiqCtG0qUXKtvoowK6xMx269pSSYkYBivczM8g8I0XEouuJceUpJPg==}
|
||||
|
||||
'@kevisual/js-filter@0.0.5':
|
||||
resolution: {integrity: sha512-+S+Sf3K/aP6XtZI2s7TgKOr35UuvUvtpJ9YDW30a+mY0/N8gRuzyKhieBzQN7Ykayzz70uoMavBXut2rUlLgzw==}
|
||||
|
||||
'@kevisual/logger@0.0.4':
|
||||
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
|
||||
|
||||
'@kevisual/permission@0.0.3':
|
||||
resolution: {integrity: sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA==}
|
||||
'@kevisual/permission@0.0.4':
|
||||
resolution: {integrity: sha512-zwBYPnT/z21W4q2wkklJrxvoYBYWG/+a3iXFDKqXQAnDOcxm/SU1f1N6FQb9KxGKl36/fclVlhxlxqszvKCenQ==}
|
||||
|
||||
'@kevisual/query@0.0.38':
|
||||
resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==}
|
||||
'@kevisual/query@0.0.52':
|
||||
resolution: {integrity: sha512-m1UbyDTIxtfAQXM+EqhXA4ytE2V8rV8mXTZVBwzfW9O6+gtvAcRY7K1YYxfewTSXLVh9nwvfHe0KQ8MDL5ukyw==}
|
||||
|
||||
'@kevisual/router@0.1.0':
|
||||
resolution: {integrity: sha512-7NHoKP36uWkTDp/hxeUBMtawma91BrOAwG/caOrVsO8tM3wjqhlmCt0sIvLBM+snVJkmylROQR0WGaygd3JqYw==}
|
||||
|
||||
'@opentelemetry/api@1.9.0':
|
||||
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
|
||||
@@ -102,8 +114,8 @@ packages:
|
||||
resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==}
|
||||
engines: {node: '>= 20'}
|
||||
|
||||
ai@6.0.67:
|
||||
resolution: {integrity: sha512-xBnTcByHCj3OcG6V8G1s6zvSEqK0Bdiu+IEXYcpGrve1iGFFRgcrKeZtr/WAW/7gupnSvBbDF24BEv1OOfqi1g==}
|
||||
ai@6.0.116:
|
||||
resolution: {integrity: sha512-7yM+cTmyRLeNIXwt4Vj+mrrJgVQ9RMIW5WO0ydoLoYkewIvsMcvUmqS4j2RJTUXaF1HphwmSKUMQ/HypNRGOmA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
@@ -111,10 +123,13 @@ packages:
|
||||
bun-types@1.3.8:
|
||||
resolution: {integrity: sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q==}
|
||||
|
||||
dotenv@17.2.3:
|
||||
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
|
||||
dotenv@17.3.1:
|
||||
resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
es-toolkit@1.45.1:
|
||||
resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==}
|
||||
|
||||
eventsource-parser@3.0.6:
|
||||
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -122,9 +137,6 @@ packages:
|
||||
json-schema@0.4.0:
|
||||
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
@@ -138,55 +150,59 @@ packages:
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ai-sdk/anthropic@3.0.35(zod@4.3.6)':
|
||||
'@ai-sdk/anthropic@3.0.58(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.7
|
||||
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.19(zod@4.3.6)
|
||||
zod: 4.3.6
|
||||
|
||||
'@ai-sdk/gateway@3.0.32(zod@4.3.6)':
|
||||
'@ai-sdk/gateway@3.0.66(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.7
|
||||
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.19(zod@4.3.6)
|
||||
'@vercel/oidc': 3.1.0
|
||||
zod: 4.3.6
|
||||
|
||||
'@ai-sdk/openai-compatible@2.0.26(zod@4.3.6)':
|
||||
'@ai-sdk/openai-compatible@2.0.35(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.7
|
||||
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.19(zod@4.3.6)
|
||||
zod: 4.3.6
|
||||
|
||||
'@ai-sdk/openai@3.0.25(zod@4.3.6)':
|
||||
'@ai-sdk/openai@3.0.41(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.7
|
||||
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.19(zod@4.3.6)
|
||||
zod: 4.3.6
|
||||
|
||||
'@ai-sdk/provider-utils@4.0.13(zod@4.3.6)':
|
||||
'@ai-sdk/provider-utils@4.0.19(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.7
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@standard-schema/spec': 1.1.0
|
||||
eventsource-parser: 3.0.6
|
||||
zod: 4.3.6
|
||||
|
||||
'@ai-sdk/provider@3.0.7':
|
||||
'@ai-sdk/provider@3.0.8':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@kevisual/ai@0.0.24':
|
||||
'@kevisual/ai@0.0.26':
|
||||
dependencies:
|
||||
'@kevisual/logger': 0.0.4
|
||||
'@kevisual/permission': 0.0.3
|
||||
'@kevisual/query': 0.0.38
|
||||
'@kevisual/permission': 0.0.4
|
||||
'@kevisual/query': 0.0.52
|
||||
|
||||
'@kevisual/js-filter@0.0.5': {}
|
||||
|
||||
'@kevisual/logger@0.0.4': {}
|
||||
|
||||
'@kevisual/permission@0.0.3': {}
|
||||
'@kevisual/permission@0.0.4': {}
|
||||
|
||||
'@kevisual/query@0.0.38':
|
||||
'@kevisual/query@0.0.52': {}
|
||||
|
||||
'@kevisual/router@0.1.0':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
es-toolkit: 1.45.1
|
||||
|
||||
'@opentelemetry/api@1.9.0': {}
|
||||
|
||||
@@ -202,11 +218,11 @@ snapshots:
|
||||
|
||||
'@vercel/oidc@3.1.0': {}
|
||||
|
||||
ai@6.0.67(zod@4.3.6):
|
||||
ai@6.0.116(zod@4.3.6):
|
||||
dependencies:
|
||||
'@ai-sdk/gateway': 3.0.32(zod@4.3.6)
|
||||
'@ai-sdk/provider': 3.0.7
|
||||
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
|
||||
'@ai-sdk/gateway': 3.0.66(zod@4.3.6)
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.19(zod@4.3.6)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
zod: 4.3.6
|
||||
|
||||
@@ -214,14 +230,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 25.2.0
|
||||
|
||||
dotenv@17.2.3: {}
|
||||
dotenv@17.3.1: {}
|
||||
|
||||
es-toolkit@1.45.1: {}
|
||||
|
||||
eventsource-parser@3.0.6: {}
|
||||
|
||||
json-schema@0.4.0: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
|
||||
@@ -2,6 +2,24 @@ import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
||||
import { createAnthropic } from '@ai-sdk/anthropic';
|
||||
import { generateText } from 'ai';
|
||||
import 'dotenv/config';
|
||||
import util from 'node:util';
|
||||
// 保存原始 fetch 引用
|
||||
const originalFetch = fetch;
|
||||
|
||||
export const defaultFetch = {
|
||||
fetch: async (input: string | URL | Request, init?: BunFetchRequestInit) => {
|
||||
console.log('请求 URL:', input);
|
||||
console.log('请求选项:', init);
|
||||
|
||||
const response = await originalFetch(input, init); // 调用原始 fetch
|
||||
|
||||
console.log('响应状态:', response.status);
|
||||
const responseBody = await response.clone().text(); // 使用 clone 避免消耗原始 response
|
||||
console.log('响应体:', responseBody);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveEnvVars(value: string): string {
|
||||
return value.replace(/{env:([^}]+)}/g, (_, varName) => {
|
||||
@@ -19,6 +37,8 @@ export const models = {
|
||||
'MiniMax-M2.1': 'MiniMax-M2.1',
|
||||
'qwen3-coder-plus': 'qwen3-coder-plus',
|
||||
'hunyuan-a13b': 'hunyuan-a13b',
|
||||
'qwen-plus': 'qwen-plus',
|
||||
'auto': 'AUTO_Models',
|
||||
}
|
||||
export const bailian = createOpenAICompatible({
|
||||
baseURL: 'https://coding.dashscope.aliyuncs.com/v1',
|
||||
@@ -36,6 +56,7 @@ export const minimax = createAnthropic({
|
||||
baseURL: 'https://api.minimaxi.com/anthropic/v1',
|
||||
name: 'custom-minimax',
|
||||
apiKey: process.env.MINIMAX_API_KEY!,
|
||||
fetch: defaultFetch.fetch as any
|
||||
});
|
||||
|
||||
export const doubao = createOpenAICompatible({
|
||||
@@ -44,8 +65,20 @@ export const doubao = createOpenAICompatible({
|
||||
apiKey: process.env.DOUBAO_API_KEY!,
|
||||
});
|
||||
|
||||
|
||||
export const cnb = createOpenAICompatible({
|
||||
baseURL: resolveEnvVars('https://api.cnb.cool/{env:CNB_REPO_SLUG}/-/ai/'),
|
||||
baseURL: 'https://api.cnb.cool/kevisual/kevisual/-/ai/',
|
||||
// baseURL: resolveEnvVars('https://api.cnb.cool/{env:CNB_REPO_SLUG}/-/ai/'),
|
||||
name: 'custom-cnb',
|
||||
apiKey: process.env.CNB_API_KEY!,
|
||||
fetch: defaultFetch.fetch as any
|
||||
});
|
||||
export const proxyCnb = createOpenAICompatible({
|
||||
baseURL: 'http://localhost:4005/api',
|
||||
name: 'proxy-cnb',
|
||||
apiKey: process.env.CNB_API_KEY!,
|
||||
});
|
||||
|
||||
export const showMore = (obj: any) => {
|
||||
return util.inspect(obj, { depth: null, colors: true });
|
||||
}
|
||||
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 });
|
||||
}
|
||||
@@ -17,7 +17,7 @@ function resolveEnvVars(value: string): string {
|
||||
const cnb = createOpenAICompatible({
|
||||
baseURL: resolveEnvVars('{env:CNB_API_ENDPOINT}/{env:CNB_REPO_SLUG}/-/ai/'),
|
||||
name: 'custom-cnb',
|
||||
apiKey: process.env.CNB_API_KEY!,
|
||||
// apiKey: process.env.CNB_API_KEY!,
|
||||
});
|
||||
|
||||
const { text } = await generateText({
|
||||
|
||||
9
src/test-minimax.ts
Normal file
9
src/test-minimax.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { minimax } from "./common"
|
||||
import { generateText } from 'ai';
|
||||
|
||||
const { text } = await generateText({
|
||||
model: minimax('MiniMax-M2.1'),
|
||||
prompt: 'What is an agent?',
|
||||
});
|
||||
|
||||
console.log('Response:', text);
|
||||
Reference in New Issue
Block a user