@my-life-buddies/buddy-runtime 0.0.1
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/README.md +175 -0
- package/dist/conversation/agent.d.ts +24 -0
- package/dist/conversation/agent.d.ts.map +1 -0
- package/dist/conversation/agent.js +94 -0
- package/dist/conversation/agent.js.map +1 -0
- package/dist/conversation/convert.d.ts +46 -0
- package/dist/conversation/convert.d.ts.map +1 -0
- package/dist/conversation/convert.js +275 -0
- package/dist/conversation/convert.js.map +1 -0
- package/dist/conversation/history.d.ts +18 -0
- package/dist/conversation/history.d.ts.map +1 -0
- package/dist/conversation/history.js +28 -0
- package/dist/conversation/history.js.map +1 -0
- package/dist/conversation/index.d.ts +92 -0
- package/dist/conversation/index.d.ts.map +1 -0
- package/dist/conversation/index.js +322 -0
- package/dist/conversation/index.js.map +1 -0
- package/dist/conversation/presence.d.ts +24 -0
- package/dist/conversation/presence.d.ts.map +1 -0
- package/dist/conversation/presence.js +56 -0
- package/dist/conversation/presence.js.map +1 -0
- package/dist/conversation/writeBack.d.ts +36 -0
- package/dist/conversation/writeBack.d.ts.map +1 -0
- package/dist/conversation/writeBack.js +114 -0
- package/dist/conversation/writeBack.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +5 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +17 -0
- package/dist/log.js.map +1 -0
- package/dist/mlbClient.d.ts +118 -0
- package/dist/mlbClient.d.ts.map +1 -0
- package/dist/mlbClient.js +322 -0
- package/dist/mlbClient.js.map +1 -0
- package/dist/model.d.ts +24 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +45 -0
- package/dist/model.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +99 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/resource.d.ts +18 -0
- package/dist/tools/resource.d.ts.map +1 -0
- package/dist/tools/resource.js +57 -0
- package/dist/tools/resource.js.map +1 -0
- package/dist/tools/widgets.d.ts +16 -0
- package/dist/tools/widgets.d.ts.map +1 -0
- package/dist/tools/widgets.js +205 -0
- package/dist/tools/widgets.js.map +1 -0
- package/dist/types.d.ts +137 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { StreamFn } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
3
|
+
/**
|
|
4
|
+
* 按模型 id 取目录条目(带 compat,openai-completions 靠它决定 max_tokens 字段名、reasoning_content 回填、思考参数怎么发等),
|
|
5
|
+
* 只把 baseUrl 换成平台网关的 /internal/v1。返回的是新对象,目录里那份不动。
|
|
6
|
+
* 两份目录里都没有这个 id 就 throw,启动即失败,不带病跑。
|
|
7
|
+
*/
|
|
8
|
+
export declare function createModel(gatewayUrl: string, id?: string): Model<Api>;
|
|
9
|
+
/**
|
|
10
|
+
* Agent 的 streamFn:直接用 pi-ai openai-completions 的 streamSimple。
|
|
11
|
+
* pi 的 StreamFn 收 Model<Api>,而 streamSimple 只认 Model<"openai-completions">;
|
|
12
|
+
* 运行时喂进来的永远是 createModel 给的条目;网关只有 chat/completions 一个转发口,平台认领的模型在这两份目录里的条目 api 都是 openai-completions,
|
|
13
|
+
* 所以这里只做类型收窄,不做别的。
|
|
14
|
+
* apiKey 走 options.apiKey:agent-loop 每回合把 getApiKey() 的返回值放进去,openai-completions 的 getClientApiKey 优先取它,
|
|
15
|
+
* 再由 openai SDK 变成 Authorization: Bearer 头。
|
|
16
|
+
*/
|
|
17
|
+
export declare const streamFn: StreamFn;
|
|
18
|
+
/** 重建历史时给合成的 assistant 消息抄的三个字段,值就是目录条目上的 */
|
|
19
|
+
export declare function modelIdentity(model: Model<Api>): {
|
|
20
|
+
api: string;
|
|
21
|
+
provider: string;
|
|
22
|
+
model: string;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAexD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,GAAE,MAAyB,GAAG,KAAK,CAAC,GAAG,CAAC,CAMzF;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,QAC8C,CAAC;AAEtE,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAEjG"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// 模型驱动:搭子用哪个模型、走哪条 HTTP 通道、怎么把流式响应喂给 pi 的 Agent。
|
|
2
|
+
// 管的事:按开发者在 BuddyServer.start 里给的模型 id(不给就是 kimi-k3),从 pi-ai 的模型目录里取出条目并把 baseUrl 改指向平台网关;
|
|
3
|
+
// 提供 Agent 要的 streamFn;提供重建历史时给合成 assistant 消息抄的 api / provider / model 三元组。
|
|
4
|
+
// 不管的事:apiKey(由 Agent 的 getApiKey 每回合交 token,pi 的 agent-loop 会把它放进 options.apiKey)、
|
|
5
|
+
// 会话与历史(conversation/ 目录)、平台读写口(mlbClient.ts)。
|
|
6
|
+
//
|
|
7
|
+
// 通道:openai SDK 在 baseURL 后拼 /chat/completions,所以 baseUrl 只写到 /internal/v1 为止,
|
|
8
|
+
// 最终请求落到 POST <网关地址>/internal/v1/chat/completions(网关地址怎么定见 server.ts)。
|
|
9
|
+
import { streamSimple } from "@earendil-works/pi-ai/api/openai-completions";
|
|
10
|
+
import { DEEPSEEK_MODELS } from "@earendil-works/pi-ai/providers/deepseek.models";
|
|
11
|
+
import { MOONSHOTAI_CN_MODELS } from "@earendil-works/pi-ai/providers/moonshotai-cn.models";
|
|
12
|
+
/** 开发者没在 BuddyServer.start 里给 model 时用的模型 id */
|
|
13
|
+
const DEFAULT_MODEL_ID = "kimi-k3";
|
|
14
|
+
/**
|
|
15
|
+
* 按 id 找条目的目录:只放平台网关背后两家上游(Moonshot、DeepSeek)的官方目录。
|
|
16
|
+
* 同一个 id 在 opencode / github-copilot / qwen-token-plan 等目录里也有,但那些条目的 compat 是照各自中转商写的,
|
|
17
|
+
* 拿去发给官方上游,请求体里的参数会对不上。
|
|
18
|
+
*/
|
|
19
|
+
const CATALOGS = [MOONSHOTAI_CN_MODELS, DEEPSEEK_MODELS];
|
|
20
|
+
/**
|
|
21
|
+
* 按模型 id 取目录条目(带 compat,openai-completions 靠它决定 max_tokens 字段名、reasoning_content 回填、思考参数怎么发等),
|
|
22
|
+
* 只把 baseUrl 换成平台网关的 /internal/v1。返回的是新对象,目录里那份不动。
|
|
23
|
+
* 两份目录里都没有这个 id 就 throw,启动即失败,不带病跑。
|
|
24
|
+
*/
|
|
25
|
+
export function createModel(gatewayUrl, id = DEFAULT_MODEL_ID) {
|
|
26
|
+
const entry = CATALOGS.map((catalog) => catalog[id]).find((m) => m !== undefined);
|
|
27
|
+
if (entry === undefined) {
|
|
28
|
+
throw new Error(`pi-ai 的 moonshotai-cn / deepseek 模型目录里都没有 ${id};模型 id 照平台 GET /cli/models 报的填`);
|
|
29
|
+
}
|
|
30
|
+
return { ...entry, baseUrl: `${gatewayUrl.replace(/\/+$/, "")}/internal/v1` };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Agent 的 streamFn:直接用 pi-ai openai-completions 的 streamSimple。
|
|
34
|
+
* pi 的 StreamFn 收 Model<Api>,而 streamSimple 只认 Model<"openai-completions">;
|
|
35
|
+
* 运行时喂进来的永远是 createModel 给的条目;网关只有 chat/completions 一个转发口,平台认领的模型在这两份目录里的条目 api 都是 openai-completions,
|
|
36
|
+
* 所以这里只做类型收窄,不做别的。
|
|
37
|
+
* apiKey 走 options.apiKey:agent-loop 每回合把 getApiKey() 的返回值放进去,openai-completions 的 getClientApiKey 优先取它,
|
|
38
|
+
* 再由 openai SDK 变成 Authorization: Bearer 头。
|
|
39
|
+
*/
|
|
40
|
+
export const streamFn = (model, context, options) => streamSimple(model, context, options);
|
|
41
|
+
/** 重建历史时给合成的 assistant 消息抄的三个字段,值就是目录条目上的 */
|
|
42
|
+
export function modelIdentity(model) {
|
|
43
|
+
return { api: model.api, provider: model.provider, model: model.id };
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,2FAA2F;AAC3F,6EAA6E;AAC7E,oFAAoF;AACpF,+CAA+C;AAC/C,EAAE;AACF,+EAA+E;AAC/E,uEAAuE;AAIvE,OAAO,EAAE,YAAY,EAAE,MAAM,8CAA8C,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,iDAAiD,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAE5F,gDAAgD;AAChD,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAEnC;;;;GAIG;AACH,MAAM,QAAQ,GAA6C,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;AAEnG;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,KAAa,gBAAgB;IAC5E,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAClF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,6CAA6C,EAAE,gCAAgC,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,EAAE,CAAC;AAC/E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAC7D,YAAY,CAAC,KAAoC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEtE,6CAA6C;AAC7C,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC9C,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;AACtE,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAc,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiB9E,qBAAa,WAAW;IACvB,iDAAiD;WACpC,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA6ErE"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// 进程级入口:一只搭子起来后长什么样。
|
|
2
|
+
// 管:读环境变量(MLB_BUDDY_ID / MLB_BUDDY_TOKEN 必填;网关地址缺省连 ecs-test,开发者不用填,设了 MLB_GATEWAY_URL 才覆盖)、
|
|
3
|
+
// 建日志 / MLBClient / 模型条目、小挂件类型目录懒拉一次存 promise、按会话键管一张 ConversationHandle 表、
|
|
4
|
+
// 把事件流的 message / delete 分发给对应会话、每 60 秒淘汰不忙且 30 分钟没消息的会话、stop 时把一切收掉。
|
|
5
|
+
// 不管:会话里的纪律(conversation/ 目录)、协议细节(mlbClient.ts)。没有 HTTP 面、没有主动触达、没有会话列表。
|
|
6
|
+
import { ConversationHandle } from "./conversation/index.js";
|
|
7
|
+
import { createLogger } from "./log.js";
|
|
8
|
+
import { EventStream, MLBClient } from "./mlbClient.js";
|
|
9
|
+
import { createModel, streamFn } from "./model.js";
|
|
10
|
+
/** 缺省连的网关:测试环境 ecs-test。环境变量 MLB_GATEWAY_URL 有值时以它为准 */
|
|
11
|
+
const DEFAULT_GATEWAY_URL = "http://47.116.168.81:8788";
|
|
12
|
+
/** 空闲淘汰:多久扫一次、多久没消息算空闲 */
|
|
13
|
+
const SWEEP_INTERVAL_MS = 60_000;
|
|
14
|
+
const IDLE_AFTER_MS = 30 * 60_000;
|
|
15
|
+
function requireEnv(name, meaning) {
|
|
16
|
+
const value = process.env[name];
|
|
17
|
+
if (value === undefined || value === "") {
|
|
18
|
+
throw new Error(`缺环境变量 ${name}(${meaning}):平台托管时由平台注入;本地跑请在 .env 里设`);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
export class BuddyServer {
|
|
23
|
+
/** 起一只搭子:连上事件流,之后消息来一条处理一条。返回的 handle 只有 stop */
|
|
24
|
+
static async start(options) {
|
|
25
|
+
const buddyId = requireEnv("MLB_BUDDY_ID", "搭子 id");
|
|
26
|
+
const token = requireEnv("MLB_BUDDY_TOKEN", "搭子令牌");
|
|
27
|
+
const gatewayUrl = process.env.MLB_GATEWAY_URL || DEFAULT_GATEWAY_URL;
|
|
28
|
+
const log = createLogger(buddyId);
|
|
29
|
+
const client = new MLBClient({ gatewayUrl, token, log });
|
|
30
|
+
const model = createModel(gatewayUrl, options.model);
|
|
31
|
+
// 小挂件类型目录:第一场会话起来时拉一次,之后大家共用同一个 promise
|
|
32
|
+
let widgetTypes;
|
|
33
|
+
const loadWidgetTypes = () => {
|
|
34
|
+
widgetTypes ??= client.widgetTypes();
|
|
35
|
+
return widgetTypes;
|
|
36
|
+
};
|
|
37
|
+
const conversations = new Map();
|
|
38
|
+
/** 拿到这个键的会话;没有或已退役就新起一场 */
|
|
39
|
+
const conversationOf = (key) => {
|
|
40
|
+
const existing = conversations.get(key);
|
|
41
|
+
if (existing !== undefined && !existing.closed)
|
|
42
|
+
return existing;
|
|
43
|
+
const conversation = new ConversationHandle({
|
|
44
|
+
key,
|
|
45
|
+
buddyId,
|
|
46
|
+
client,
|
|
47
|
+
model,
|
|
48
|
+
streamFn,
|
|
49
|
+
token,
|
|
50
|
+
widgetTypes: loadWidgetTypes,
|
|
51
|
+
factory: options.buddy,
|
|
52
|
+
log,
|
|
53
|
+
onInvalid: () => {
|
|
54
|
+
if (conversations.get(key) === conversation)
|
|
55
|
+
conversations.delete(key);
|
|
56
|
+
void conversation.close();
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
conversations.set(key, conversation);
|
|
60
|
+
return conversation;
|
|
61
|
+
};
|
|
62
|
+
const dropConversation = (key, reason) => {
|
|
63
|
+
const conversation = conversations.get(key);
|
|
64
|
+
if (conversation === undefined)
|
|
65
|
+
return;
|
|
66
|
+
conversations.delete(key);
|
|
67
|
+
log.info("会话退役", { convoKey: key, reason });
|
|
68
|
+
void conversation.close();
|
|
69
|
+
};
|
|
70
|
+
const onEvent = (event) => {
|
|
71
|
+
if (event.type === "message")
|
|
72
|
+
conversationOf(event.convoKey).notify();
|
|
73
|
+
else
|
|
74
|
+
dropConversation(event.convoKey, "delete");
|
|
75
|
+
};
|
|
76
|
+
const stream = new EventStream({ gatewayUrl, token, log, onEvent });
|
|
77
|
+
stream.start();
|
|
78
|
+
const sweeper = setInterval(() => {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
for (const [key, conversation] of conversations) {
|
|
81
|
+
if (!conversation.busy && now - conversation.lastActivity > IDLE_AFTER_MS)
|
|
82
|
+
dropConversation(key, "idle");
|
|
83
|
+
}
|
|
84
|
+
}, SWEEP_INTERVAL_MS);
|
|
85
|
+
sweeper.unref();
|
|
86
|
+
log.info("搭子已启动", { buddyId, gatewayUrl, model: model.id });
|
|
87
|
+
return {
|
|
88
|
+
stop: async () => {
|
|
89
|
+
stream.stop();
|
|
90
|
+
clearInterval(sweeper);
|
|
91
|
+
const closing = [...conversations.values()].map((conversation) => conversation.close());
|
|
92
|
+
conversations.clear();
|
|
93
|
+
await Promise.all(closing);
|
|
94
|
+
log.info("搭子已停止", { buddyId });
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,6FAA6F;AAC7F,gFAAgF;AAChF,0EAA0E;AAC1E,0EAA0E;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGnD,wDAAwD;AACxD,MAAM,mBAAmB,GAAG,2BAA2B,CAAC;AAExD,0BAA0B;AAC1B,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACjC,MAAM,aAAa,GAAG,EAAE,GAAG,MAAM,CAAC;AAElC,SAAS,UAAU,CAAC,IAAY,EAAE,OAAe;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,OAAO,4BAA4B,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,OAAO,WAAW;IACvB,iDAAiD;IACjD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAqB;QACvC,MAAM,OAAO,GAAG,UAAU,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,UAAU,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,mBAAmB,CAAC;QACtE,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAErD,wCAAwC;QACxC,IAAI,WAA8C,CAAC;QACnD,MAAM,eAAe,GAAG,GAA0B,EAAE;YACnD,WAAW,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC;YACrC,OAAO,WAAW,CAAC;QACpB,CAAC,CAAC;QAEF,MAAM,aAAa,GAAG,IAAI,GAAG,EAA8B,CAAC;QAE5D,2BAA2B;QAC3B,MAAM,cAAc,GAAG,CAAC,GAAW,EAAsB,EAAE;YAC1D,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxC,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,MAAM;gBAAE,OAAO,QAAQ,CAAC;YAChE,MAAM,YAAY,GAAG,IAAI,kBAAkB,CAAC;gBAC3C,GAAG;gBACH,OAAO;gBACP,MAAM;gBACN,KAAK;gBACL,QAAQ;gBACR,KAAK;gBACL,WAAW,EAAE,eAAe;gBAC5B,OAAO,EAAE,OAAO,CAAC,KAAK;gBACtB,GAAG;gBACH,SAAS,EAAE,GAAG,EAAE;oBACf,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,YAAY;wBAAE,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBACvE,KAAK,YAAY,CAAC,KAAK,EAAE,CAAC;gBAC3B,CAAC;aACD,CAAC,CAAC;YACH,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YACrC,OAAO,YAAY,CAAC;QACrB,CAAC,CAAC;QAEF,MAAM,gBAAgB,GAAG,CAAC,GAAW,EAAE,MAAc,EAAQ,EAAE;YAC9D,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,YAAY,KAAK,SAAS;gBAAE,OAAO;YACvC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5C,KAAK,YAAY,CAAC,KAAK,EAAE,CAAC;QAC3B,CAAC,CAAC;QAEF,MAAM,OAAO,GAAG,CAAC,KAAiB,EAAQ,EAAE;YAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;gBAAE,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;;gBACjE,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,KAAK,EAAE,CAAC;QAEf,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,aAAa,EAAE,CAAC;gBACjD,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,GAAG,GAAG,YAAY,CAAC,YAAY,GAAG,aAAa;oBAAE,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1G,CAAC;QACF,CAAC,EAAE,iBAAiB,CAAC,CAAC;QACtB,OAAO,CAAC,KAAK,EAAE,CAAC;QAEhB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAE5D,OAAO;YACN,IAAI,EAAE,KAAK,IAAI,EAAE;gBAChB,MAAM,CAAC,IAAI,EAAE,CAAC;gBACd,aAAa,CAAC,OAAO,CAAC,CAAC;gBACvB,MAAM,OAAO,GAAG,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC;gBACxF,aAAa,CAAC,KAAK,EAAE,CAAC;gBACtB,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC3B,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YAChC,CAAC;SACD,CAAC;IACH,CAAC;CACD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
export interface Resource {
|
|
4
|
+
/** 相对资料目录的路径,如 resources/core.md */
|
|
5
|
+
file: string;
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
/** 读一个目录下的全部 .md,按路径排序。非 .md 一律忽略。 */
|
|
9
|
+
export declare function loadResources(dir: string): Resource[];
|
|
10
|
+
/** 写给模型看的资料清单,接在 systemPrompt 后面。没资料就是空串。 */
|
|
11
|
+
export declare function resourceCatalog(resources: Resource[]): string;
|
|
12
|
+
declare const READ_RESOURCE_PARAMS: Type.TObject<{
|
|
13
|
+
file: Type.TString;
|
|
14
|
+
}>;
|
|
15
|
+
/** read_resource:读清单里的一篇。没有这篇就抛——pi 会把错误如实交给模型,让它换一篇。 */
|
|
16
|
+
export declare function readResourceTool(resources: Resource[]): AgentTool<typeof READ_RESOURCE_PARAMS>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/tools/resource.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,WAAW,QAAQ;IACxB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAID,sCAAsC;AACtC,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAKrD;AAeD,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAI7D;AAED,QAAA,MAAM,oBAAoB;;EAExB,CAAC;AAEH,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,OAAO,oBAAoB,CAAC,CAa9F"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// read_resource:让模型按需读一篇 markdown 资料。
|
|
2
|
+
//
|
|
3
|
+
// 资料 = 一个目录下的 .md 文件(递归,跳过 node_modules 与点目录),路径相对目录根。
|
|
4
|
+
// 这里只管资料:把目录读成清单、把清单渲染成给模型看的一段话、装一个读工具。
|
|
5
|
+
// 人设怎么拼是开发者的事——把 resourceCatalog() 那段接在自己的 systemPrompt 后面即可。
|
|
6
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
7
|
+
import { join, relative } from "node:path";
|
|
8
|
+
import { Type } from "typebox";
|
|
9
|
+
const SKIP_DIRS = new Set(["node_modules", "venv", ".venv", "__pycache__"]);
|
|
10
|
+
/** 读一个目录下的全部 .md,按路径排序。非 .md 一律忽略。 */
|
|
11
|
+
export function loadResources(dir) {
|
|
12
|
+
const out = [];
|
|
13
|
+
walk(dir, dir, out);
|
|
14
|
+
out.sort((a, b) => a.file.localeCompare(b.file));
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
function walk(root, dir, out) {
|
|
18
|
+
for (const entry of readdirSync(dir)) {
|
|
19
|
+
if (entry.startsWith(".") || SKIP_DIRS.has(entry))
|
|
20
|
+
continue;
|
|
21
|
+
const full = join(dir, entry);
|
|
22
|
+
if (statSync(full).isDirectory()) {
|
|
23
|
+
walk(root, full, out);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (!entry.endsWith(".md"))
|
|
27
|
+
continue;
|
|
28
|
+
out.push({ file: relative(root, full), content: readFileSync(full, "utf8") });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** 写给模型看的资料清单,接在 systemPrompt 后面。没资料就是空串。 */
|
|
32
|
+
export function resourceCatalog(resources) {
|
|
33
|
+
if (resources.length === 0)
|
|
34
|
+
return "";
|
|
35
|
+
const list = resources.map((r) => `- ${r.file}`).join("\n");
|
|
36
|
+
return `===== 资料清单 =====\n详细资料按需用 read_resource 读,用到哪篇读哪篇,别一次全读:\n${list}`;
|
|
37
|
+
}
|
|
38
|
+
const READ_RESOURCE_PARAMS = Type.Object({
|
|
39
|
+
file: Type.String({ description: "资料清单里的文件路径,如 resources/core.md" }),
|
|
40
|
+
});
|
|
41
|
+
/** read_resource:读清单里的一篇。没有这篇就抛——pi 会把错误如实交给模型,让它换一篇。 */
|
|
42
|
+
export function readResourceTool(resources) {
|
|
43
|
+
const byFile = new Map(resources.map((r) => [r.file, r.content]));
|
|
44
|
+
return {
|
|
45
|
+
name: "read_resource",
|
|
46
|
+
label: "正在读资料",
|
|
47
|
+
description: "读一篇资料。用到哪篇读哪篇,别一次全读。可用的资料见系统提示的资料清单。",
|
|
48
|
+
parameters: READ_RESOURCE_PARAMS,
|
|
49
|
+
execute: async (_id, params) => {
|
|
50
|
+
const content = byFile.get(params.file);
|
|
51
|
+
if (content === undefined)
|
|
52
|
+
throw new Error(`没有这篇资料:${params.file}(可用的见系统提示的资料清单)`);
|
|
53
|
+
return { content: [{ type: "text", text: content }], details: undefined };
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/tools/resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { MLBClient, WidgetType } from "../mlbClient.ts";
|
|
3
|
+
/**
|
|
4
|
+
* 把类型目录渲染成人话,供 widget_create / widget_write 的 description 用。每个类型三层:
|
|
5
|
+
* 【<id>】<description>
|
|
6
|
+
* 模块「<name>」:<description>
|
|
7
|
+
* 每条记录填:<字段>(<类型词>[,必填][:<描述>])、…
|
|
8
|
+
* 没有 description 的行只写名字;模块没声明字段就不出第三行。
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderCatalog(types: WidgetType[]): string;
|
|
11
|
+
/**
|
|
12
|
+
* 给一场会话造五个小挂件工具。types 为空说明平台没开放任何小挂件类型,回 [],模型就见不到这些工具。
|
|
13
|
+
* 五个工具都绑死在这场会话的 convoKey 上,跨会话看不见对方的小挂件。
|
|
14
|
+
*/
|
|
15
|
+
export declare function widgetTools(client: MLBClient, convoKey: string, types: WidgetType[]): AgentTool[];
|
|
16
|
+
//# sourceMappingURL=widgets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgets.d.ts","sourceRoot":"","sources":["../../src/tools/widgets.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,+BAA+B,CAAC;AAEhF,OAAO,KAAK,EAAE,SAAS,EAAoB,UAAU,EAAe,MAAM,iBAAiB,CAAC;AAY5F;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAWzD;AAuFD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,SAAS,EAAE,CA8EjG"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// 小挂件工具:把平台的小挂件接口包成五个 pi AgentTool,交给模型在会话里建面板、写记录、读记录、删记录、把小挂件发进聊天流。
|
|
2
|
+
// 管:五个工具的名字与 label(固定,人设按名字叫它们)、参数 schema(typebox)、description 里把小挂件类型目录渲染成人话、
|
|
3
|
+
// 把平台的回复翻成给模型看的一句话(平台拒绝时也回人话,不 throw,让模型看到原因后改参数重试)。
|
|
4
|
+
// 不管:HTTP 请求本身(mlbClient.ts)、类型目录从哪拉(server.ts 懒拉一次)、什么时候把工具装进 Agent(conversation/agent.ts)。
|
|
5
|
+
import { Type } from "typebox";
|
|
6
|
+
/** JSON Schema 的 type 值 → 目录里给模型看的中文类型词 */
|
|
7
|
+
const TYPE_WORDS = {
|
|
8
|
+
string: "文字",
|
|
9
|
+
number: "数字",
|
|
10
|
+
integer: "整数",
|
|
11
|
+
boolean: "是或否",
|
|
12
|
+
array: "列表",
|
|
13
|
+
object: "对象",
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 把类型目录渲染成人话,供 widget_create / widget_write 的 description 用。每个类型三层:
|
|
17
|
+
* 【<id>】<description>
|
|
18
|
+
* 模块「<name>」:<description>
|
|
19
|
+
* 每条记录填:<字段>(<类型词>[,必填][:<描述>])、…
|
|
20
|
+
* 没有 description 的行只写名字;模块没声明字段就不出第三行。
|
|
21
|
+
*/
|
|
22
|
+
export function renderCatalog(types) {
|
|
23
|
+
const lines = [];
|
|
24
|
+
for (const type of types) {
|
|
25
|
+
lines.push(`【${type.id}】${type.description ?? ""}`);
|
|
26
|
+
for (const [name, decl] of Object.entries(type.modules ?? {})) {
|
|
27
|
+
lines.push(` 模块「${name}」${decl.description === undefined ? "" : `:${decl.description}`}`);
|
|
28
|
+
const fields = renderFields(decl);
|
|
29
|
+
if (fields !== "")
|
|
30
|
+
lines.push(` 每条记录填:${fields}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return lines.join("\n");
|
|
34
|
+
}
|
|
35
|
+
/** 一个模块的字段清单:`<字段>(<类型词>[,必填][:<描述>])` 用「、」连起来;没有字段回空串 */
|
|
36
|
+
function renderFields(decl) {
|
|
37
|
+
const properties = decl.record?.properties ?? {};
|
|
38
|
+
const required = new Set(decl.record?.required ?? []);
|
|
39
|
+
return Object.entries(properties)
|
|
40
|
+
.map(([field, prop]) => {
|
|
41
|
+
const bits = [];
|
|
42
|
+
const word = prop.type === undefined ? undefined : (TYPE_WORDS[prop.type] ?? prop.type);
|
|
43
|
+
if (word !== undefined)
|
|
44
|
+
bits.push(word);
|
|
45
|
+
if (required.has(field))
|
|
46
|
+
bits.push("必填");
|
|
47
|
+
let inner = bits.join(",");
|
|
48
|
+
if (prop.description !== undefined && prop.description !== "") {
|
|
49
|
+
inner = inner === "" ? prop.description : `${inner}:${prop.description}`;
|
|
50
|
+
}
|
|
51
|
+
return inner === "" ? field : `${field}(${inner})`;
|
|
52
|
+
})
|
|
53
|
+
.join("、");
|
|
54
|
+
}
|
|
55
|
+
/** 工具结果只有一段文字给模型看,details 不用 */
|
|
56
|
+
function text(value) {
|
|
57
|
+
return { content: [{ type: "text", text: value }], details: undefined };
|
|
58
|
+
}
|
|
59
|
+
/** 平台回的响应体里挑出一个字段(响应体不是对象就当没有) */
|
|
60
|
+
function field(body, name) {
|
|
61
|
+
return typeof body === "object" && body !== null ? body[name] : undefined;
|
|
62
|
+
}
|
|
63
|
+
/** 一个模块的记录列表渲染成几行:第一行条数,后面每条一行 `<记录 id>:<data 的 JSON>` */
|
|
64
|
+
function renderRecords(module, records) {
|
|
65
|
+
if (!Array.isArray(records))
|
|
66
|
+
return [`模块「${module}」:${JSON.stringify(records)}`];
|
|
67
|
+
if (records.length === 0)
|
|
68
|
+
return [`模块「${module}」:没有记录`];
|
|
69
|
+
const lines = [`模块「${module}」:${records.length} 条记录`];
|
|
70
|
+
for (const record of records) {
|
|
71
|
+
lines.push(` ${String(record.id ?? "?")}:${JSON.stringify(record.data)}`);
|
|
72
|
+
}
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
75
|
+
/** 整个小挂件(GET /internal/v1/widgets/:widgetId 的响应体)渲染成人话;形状不认识就原样 JSON */
|
|
76
|
+
function renderWidget(body) {
|
|
77
|
+
const modules = field(body, "modules");
|
|
78
|
+
if (typeof modules !== "object" || modules === null)
|
|
79
|
+
return JSON.stringify(body);
|
|
80
|
+
const lines = [`小挂件 ${String(field(body, "id"))}(类型 ${String(field(body, "type"))},标题「${String(field(body, "title"))}」)`];
|
|
81
|
+
for (const [name, records] of Object.entries(modules)) {
|
|
82
|
+
lines.push(...renderRecords(name, records));
|
|
83
|
+
}
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
86
|
+
/** 五个工具的参数 schema。description 是给模型看的,写清该填什么 */
|
|
87
|
+
const createParams = Type.Object({
|
|
88
|
+
type: Type.String({ description: "类型 id,从下面的目录里选" }),
|
|
89
|
+
title: Type.String({ description: "标题,给人看的" }),
|
|
90
|
+
});
|
|
91
|
+
const writeParams = Type.Object({
|
|
92
|
+
widgetId: Type.String({ description: "widget_create 回的 id" }),
|
|
93
|
+
module: Type.String({ description: "模块名,见目录" }),
|
|
94
|
+
recordId: Type.String({ description: "记录 id,自己起;已存在就覆盖" }),
|
|
95
|
+
data: Type.Object({}, { additionalProperties: true, description: "这条记录的内容,字段照该模块的说明填" }),
|
|
96
|
+
});
|
|
97
|
+
const readParams = Type.Object({
|
|
98
|
+
widgetId: Type.String({ description: "widget_create 回的 id" }),
|
|
99
|
+
module: Type.Optional(Type.String({ description: "只读这个模块;不给就读整个 widget" })),
|
|
100
|
+
});
|
|
101
|
+
const deleteParams = Type.Object({
|
|
102
|
+
widgetId: Type.String({ description: "widget_create 回的 id" }),
|
|
103
|
+
module: Type.String({ description: "模块名" }),
|
|
104
|
+
recordId: Type.String({ description: "要删的记录 id" }),
|
|
105
|
+
});
|
|
106
|
+
const sendParams = Type.Object({
|
|
107
|
+
widgetId: Type.String({ description: "要发的小挂件的 id" }),
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* 给一场会话造五个小挂件工具。types 为空说明平台没开放任何小挂件类型,回 [],模型就见不到这些工具。
|
|
111
|
+
* 五个工具都绑死在这场会话的 convoKey 上,跨会话看不见对方的小挂件。
|
|
112
|
+
*/
|
|
113
|
+
export function widgetTools(client, convoKey, types) {
|
|
114
|
+
if (types.length === 0)
|
|
115
|
+
return [];
|
|
116
|
+
const catalog = renderCatalog(types);
|
|
117
|
+
const create = {
|
|
118
|
+
name: "widget_create",
|
|
119
|
+
label: "正在建 widget",
|
|
120
|
+
description: "在这场会话里新建一个小挂件(一块给人看的结构化面板)。建好只是个空壳:还要用 widget_write 往模块里写记录,再用 widget_send 把小挂件发进聊天流,用户才看得见。\n" +
|
|
121
|
+
`你能建的类型:\n${catalog}`,
|
|
122
|
+
parameters: createParams,
|
|
123
|
+
execute: async (_id, params) => {
|
|
124
|
+
const reply = await client.widgetCreate(convoKey, params.type, params.title);
|
|
125
|
+
if (!reply.ok)
|
|
126
|
+
return text(`没建成:${reply.error}`);
|
|
127
|
+
const id = field(reply.body, "id");
|
|
128
|
+
if (typeof id !== "string")
|
|
129
|
+
return text(`没建成:平台响应里没有 id(${JSON.stringify(reply.body)})`);
|
|
130
|
+
return text(`建好了,小挂件 id 是 ${id}。接下来用 widget_write 往里写记录,用 widget_send 把小挂件发进聊天流。`);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
const write = {
|
|
134
|
+
name: "widget_write",
|
|
135
|
+
label: "正在写 widget",
|
|
136
|
+
description: "往小挂件的某个模块里写一条记录:recordId 已存在就整条覆盖,不存在就新建。data 里的字段照该模块的说明填。\n" +
|
|
137
|
+
`各类型的模块与字段:\n${catalog}`,
|
|
138
|
+
parameters: writeParams,
|
|
139
|
+
execute: async (_id, params) => {
|
|
140
|
+
const reply = await client.widgetWrite(convoKey, params.widgetId, params.module, params.recordId, params.data);
|
|
141
|
+
if (!reply.ok)
|
|
142
|
+
return text(`没写成:${reply.error}`);
|
|
143
|
+
const revision = field(reply.body, "revision");
|
|
144
|
+
return text(`写好了:${params.module}/${params.recordId}${revision === undefined ? "" : `(revision ${String(revision)})`}`);
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const read = {
|
|
148
|
+
name: "widget_read",
|
|
149
|
+
label: "正在读 widget",
|
|
150
|
+
description: "读一个小挂件里现在有什么:不给 module 就读整个小挂件(所有模块的记录),给了 module 只读那个模块。改记录之前先读一遍,别凭印象。",
|
|
151
|
+
parameters: readParams,
|
|
152
|
+
execute: async (_id, params) => {
|
|
153
|
+
const reply = await client.widgetRead(convoKey, params.widgetId, params.module);
|
|
154
|
+
if (!reply.ok)
|
|
155
|
+
return text(`没读到:${reply.error}`);
|
|
156
|
+
if (params.module === undefined)
|
|
157
|
+
return text(renderWidget(reply.body));
|
|
158
|
+
return text(renderRecords(params.module, field(reply.body, "records")).join("\n"));
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
const remove = {
|
|
162
|
+
name: "widget_delete",
|
|
163
|
+
label: "正在删小挂件记录",
|
|
164
|
+
description: "删掉小挂件某个模块里的一条记录。删的是记录,不是整个小挂件。",
|
|
165
|
+
parameters: deleteParams,
|
|
166
|
+
execute: async (_id, params) => {
|
|
167
|
+
const reply = await client.widgetDelete(convoKey, params.widgetId, params.module, params.recordId);
|
|
168
|
+
if (!reply.ok)
|
|
169
|
+
return text(`没删成:${reply.error}`);
|
|
170
|
+
return text(`删掉了:${params.module}/${params.recordId}`);
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
const send = {
|
|
174
|
+
name: "widget_send",
|
|
175
|
+
label: "正在把小挂件发进聊天流",
|
|
176
|
+
description: "把一个建好的小挂件发进聊天流,用户点开就能看。内容改了不用重发,用户点开看到的就是最新的;同一个小挂件也可以再发一次。",
|
|
177
|
+
parameters: sendParams,
|
|
178
|
+
execute: async (_id, params) => {
|
|
179
|
+
let result;
|
|
180
|
+
try {
|
|
181
|
+
result = await client.writeMessages(convoKey, [{ type: "widget", widget: { id: params.widgetId } }]);
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
return text(`没发成:${e instanceof Error ? e.message : String(e)}`);
|
|
185
|
+
}
|
|
186
|
+
if (result.kind === "rejected")
|
|
187
|
+
return text(`没发成:${rejectionText(result.status, result.body)}`);
|
|
188
|
+
return text("小挂件发进聊天流了。");
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
return [create, write, read, remove, send];
|
|
192
|
+
}
|
|
193
|
+
/** 写口 4xx 的错误体是 { error },取出来给模型看;不是 JSON 就把原文带上 */
|
|
194
|
+
function rejectionText(status, body) {
|
|
195
|
+
try {
|
|
196
|
+
const error = field(JSON.parse(body), "error");
|
|
197
|
+
if (typeof error === "string")
|
|
198
|
+
return error;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// 错误体不是 JSON,下面退回用原文
|
|
202
|
+
}
|
|
203
|
+
return `HTTP ${status}${body === "" ? "" : `:${body}`}`;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=widgets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgets.js","sourceRoot":"","sources":["../../src/tools/widgets.ts"],"names":[],"mappings":"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"}
|