@maiyunnet/kebab 7.7.0 → 7.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/doc/kebab-rag.md +136 -126
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/crypto.js +1 -0
- package/lib/sql.d.ts +8 -6
- package/lib/sql.js +18 -6
- package/package.json +12 -12
- package/sys/master.js +4 -4
- package/sys/mod.js +8 -2
- package/www/example/ctr/agent.d.ts +8 -0
- package/www/example/ctr/agent.js +68 -0
- package/www/example/ctr/test.js +7 -0
- package/www/example/skill/weather.d.ts +15 -0
- package/www/example/skill/weather.js +36 -0
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '7.
|
|
9
|
+
export const VER = '7.8.0';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/lib/crypto.js
CHANGED
|
@@ -31,6 +31,7 @@ export function generateKeyPair(type, options = {}) {
|
|
|
31
31
|
};
|
|
32
32
|
options.privateKeyEncoding.type ??= 'pkcs8';
|
|
33
33
|
options.privateKeyEncoding.format ??= 'pem';
|
|
34
|
+
/** --- 由于 generateKeyPair 重载过多且 options 为 any,此处参数也采用 any --- */
|
|
34
35
|
crypto.generateKeyPair(type, options, (err, publicKey, privateKey) => {
|
|
35
36
|
resolve({
|
|
36
37
|
'private': privateKey,
|
package/lib/sql.d.ts
CHANGED
|
@@ -12,16 +12,18 @@ export declare enum ESERVICE {
|
|
|
12
12
|
}
|
|
13
13
|
/** --- JSON 查询操作符 --- */
|
|
14
14
|
export declare enum EJSON {
|
|
15
|
-
/** ---
|
|
15
|
+
/** --- 包含值 (MySQL: JSON_CONTAINS, PG: @>) --- */
|
|
16
16
|
'CONTAINS' = "json",
|
|
17
|
-
/** ---
|
|
17
|
+
/** --- 被包含值 (MySQL: JSON_CONTAINS, PG: <@) --- */
|
|
18
18
|
'CONTAINED_BY' = "json_in",
|
|
19
|
-
/** --- 存在 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
|
|
19
|
+
/** --- 存在 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
|
|
20
20
|
'HAS_KEY' = "json_key",
|
|
21
|
-
/** --- 存在任意 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
|
|
21
|
+
/** --- 存在任意 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
|
|
22
22
|
'HAS_ANY_KEYS' = "json_any",
|
|
23
|
-
/** --- 存在所有 Key (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
|
|
24
|
-
'HAS_ALL_KEYS' = "json_all"
|
|
23
|
+
/** --- 存在所有 Key 不含值 (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
|
|
24
|
+
'HAS_ALL_KEYS' = "json_all",
|
|
25
|
+
/** --- 简单数组重叠 (MySQL: JSON_OVERLAPS, PG: &&) --- */
|
|
26
|
+
'OVERLAPS' = "json_overlaps"
|
|
25
27
|
}
|
|
26
28
|
export declare class Sql {
|
|
27
29
|
/** --- ctr 对象 --- */
|
package/lib/sql.js
CHANGED
|
@@ -11,16 +11,18 @@ export var ESERVICE;
|
|
|
11
11
|
/** --- JSON 查询操作符 --- */
|
|
12
12
|
export var EJSON;
|
|
13
13
|
(function (EJSON) {
|
|
14
|
-
/** ---
|
|
14
|
+
/** --- 包含值 (MySQL: JSON_CONTAINS, PG: @>) --- */
|
|
15
15
|
EJSON["CONTAINS"] = "json";
|
|
16
|
-
/** ---
|
|
16
|
+
/** --- 被包含值 (MySQL: JSON_CONTAINS, PG: <@) --- */
|
|
17
17
|
EJSON["CONTAINED_BY"] = "json_in";
|
|
18
|
-
/** --- 存在 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
|
|
18
|
+
/** --- 存在 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?) --- */
|
|
19
19
|
EJSON["HAS_KEY"] = "json_key";
|
|
20
|
-
/** --- 存在任意 Key (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
|
|
20
|
+
/** --- 存在任意 Key 不含值 (MySQL: JSON_CONTAINS_PATH one, PG: ?|) --- */
|
|
21
21
|
EJSON["HAS_ANY_KEYS"] = "json_any";
|
|
22
|
-
/** --- 存在所有 Key (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
|
|
22
|
+
/** --- 存在所有 Key 不含值 (MySQL: JSON_CONTAINS_PATH all, PG: ?&) --- */
|
|
23
23
|
EJSON["HAS_ALL_KEYS"] = "json_all";
|
|
24
|
+
/** --- 简单数组重叠 (MySQL: JSON_OVERLAPS, PG: &&) --- */
|
|
25
|
+
EJSON["OVERLAPS"] = "json_overlaps";
|
|
24
26
|
})(EJSON || (EJSON = {}));
|
|
25
27
|
/** --- field 用 token --- */
|
|
26
28
|
let columnToken = '';
|
|
@@ -452,7 +454,7 @@ export class Sql {
|
|
|
452
454
|
data.push(...v[1]);
|
|
453
455
|
}
|
|
454
456
|
}
|
|
455
|
-
else if (typeof v[1] === 'string' && ['json', 'json_in', 'json_key', 'json_any', 'json_all'].includes(v[1].toLowerCase())) {
|
|
457
|
+
else if (typeof v[1] === 'string' && ['json', 'json_in', 'json_key', 'json_any', 'json_all', 'json_overlaps'].includes(v[1].toLowerCase())) {
|
|
456
458
|
// --- json ---
|
|
457
459
|
const op = v[1].toLowerCase();
|
|
458
460
|
const nv = v[2];
|
|
@@ -476,6 +478,16 @@ export class Sql {
|
|
|
476
478
|
data.push(lText.stringifyJson(nv));
|
|
477
479
|
}
|
|
478
480
|
}
|
|
481
|
+
else if (op === 'json_overlaps') {
|
|
482
|
+
if (this._service === ESERVICE.MYSQL) {
|
|
483
|
+
sql += `JSON_OVERLAPS(${this.field(v[0])}, ${this._placeholder()}) AND `;
|
|
484
|
+
data.push(lText.stringifyJson(nv));
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
sql += `${this.field(v[0])} && ${this._placeholder()} AND `;
|
|
488
|
+
data.push(nv);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
479
491
|
else {
|
|
480
492
|
// --- json_key, json_any, json_all ---
|
|
481
493
|
const keys = Array.isArray(nv) ? nv : [nv];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maiyunnet/kebab",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -22,30 +22,30 @@
|
|
|
22
22
|
"#kebab/*": "./*"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@aws-sdk/client-s3": "^3.
|
|
26
|
-
"@aws-sdk/lib-storage": "^3.
|
|
25
|
+
"@aws-sdk/client-s3": "^3.975.0",
|
|
26
|
+
"@aws-sdk/lib-storage": "^3.975.0",
|
|
27
27
|
"@litert/http-client": "^1.1.2",
|
|
28
28
|
"@litert/mime": "^0.1.3",
|
|
29
29
|
"@litert/redis": "^3.1.0",
|
|
30
30
|
"@litert/websocket": "^0.2.8",
|
|
31
31
|
"@types/ssh2": "^1.15.5",
|
|
32
|
-
"@zilliz/milvus2-sdk-node": "^2.6.
|
|
33
|
-
"ejs": "^
|
|
32
|
+
"@zilliz/milvus2-sdk-node": "^2.6.9",
|
|
33
|
+
"ejs": "^4.0.1",
|
|
34
34
|
"jszip": "^3.10.1",
|
|
35
|
-
"mysql2": "^3.
|
|
35
|
+
"mysql2": "^3.16.1",
|
|
36
36
|
"node-cron": "^4.2.1",
|
|
37
|
-
"openai": "^6.
|
|
38
|
-
"pg": "^8.
|
|
37
|
+
"openai": "^6.16.0",
|
|
38
|
+
"pg": "^8.17.2",
|
|
39
39
|
"ssh2": "^1.17.0",
|
|
40
40
|
"svg-captcha": "^1.4.0",
|
|
41
|
-
"tencentcloud-sdk-nodejs": "^4.1.
|
|
41
|
+
"tencentcloud-sdk-nodejs": "^4.1.175"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@litert/eslint-plugin-rules": "^0.3.1",
|
|
45
45
|
"@types/ejs": "^3.1.5",
|
|
46
|
-
"@types/node": "^
|
|
47
|
-
"@types/pg": "^8.
|
|
48
|
-
"typedoc": "^0.28.
|
|
46
|
+
"@types/node": "^25.0.10",
|
|
47
|
+
"@types/pg": "^8.16.0",
|
|
48
|
+
"typedoc": "^0.28.16",
|
|
49
49
|
"typedoc-plugin-markdown": "^4.9.0",
|
|
50
50
|
"typescript": "^5.9.3"
|
|
51
51
|
}
|
package/sys/master.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Date: 2019-5-2 21:03:42
|
|
4
4
|
* Last: 2020-3-7 10:33:17, 2022-07-22 13:40:10, 2022-09-06 22:40:58, 2024-2-7 01:44:59, 2024-7-2 15:17:09, 2025-6-13 13:06:43, 2025-12-5 13:15:03
|
|
5
5
|
*/
|
|
6
|
+
import cluster from 'cluster';
|
|
6
7
|
import * as os from 'os';
|
|
7
|
-
import * as cluster from 'cluster';
|
|
8
8
|
import * as http from 'http';
|
|
9
9
|
// --- 库和定义 ---
|
|
10
10
|
import * as kebab from '#kebab/index.js';
|
|
@@ -22,7 +22,7 @@ const workerList = {};
|
|
|
22
22
|
*/
|
|
23
23
|
async function run() {
|
|
24
24
|
// --- 设置 cluster 调度策略为 round-robin(在支持的平台上启用负载均衡) ---
|
|
25
|
-
cluster.
|
|
25
|
+
cluster.schedulingPolicy = cluster.SCHED_RR;
|
|
26
26
|
// --- 读取配置文件 ---
|
|
27
27
|
const configContent = await lFs.getContent(kebab.CONF_CWD + 'config.json', 'utf8');
|
|
28
28
|
if (!configContent) {
|
|
@@ -48,7 +48,7 @@ async function run() {
|
|
|
48
48
|
for (let i = 0; i < cpuLengthMax; ++i) {
|
|
49
49
|
await createChildProcess(i);
|
|
50
50
|
}
|
|
51
|
-
cluster.
|
|
51
|
+
cluster.on('listening', function (worker, address) {
|
|
52
52
|
// --- 子进程开始监听 ---
|
|
53
53
|
lCore.display(`[master] Listening: worker ${worker.process.pid ?? 'undefined'}, Address: ${address.address}:${address.port}.`);
|
|
54
54
|
}).on('exit', function (worker, code) {
|
|
@@ -419,7 +419,7 @@ async function checkWorkerLost() {
|
|
|
419
419
|
* @param cpu CPU ID
|
|
420
420
|
*/
|
|
421
421
|
async function createChildProcess(cpu) {
|
|
422
|
-
const worker = cluster.
|
|
422
|
+
const worker = cluster.fork();
|
|
423
423
|
if (!worker.process.pid) {
|
|
424
424
|
return;
|
|
425
425
|
}
|
package/sys/mod.js
CHANGED
|
@@ -615,8 +615,11 @@ export default class Mod {
|
|
|
615
615
|
}
|
|
616
616
|
// --- 未处理的错误 ---
|
|
617
617
|
const service = this._db.getService();
|
|
618
|
-
lCore.debug('[MOD][create0][' + cstr._$table + ']', service !== null ? lDb.ESERVICE[service] : 'NONE', r);
|
|
619
618
|
lCore.log(this._ctr ?? {}, '[MOD][create0][' + cstr._$table + '] ' + lText.stringifyJson(r.error?.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
|
|
619
|
+
if (lCore.globalConfig.debug) {
|
|
620
|
+
lCore.debug('[MOD][create0][' + cstr._$table + ']', service !== null ? lDb.ESERVICE[service] : 'NONE', r);
|
|
621
|
+
lCore.debug(this._sql.format());
|
|
622
|
+
}
|
|
620
623
|
return false;
|
|
621
624
|
}
|
|
622
625
|
}
|
|
@@ -626,8 +629,11 @@ export default class Mod {
|
|
|
626
629
|
r = await this._db.execute(this._sql.getSql(), this._sql.getData());
|
|
627
630
|
if (r.error) {
|
|
628
631
|
if (r.error.errno !== 1062) {
|
|
629
|
-
lCore.debug('[MOD][create1][' + cstr._$table + ']', r);
|
|
630
632
|
lCore.log(this._ctr ?? {}, '[MOD][create1][' + cstr._$table + '] ' + lText.stringifyJson(r.error?.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
|
|
633
|
+
if (lCore.globalConfig.debug) {
|
|
634
|
+
lCore.debug('[MOD][create1][' + cstr._$table + ']', r);
|
|
635
|
+
lCore.debug(this._sql.format());
|
|
636
|
+
}
|
|
631
637
|
return false;
|
|
632
638
|
}
|
|
633
639
|
return null;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as sCtr from '#kebab/sys/ctr.js';
|
|
2
|
+
import * as lAi from '#kebab/lib/ai.js';
|
|
3
|
+
import * as lFs from '#kebab/lib/fs.js';
|
|
4
|
+
import * as lText from '#kebab/lib/text.js';
|
|
5
|
+
/** --- Skill 导入 --- */
|
|
6
|
+
import sWeather from '../skill/weather.js';
|
|
7
|
+
export default class extends sCtr.Ctr {
|
|
8
|
+
/**
|
|
9
|
+
* --- Agent 示例接口 ---
|
|
10
|
+
* --- 访问:/agent?q=上海天气怎么样? ---
|
|
11
|
+
*/
|
|
12
|
+
async index() {
|
|
13
|
+
// --- 1. 获取用户问题 ---
|
|
14
|
+
const userPrompt = this._get['q'] ?? '你好';
|
|
15
|
+
// --- 2. 加载系统提示词 ---
|
|
16
|
+
const systemPrompt = await lFs.getContent(`${this._config.const.rootPath}www/example/prompt/agent.txt`, 'utf8') ?? '';
|
|
17
|
+
// --- 3. 初始化 AI ---
|
|
18
|
+
const ai = lAi.get(this, {
|
|
19
|
+
'service': lAi.ESERVICE.ALICN,
|
|
20
|
+
});
|
|
21
|
+
// --- 4. 准备消息 ---
|
|
22
|
+
const messages = [
|
|
23
|
+
{ 'role': 'system', 'content': systemPrompt },
|
|
24
|
+
{ 'role': 'user', 'content': userPrompt },
|
|
25
|
+
];
|
|
26
|
+
// --- 5. 第一次对话,传入 Skills 定义 ---
|
|
27
|
+
const res = await ai.chat({
|
|
28
|
+
'model': 'qwen-plus',
|
|
29
|
+
'messages': messages,
|
|
30
|
+
'tools': [sWeather.definition],
|
|
31
|
+
});
|
|
32
|
+
if (!res) {
|
|
33
|
+
return '系统错误';
|
|
34
|
+
}
|
|
35
|
+
const message = res.choices[0].message;
|
|
36
|
+
// --- 6. 如果 AI 决定调用工具 ---
|
|
37
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
38
|
+
messages.push(message);
|
|
39
|
+
for (const toolCall of message.tool_calls) {
|
|
40
|
+
if (toolCall.function.name === 'get_weather') {
|
|
41
|
+
// --- 解析参数并执行 ---
|
|
42
|
+
const args = lText.parseJson(toolCall.function.arguments);
|
|
43
|
+
if (args === false) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const result = await sWeather.execute(args);
|
|
47
|
+
// --- 将结果反馈给消息流 ---
|
|
48
|
+
messages.push({
|
|
49
|
+
'role': 'tool',
|
|
50
|
+
'tool_call_id': toolCall.id,
|
|
51
|
+
'name': 'get_weather',
|
|
52
|
+
'content': result,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// --- 7. 第二次对话,获取最终文本回复 ---
|
|
57
|
+
const finalRes = await ai.chat({
|
|
58
|
+
'model': 'qwen-plus',
|
|
59
|
+
'messages': messages,
|
|
60
|
+
});
|
|
61
|
+
if (finalRes) {
|
|
62
|
+
return finalRes.choices[0].message.content ?? '未能生成回复';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// --- 如果不需要工具调用,直接返回内容 ---
|
|
66
|
+
return message.content ?? '未能生成回复';
|
|
67
|
+
}
|
|
68
|
+
}
|
package/www/example/ctr/test.js
CHANGED
|
@@ -2601,6 +2601,13 @@ Result:<pre id="result">Nothing.</pre>`);
|
|
|
2601
2601
|
echo.push(`<pre>sql.select('*', 'user').where([['info', lSql.EJSON.HAS_ALL_KEYS, ['age', 'name']]]);</pre>
|
|
2602
2602
|
<b>getSql() :</b> ${s}<br>
|
|
2603
2603
|
<b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
|
|
2604
|
+
<b>format() :</b> ${sql.format(s, sd)}<hr>`);
|
|
2605
|
+
// --- overlaps ---
|
|
2606
|
+
s = sql.select('*', 'test').where([['tags', lSql.EJSON.OVERLAPS, ['a', 'b']]]).getSql();
|
|
2607
|
+
sd = sql.getData();
|
|
2608
|
+
echo.push(`<pre>sql.select('*', 'test').where([['tags', lSql.EJSON.OVERLAPS, ['a', 'b']]]);</pre>
|
|
2609
|
+
<b>getSql() :</b> ${s}<br>
|
|
2610
|
+
<b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
|
|
2604
2611
|
<b>format() :</b> ${sql.format(s, sd)}`);
|
|
2605
2612
|
break;
|
|
2606
2613
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type * as openai from 'openai';
|
|
2
|
+
/**
|
|
3
|
+
* --- 获取天气预报的 Skill ---
|
|
4
|
+
*/
|
|
5
|
+
export default class {
|
|
6
|
+
/** --- Skill 的定义,供 AI 模型识别 --- */
|
|
7
|
+
static definition: openai.default.Chat.ChatCompletionTool;
|
|
8
|
+
/**
|
|
9
|
+
* --- 执行获取天气的逻辑 ---
|
|
10
|
+
* @param args AI 传过来的参数
|
|
11
|
+
*/
|
|
12
|
+
static execute(args: {
|
|
13
|
+
'city': string;
|
|
14
|
+
}): Promise<string>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* --- 获取天气预报的 Skill ---
|
|
3
|
+
*/
|
|
4
|
+
export default class default_1 {
|
|
5
|
+
/** --- Skill 的定义,供 AI 模型识别 --- */
|
|
6
|
+
static definition = {
|
|
7
|
+
'type': 'function',
|
|
8
|
+
'function': {
|
|
9
|
+
'name': 'get_weather',
|
|
10
|
+
'description': '获取指定城市的天气信息',
|
|
11
|
+
'parameters': {
|
|
12
|
+
'type': 'object',
|
|
13
|
+
'properties': {
|
|
14
|
+
'city': {
|
|
15
|
+
'type': 'string',
|
|
16
|
+
'description': '城市名称,例如“上海”、“北京”',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
'required': ['city'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* --- 执行获取天气的逻辑 ---
|
|
25
|
+
* @param args AI 传过来的参数
|
|
26
|
+
*/
|
|
27
|
+
static async execute(args) {
|
|
28
|
+
// --- 这里是模拟获取天气的逻辑,实际开发中可以调用第三方 API ---
|
|
29
|
+
const weathers = {
|
|
30
|
+
'上海': '晴朗,25℃',
|
|
31
|
+
'北京': '多云,20℃',
|
|
32
|
+
'广州': '阵雨,28℃',
|
|
33
|
+
};
|
|
34
|
+
return weathers[args.city] ?? `抱歉,暂不支持查询 ${args.city} 的天气。`;
|
|
35
|
+
}
|
|
36
|
+
}
|