This commit is contained in:
2025-10-24 00:46:40 +08:00
commit 7fb79cde54
5 changed files with 347 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules
.env
!.env*.example

22
package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "test-router-ai",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT",
"packageManager": "pnpm@10.14.0",
"type": "module",
"dependencies": {
"@kevisual/ai": "^0.0.9",
"@kevisual/router": "^0.0.30",
"@kevisual/use-config": "^1.0.19"
},
"devDependencies": {
"@types/node": "^24.9.1"
}
}

249
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,249 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@kevisual/ai':
specifier: ^0.0.9
version: 0.0.9
'@kevisual/router':
specifier: ^0.0.30
version: 0.0.30
'@kevisual/use-config':
specifier: ^1.0.19
version: 1.0.19(dotenv@16.6.1)
devDependencies:
'@types/node':
specifier: ^24.9.1
version: 24.9.1
packages:
'@kevisual/ai@0.0.9':
resolution: {integrity: sha512-pBe6pjN3pm3zF1CmxR4uQnhxLteGuCiT3JaKOd3pasB5OfEYFJ6qu6RfE+4upnyKiEtAAlYwXMMNR+8S3Jzf5g==}
'@kevisual/load@0.0.6':
resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==}
'@kevisual/logger@0.0.4':
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
'@kevisual/router@0.0.30':
resolution: {integrity: sha512-/mBo7aZFWjT4QfHkI5HPXfdgSwZzt3mAVei7dcNSBTPe9KQSoYKZ8BTq9VTUj3XE0sI6o1bZjlLYvinpVnZilw==}
'@kevisual/use-config@1.0.19':
resolution: {integrity: sha512-Q1IH4eMqUe5w6Bq8etoqOSls9FPIy0xwwD3wHf26EsQLZadhccI9qkDuFzP/rFWDa57mwFPEfwbGE5UlqWOCkw==}
peerDependencies:
dotenv: ^16.4.7
'@types/node@24.9.1':
resolution: {integrity: sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
dotenv@16.6.1:
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
engines: {node: '>=12'}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
fresh@2.0.0:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
mime-db@1.54.0:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'}
mime-types@3.0.1:
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
engines: {node: '>= 0.6'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
node-forge@1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
engines: {node: '>= 6.13.0'}
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
path-to-regexp@8.3.0:
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
selfsigned@3.0.1:
resolution: {integrity: sha512-6U6w6kSLrM9Zxo0D7mC7QdGS6ZZytMWBnj/vhF9p+dAHx6CwGezuRcO4VclTbrrI7mg7SD6zNiqXUuBHOVopNQ==}
engines: {node: '>=10'}
send@1.2.0:
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
engines: {node: '>= 18'}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
statuses@2.0.2:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
snapshots:
'@kevisual/ai@0.0.9':
dependencies:
'@kevisual/logger': 0.0.4
'@kevisual/load@0.0.6':
dependencies:
eventemitter3: 5.0.1
'@kevisual/logger@0.0.4': {}
'@kevisual/router@0.0.30':
dependencies:
path-to-regexp: 8.3.0
selfsigned: 3.0.1
send: 1.2.0
transitivePeerDependencies:
- supports-color
'@kevisual/use-config@1.0.19(dotenv@16.6.1)':
dependencies:
'@kevisual/load': 0.0.6
dotenv: 16.6.1
'@types/node@24.9.1':
dependencies:
undici-types: 7.16.0
debug@4.4.3:
dependencies:
ms: 2.1.3
depd@2.0.0: {}
dotenv@16.6.1: {}
ee-first@1.1.1: {}
encodeurl@2.0.0: {}
escape-html@1.0.3: {}
etag@1.8.1: {}
eventemitter3@5.0.1: {}
fresh@2.0.0: {}
http-errors@2.0.0:
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.1
toidentifier: 1.0.1
inherits@2.0.4: {}
mime-db@1.54.0: {}
mime-types@3.0.1:
dependencies:
mime-db: 1.54.0
ms@2.1.3: {}
node-forge@1.3.1: {}
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
path-to-regexp@8.3.0: {}
range-parser@1.2.1: {}
selfsigned@3.0.1:
dependencies:
node-forge: 1.3.1
send@1.2.0:
dependencies:
debug: 4.4.3
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
fresh: 2.0.0
http-errors: 2.0.0
mime-types: 3.0.1
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.2
transitivePeerDependencies:
- supports-color
setprototypeof@1.2.0: {}
statuses@2.0.1: {}
statuses@2.0.2: {}
toidentifier@1.0.1: {}
undici-types@7.16.0: {}

3
src/config.ts Normal file
View File

@@ -0,0 +1,3 @@
import { useConfig } from '@kevisual/use-config'
export const config = useConfig();

69
src/index.ts Normal file
View File

@@ -0,0 +1,69 @@
import { BailianProvider } from '@kevisual/ai'
import { config } from './config.ts';
import { App } from '@kevisual/router'
import util from 'node:util';
const ai = new BailianProvider({
apiKey: config.BAILIAN_API_KEY || '',
model: 'qwen-turbo-latest',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
})
// ai.getUsageInfo().then(console.log).catch(console.error);
const app = new App()
app.route({
path: 'weather',
description: '获取天气的工具。 参数 city: 城市名称, 如果不传则获取当前定位城市的天气'
}).define(async (ctx) => {
ctx.body = 'Hello World'
}).addTo(app);
app.route({
path: 'address',
description: '获取当前我的城市地址的工具'
}).define(async (ctx) => {
ctx.body = 'Hello World Address'
}).addTo(app);
app.route({
path: 'news',
description: '获取新闻的工具'
}).define(async (ctx) => {
ctx.body = 'Hello World News'
}).addTo(app);
const routes = app.router.getList()
console.log('Routes:', routes);
const callPrompts = `你是一个拥有以下工具的助手:\n\n${routes.map(r => `工具名称: ${r.path}\n描述: ${r.description}\n`).join('\n')}\n\n当用户询问时如果拥有工具请返回 JSON 数据,数据的值的内容是 path description。如果有参数在 payload 当中。其中如果需要调用多个工具,请将它们放在一个数组中。`
const res = await ai.chat([
{
role: 'system',
content: callPrompts
},
{
role: 'user',
content: `我想要获取天气`
},
// { role: 'assistant', content: '请告诉我您想查询哪个城市的天气?' },
// {
// role: 'user',
// content: `北京`
// }
],
)
console.log('AI Response:', res);
const content = res.choices[0].message?.content || ''
console.log(util.inspect(res, { depth: null }))
// const json = JSON.parse(content)
// console.log('Parsed JSON:', json);
// const callApp = await app.call({
// path: json.path,
// })
// console.log('Call App Response:', callApp.body);