@my-life-buddies/buddy-runtime 0.1.0 → 0.2.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/README.md +148 -73
- package/dist/conversation/agent.d.ts +6 -1
- package/dist/conversation/agent.d.ts.map +1 -1
- package/dist/conversation/agent.js +58 -51
- package/dist/conversation/agent.js.map +1 -1
- package/dist/conversation/agentMessageLLMMessageConverter.d.ts +2 -2
- package/dist/conversation/agentMessageLLMMessageConverter.d.ts.map +1 -1
- package/dist/conversation/agentMessageLLMMessageConverter.js +8 -32
- package/dist/conversation/agentMessageLLMMessageConverter.js.map +1 -1
- package/dist/conversation/history.js +1 -1
- package/dist/conversation/history.js.map +1 -1
- package/dist/conversation/index.d.ts +11 -1
- package/dist/conversation/index.d.ts.map +1 -1
- package/dist/conversation/index.js +60 -8
- package/dist/conversation/index.js.map +1 -1
- package/dist/conversation/mlbMessageAgentMessageConverter.d.ts +5 -2
- package/dist/conversation/mlbMessageAgentMessageConverter.d.ts.map +1 -1
- package/dist/conversation/mlbMessageAgentMessageConverter.js +77 -22
- package/dist/conversation/mlbMessageAgentMessageConverter.js.map +1 -1
- package/dist/conversation/readMLBMessages.d.ts +3 -1
- package/dist/conversation/readMLBMessages.d.ts.map +1 -1
- package/dist/conversation/readMLBMessages.js +9 -5
- package/dist/conversation/readMLBMessages.js.map +1 -1
- package/dist/conversation/writeMLBMessage.d.ts +3 -1
- package/dist/conversation/writeMLBMessage.d.ts.map +1 -1
- package/dist/conversation/writeMLBMessage.js +5 -3
- package/dist/conversation/writeMLBMessage.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mlbClient.d.ts +1 -128
- package/dist/mlbClient.d.ts.map +1 -1
- package/dist/mlbClient.js +2 -375
- package/dist/mlbClient.js.map +1 -1
- package/dist/platform/client.d.ts +29 -0
- package/dist/platform/client.d.ts.map +1 -0
- package/dist/platform/client.js +22 -0
- package/dist/platform/client.js.map +1 -0
- package/dist/platform/mlbClient.d.ts +133 -0
- package/dist/platform/mlbClient.d.ts.map +1 -0
- package/dist/platform/mlbClient.js +420 -0
- package/dist/platform/mlbClient.js.map +1 -0
- package/dist/platform/widgets.d.ts +74 -0
- package/dist/platform/widgets.d.ts.map +1 -0
- package/dist/platform/widgets.js +85 -0
- package/dist/platform/widgets.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/dataAccess.d.ts +2 -2
- package/dist/tools/dataAccess.d.ts.map +1 -1
- package/dist/tools/dataAccess.js +4 -4
- package/dist/tools/dataAccess.js.map +1 -1
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +23 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/widgets.d.ts +3 -2
- package/dist/tools/widgets.d.ts.map +1 -1
- package/dist/tools/widgets.js +22 -53
- package/dist/tools/widgets.js.map +1 -1
- package/dist/types.d.ts +37 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -3
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
- package/dist/conversation/convert.d.ts +0 -46
- package/dist/conversation/convert.d.ts.map +0 -1
- package/dist/conversation/convert.js +0 -297
- package/dist/conversation/convert.js.map +0 -1
- package/dist/conversation/presence.d.ts +0 -24
- package/dist/conversation/presence.d.ts.map +0 -1
- package/dist/conversation/presence.js +0 -56
- package/dist/conversation/presence.js.map +0 -1
- package/dist/conversation/writeBack.d.ts +0 -36
- package/dist/conversation/writeBack.d.ts.map +0 -1
- package/dist/conversation/writeBack.js +0 -114
- package/dist/conversation/writeBack.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ MLB 搭子运行时。基于 [`@earendil-works/pi-agent-core`](https://www.npmjs
|
|
|
5
5
|
## 1. 安装
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @my-life-buddies/buddy-runtime
|
|
8
|
+
npm install @my-life-buddies/buddy-runtime typebox
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
要求 Node.js ≥ 22.19。
|
|
@@ -67,6 +67,7 @@ await BuddyServer.start({
|
|
|
67
67
|
| 参数 | 类型 | 说明 |
|
|
68
68
|
|---|---|---|
|
|
69
69
|
| `options.model` | `string`,必填 | 模型 id,取值见平台 `GET /cli/models`。进程内所有会话共用。没填,或 id 在运行时内置的模型目录中不存在时,启动失败 |
|
|
70
|
+
| `options.welcome` / `options.errorReply` | `string`,可选 | 应用提供的静态问候,以及模型出错、空回复时发的兜底回复 |
|
|
70
71
|
| `options.buddy` | `(conversation: Conversation) => BuddyOptions \| Promise<BuddyOptions>` | 搭子配置工厂。每场会话在第一条消息到达时调用一次 |
|
|
71
72
|
|
|
72
73
|
### 3.2 `BuddyOptions`
|
|
@@ -76,40 +77,72 @@ await BuddyServer.start({
|
|
|
76
77
|
```ts
|
|
77
78
|
type BuddyOptions =
|
|
78
79
|
Pick<AgentOptions,
|
|
79
|
-
| "
|
|
80
|
+
| "beforeToolCall" | "afterToolCall"
|
|
80
81
|
| "shouldStopAfterTurn" | "prepareNextTurnWithContext"
|
|
81
82
|
| "thinkingBudgets" | "toolExecution">
|
|
82
|
-
& { initialState: Pick<AgentState, "systemPrompt"> & Partial<Pick<AgentState, "tools" | "thinkingLevel">> };
|
|
83
|
+
& { platformTools?: readonly PlatformToolName[]; initialState: Pick<AgentState, "systemPrompt"> & Partial<Pick<AgentState, "tools" | "thinkingLevel">> };
|
|
83
84
|
```
|
|
84
85
|
|
|
85
86
|
| 字段 | 说明 |
|
|
86
87
|
|---|---|
|
|
87
|
-
| `
|
|
88
|
-
| `initialState.
|
|
88
|
+
| `platformTools` | 要注册给模型的平台工具名单,默认 `[]`,见 3.3 |
|
|
89
|
+
| `initialState.systemPrompt` | 系统提示。静态内容;每回合变化的信息不应写在这里。`platformTools` 里有 `read_resource` 且有 `resources/` 时,运行时在末尾追加资料清单 |
|
|
90
|
+
| `initialState.tools` | 工具列表,类型为 pi 的 `AgentTool`。`label` 为必填,用作用户可见的状态行文案(空字符串表示不显示);`execute` 返回值的 `details` 为必填(可为 `undefined`);执行失败应直接抛出异常。工具名不得与平台工具或其他工具重复。先单独声明为 `AgentTool<typeof 参数 schema>` 再放入数组,`execute` 的参数才有类型;直接写在数组里时参数类型为 `unknown` |
|
|
89
91
|
| `initialState.thinkingLevel` | 思考级别,默认 `off`。模型不支持的级别在发请求前就近换成支持的一档。显式设了模型不支持的级别时,每场会话建立时打一条 warn |
|
|
90
|
-
| `transformContext` | 历史裁剪。入参是这场会话完整的 Agent 历史,平台交代的场合也在其中,消息从哪来、长什么样见下方「4. 会话历史」一节;返回值只用于这一次模型请求,不改动历史;抛出异常时使用原历史 |
|
|
91
92
|
| `beforeToolCall` / `afterToolCall` / `shouldStopAfterTurn` / `prepareNextTurnWithContext` / `thinkingBudgets` / `toolExecution` | 原样传递给 pi |
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
密钥、会话历史与插话策略由运行时统一管理;工厂提供当前会话键与已绑定的平台能力,不传递 Token。模型通过 `BuddyServer.start` 的 `model` 选择。
|
|
94
95
|
|
|
95
|
-
### 3.3 `Conversation
|
|
96
|
+
### 3.3 `Conversation` 与平台能力
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
工厂拿到的对象,可在工具闭包中使用。会话之间的数据应以 `key` 隔离。
|
|
98
99
|
|
|
99
100
|
```ts
|
|
100
101
|
interface Conversation {
|
|
101
|
-
readonly key: string;
|
|
102
|
+
readonly key: string; // 会话键:私聊为 p_ 前缀,群聊为 g_ 前缀
|
|
102
103
|
readonly log: BuddyLogger;
|
|
104
|
+
readonly signal: AbortSignal; // 会话删除、空闲淘汰、stop 时中止
|
|
105
|
+
readonly platform: PlatformClient; // 已绑好这场会话的平台能力
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface PlatformClient {
|
|
103
109
|
readonly memory: {
|
|
104
|
-
list(): Promise<MemoryEntry[]>;
|
|
105
|
-
write(key: string, text: string): Promise<void>;
|
|
106
|
-
delete(key: string): Promise<void>;
|
|
107
|
-
};
|
|
108
|
-
readonly dataAccess: {
|
|
109
|
-
query(request: { datasets: string[]; memberId?: string }): Promise<DatasetAccessResult[]>; // 读用户授权的数据,见 3.4.2
|
|
110
|
+
list(): Promise<MemoryEntry[]>;
|
|
111
|
+
write(key: string, text: string): Promise<void>;
|
|
112
|
+
delete(key: string): Promise<void>;
|
|
110
113
|
};
|
|
114
|
+
readonly dataAccess: { query(request: { datasets: string[]; memberId?: string }): Promise<DatasetAccessResult[]> };
|
|
115
|
+
readonly widgets: WidgetClient;
|
|
116
|
+
readonly messages: { send(message: MLBMessageToWrite & { idempotencyKey: string }): void };
|
|
111
117
|
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`platform` 下的方法由开发者在代码里调用,会话键已经绑好,不需要传 Token 或拼 HTTP 路径。其中授权数据、小挂件、资料三项,运行时还提供了给模型用的工具,把工具名写进 `BuddyOptions.platformTools` 才注册:
|
|
112
121
|
|
|
122
|
+
| 能力 | 代码里调用 | 给模型的工具 | 工具注册还要满足 |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| 记忆 | `platform.memory` | 无,要给模型用就自己包成工具,见 3.3.1 | 无 |
|
|
125
|
+
| 授权数据 | `platform.dataAccess.query` | `data_access_query` | 声明过至少一个 Dataset |
|
|
126
|
+
| 小挂件 | `platform.widgets` | `widget_create` / `widget_write` / `widget_read` / `widget_delete` / `widget_send` | 登记过至少一个小挂件类型 |
|
|
127
|
+
| 资料 | 无 | `read_resource` | `resources/` 下有 `.md` |
|
|
128
|
+
| 发消息 | `platform.messages.send` | 无 | 无 |
|
|
129
|
+
|
|
130
|
+
- `platformTools` 默认 `[]`,一个平台工具都不注册。名单里有不认识或重复的名字时,这场会话起不来。
|
|
131
|
+
- 注册的平台工具排在 `initialState.tools` 前面;和开发者的工具重名时,这场会话起不来。
|
|
132
|
+
- 没写进名单的,照样可以在代码里调用 `platform`。
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
return {
|
|
136
|
+
platformTools: ["data_access_query", "widget_read"],
|
|
137
|
+
initialState: { systemPrompt: PERSONA, tools: [remember] },
|
|
138
|
+
};
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### 3.3.1 记忆
|
|
142
|
+
|
|
143
|
+
`platform.memory` 是这场会话的长期记忆,由平台保存,只在本会话内可见,会话删除时平台一并清除。`key` 为主题名(非空、不含空白与 `/`,最长 64 字),`text` 单条不超过 16KB;每场会话最多 64 条、合计不超过 256KB。`list` 全量列出,按 `key` 字典序;`write` 覆盖写一条;`delete` 删一条,删不存在的也算成功。超出配额、`key` 不合法或网络出错时直接抛出异常,异常信息为平台给出的原因;在工具的 `execute` 中调用时,模型能看到这个原因。
|
|
144
|
+
|
|
145
|
+
```ts
|
|
113
146
|
interface MemoryEntry {
|
|
114
147
|
key: string; // 主题名
|
|
115
148
|
text: string;
|
|
@@ -117,9 +150,7 @@ interface MemoryEntry {
|
|
|
117
150
|
}
|
|
118
151
|
```
|
|
119
152
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
`memory` 是这场会话的长期记忆,由平台保存,只在本会话内可见,会话删除时平台一并清除;会话键已经绑好,调用时无需传入。`key` 为主题名(非空、不含空白与 `/`,最长 64 字),`text` 单条不超过 16KB;每场会话最多 64 条、合计不超过 256KB。超出配额、`key` 不合法或网络出错时直接抛出异常,异常信息为平台给出的原因;在工具的 `execute` 中调用时,模型能看到这个原因。
|
|
153
|
+
运行时不提供记忆工具,要让模型记东西,开发者自己包成工具:
|
|
123
154
|
|
|
124
155
|
```ts
|
|
125
156
|
const REMEMBER_PARAMS = Type.Object({ topic: Type.String(), text: Type.String() });
|
|
@@ -133,7 +164,7 @@ await BuddyServer.start({
|
|
|
133
164
|
description: "把一条关于对方的印象记下来,同一主题覆盖旧的。",
|
|
134
165
|
parameters: REMEMBER_PARAMS,
|
|
135
166
|
execute: async (_id, { topic, text }) => {
|
|
136
|
-
await conversation.memory.write(topic, text);
|
|
167
|
+
await conversation.platform.memory.write(topic, text);
|
|
137
168
|
return { content: [{ type: "text", text: "记下了。" }], details: undefined };
|
|
138
169
|
},
|
|
139
170
|
};
|
|
@@ -142,42 +173,11 @@ await BuddyServer.start({
|
|
|
142
173
|
});
|
|
143
174
|
```
|
|
144
175
|
|
|
145
|
-
完整示例见 [examples/aunt-tu/](examples/aunt-tu/):`taste_read` / `taste_write` 两个工具用 `memory` 记每个人爱吃什么、不爱吃什么。
|
|
146
|
-
|
|
147
|
-
`dataAccess.query` 读取用户授权给搭子的数据,用法见 3.4.2。
|
|
148
|
-
|
|
149
|
-
### 3.4 内置工具:运行时注册给模型
|
|
176
|
+
完整示例见 [examples/aunt-tu/](examples/aunt-tu/):`taste_read` / `taste_write` 两个工具用 `platform.memory` 记每个人爱吃什么、不爱吃什么。
|
|
150
177
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
| 工具 | 何时注册 | 做什么 |
|
|
154
|
-
|---|---|---|
|
|
155
|
-
| `read_resource` | 工程目录下有 `resources/`,且其中有 `.md` | 按资料清单里的相对路径读一篇资料 |
|
|
156
|
-
| `data_access_query` | 搭子声明过至少一个 Dataset | 按需读取当前会话用户授权的数据;单次最多 8 个 Dataset,响应上限 512 KiB |
|
|
157
|
-
| `widget_create` / `widget_write` / `widget_read` / `widget_delete` / `widget_send` | 搭子在平台上登记了小挂件类型 | 建小挂件、写 / 读 / 删记录、把小挂件发进聊天 |
|
|
158
|
-
|
|
159
|
-
#### 3.4.1 资料
|
|
160
|
-
|
|
161
|
-
资料放在工程目录下的 `resources/` 里,是一篇篇 `.md` 文件。运行时自动让模型按需读取,开发者不需要写代码。
|
|
162
|
-
|
|
163
|
-
- 读哪些文件:进程启动时读一次 `resources/` 下全部 `.md`,包括子目录;跳过以 `.` 开头的文件与目录,以及 `node_modules`、`venv`、`.venv`、`__pycache__`;其他扩展名的文件忽略。资料改动后需重启进程。
|
|
164
|
-
- 模型看到什么:系统提示末尾追加一份资料清单,列出每篇资料相对 `resources/` 的路径;模型调用 `read_resource` 并传入清单中的路径,读到该篇全文。
|
|
165
|
-
- 没有资料时:`resources/` 不存在或其中没有 `.md`,不注册 `read_resource`,也不追加清单。
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
my-buddy/ 工程目录
|
|
169
|
-
├─ package.json
|
|
170
|
-
├─ resources/ 资料目录
|
|
171
|
-
│ └─ research/
|
|
172
|
-
│ └─ 01-writings.md 清单中写作 research/01-writings.md
|
|
173
|
-
└─ src/main.ts 入口脚本
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
完整示例见 [examples/stacy-sims/](examples/stacy-sims/)。
|
|
178
|
+
#### 3.3.2 授权数据
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
经用户同意,搭子可以读取用户授权数据。数据按 Dataset 分项,每项内容与字段固定,用户逐项授权。开发者要做的是声明需要哪些 Dataset;查询工具由运行时注册给模型,开发者的工具里也能直接查。
|
|
180
|
+
经用户同意,搭子可以读取用户授权数据。数据按 Dataset 分项,每项内容与字段固定,用户逐项授权。开发者要做的是声明需要哪些 Dataset,然后让模型用 `data_access_query` 查,或者在自己的工具里查。会话与真实用户身份由平台绑定,搭子拿不到真实用户 id。
|
|
181
181
|
|
|
182
182
|
**声明需要哪些数据**
|
|
183
183
|
|
|
@@ -213,20 +213,20 @@ my-buddy/ 工程目录
|
|
|
213
213
|
|
|
214
214
|
**让模型去查**
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
`platformTools` 里有 `data_access_query`,且声明过至少一项数据,模型就有了这个工具,工具说明里列着开发者声明的数据与用途。开发者可以在系统提示里写明什么时候查,例如「聊到作息时,先用 data_access_query 查 health.sleep」。
|
|
217
217
|
|
|
218
218
|
**在自己的工具里查**
|
|
219
219
|
|
|
220
|
-
调用 `conversation.dataAccess.query`:
|
|
220
|
+
调用 `conversation.platform.dataAccess.query`,不需要写进 `platformTools`:
|
|
221
221
|
|
|
222
222
|
```ts
|
|
223
|
-
const [sleep] = await conversation.dataAccess.query({ datasets: ["health.sleep"] });
|
|
223
|
+
const [sleep] = await conversation.platform.dataAccess.query({ datasets: ["health.sleep"] });
|
|
224
224
|
if (sleep.status === "available") {
|
|
225
225
|
// sleep.data.items 是每天一条的睡眠记录
|
|
226
226
|
}
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
- `datasets`:一次最多 8
|
|
229
|
+
- `datasets`:一次最多 8 个;响应超过 512 KiB 时抛出异常,减少 Dataset 后重试。
|
|
230
230
|
- `memberId`:私聊不传;群聊必须传,即群消息前缀 `[名字 #m_…]` 里的 `m_…`,查的是这位成员的数据。传错时直接抛出异常。
|
|
231
231
|
|
|
232
232
|
**查询结果**
|
|
@@ -243,33 +243,109 @@ if (sleep.status === "available") {
|
|
|
243
243
|
|
|
244
244
|
**数据只在这一轮回复里可见**
|
|
245
245
|
|
|
246
|
-
一轮回复里只要查过授权数据,这一轮结束后,聊天记录和会话历史里这一轮的工具参数、工具结果与 thinking
|
|
246
|
+
一轮回复里只要查过授权数据,这一轮结束后,聊天记录和会话历史里这一轮的工具参数、工具结果与 thinking,都会换成「查了哪些数据、各是什么状态」,给用户看的回复照常保留。开发者自己写进记忆或小挂件的内容不会被替换,不要把查到的数据写进去。
|
|
247
|
+
|
|
248
|
+
#### 3.3.3 小挂件
|
|
247
249
|
|
|
248
|
-
|
|
250
|
+
**登记类型**
|
|
249
251
|
|
|
250
252
|
开发者通过平台 CLI 接口给搭子登记小挂件类型:`PUT /cli/buddies/:id/widget-types/:typeId/schema` 交 schema,每个模块写一句 `description` 给模型读,每条记录的形状是一份 JSON Schema;`PUT /cli/buddies/:id/widget-types/:typeId/page` 交展示页面,单个 HTML 文件。
|
|
251
253
|
|
|
252
|
-
|
|
254
|
+
**让模型去用**
|
|
255
|
+
|
|
256
|
+
`platformTools` 里写了哪几个 `widget_*`,运行时就注册哪几个:进程里第一次要用时拉一次 `GET /internal/v1/widget-types`,之后共用;工具说明里附上各类型的模块与字段;一个类型都没登记时一个也不注册。这场会话里已有哪些小挂件,由平台消息流里的 `context` 记录告诉模型。
|
|
257
|
+
|
|
258
|
+
- 平台请求失败时工具抛出异常,模型看到的是「平台请求失败(HTTP 409),请查询最新状态后再操作」这样的提示,不带平台返回的具体原因。
|
|
259
|
+
- `widget_create` 用 `widget-create:<toolCallId>` 作幂等键,同一次工具调用重发不会建出两个。
|
|
260
|
+
- `widget_send` 把小挂件放进这场会话的发送队列就返回,不等平台确认送达。
|
|
261
|
+
|
|
262
|
+
**在自己的工具里用**
|
|
263
|
+
|
|
264
|
+
调用 `conversation.platform.widgets`,不需要写进 `platformTools`:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
execute: async (toolCallId, params, signal) => {
|
|
268
|
+
const widgets = conversation.platform.widgets.withSignal(signal);
|
|
269
|
+
const { id } = await widgets.create({ type: "itinerary", title: "杭州行程", idempotencyKey: `itinerary:${toolCallId}` });
|
|
270
|
+
await widgets.write(id, { module: "days", recordId: "day-1", data: { city: "杭州" } });
|
|
271
|
+
widgets.send(id, { idempotencyKey: `itinerary-send:${toolCallId}` });
|
|
272
|
+
return { content: [{ type: "text", text: "行程建好了。" }], details: undefined };
|
|
273
|
+
},
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
| 方法 | 做什么 | 平台接口 |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| `list()` | 列出这场会话里的小挂件,不带记录 | `GET /internal/v1/widgets` |
|
|
279
|
+
| `create({ type, title, idempotencyKey? })` | 新建一个,回 `{ id }`;同一个 `idempotencyKey` 重复建回同一个 id | `POST /internal/v1/widgets` |
|
|
280
|
+
| `read(id)` | 读整个小挂件,含各模块的记录 | `GET /internal/v1/widgets/:widgetId` |
|
|
281
|
+
| `readModule(id, module)` | 读一个模块,回 `{ records }` | `GET /internal/v1/widgets/:widgetId/:module` |
|
|
282
|
+
| `rename(id, title)` | 改标题,id 不变 | `PUT /internal/v1/widgets/:widgetId` |
|
|
283
|
+
| `write(id, { module, recordId, data, expectedRevision? })` | 写一条记录,已存在就整条覆盖;带 `expectedRevision` 时版本号对不上,平台回 409 | `PUT /internal/v1/widgets/:widgetId/:module/:recordId` |
|
|
284
|
+
| `deleteRecord(id, module, recordId)` | 删一条记录 | `DELETE /internal/v1/widgets/:widgetId/:module/:recordId` |
|
|
285
|
+
| `setPresentation(id, { summary, imageMediaId })` | 设置聊天流里小挂件卡片的摘要(不超过 500 字)和封面图,`imageMediaId` 传空串去掉封面 | `PUT /internal/v1/widgets/:widgetId/presentation` |
|
|
286
|
+
| `send(id, { idempotencyKey })` | 放进发送队列,按顺序发一条 `type: "widget"` 的消息 | `POST /internal/v1/messages` |
|
|
287
|
+
| `withSignal(signal)` | 回一个同时受这个 signal 与会话 `signal` 控制的 Client | 无 |
|
|
288
|
+
|
|
289
|
+
- 每个方法最后都可以传 `{ signal }`。
|
|
290
|
+
- `data` 要符合登记的 schema,不符合时平台回 400。
|
|
291
|
+
- 平台回非 2xx、网络出错、超过 30 秒或响应超过 4 MiB 时抛出 `PlatformRequestError`,异常只带 HTTP 状态码。写操作不自动重试,失败后先读一遍真实状态再决定。
|
|
253
292
|
|
|
254
293
|
用法见 [examples/aunt-tu/](examples/aunt-tu/)。
|
|
255
294
|
|
|
295
|
+
#### 3.3.4 资料
|
|
296
|
+
|
|
297
|
+
资料放在工程目录(`package.json` 所在目录)下的 `resources/`。工程目录由入口脚本向上定位,与启动进程的工作目录无关。
|
|
298
|
+
|
|
299
|
+
- 启动时递归读取 `.md`,跳过 `node_modules`、`venv`、`.venv`、`__pycache__` 及点开头的目录/文件;资料变更后需重启进程。
|
|
300
|
+
- `platformTools` 里有 `read_resource` 时,系统提示末尾追加资料清单;模型通过清单中的相对路径读取内容。
|
|
301
|
+
- 没有资料或名单里没有 `read_resource` 时,不注册 `read_resource`,也不追加资料清单。
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
my-buddy/ 工程目录
|
|
305
|
+
├─ package.json
|
|
306
|
+
├─ resources/ 资料目录
|
|
307
|
+
│ └─ research/
|
|
308
|
+
│ └─ 01-writings.md 清单中写作 research/01-writings.md
|
|
309
|
+
└─ src/main.ts 入口脚本
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
完整示例见 [examples/stacy-sims/](examples/stacy-sims/)。
|
|
313
|
+
|
|
314
|
+
#### 3.3.5 发消息与图片
|
|
315
|
+
|
|
316
|
+
**发消息**
|
|
317
|
+
|
|
318
|
+
`platform.messages.send(message)` 把一条消息放进这场会话的发送队列,和模型的回复排在同一条队列里按顺序发(`POST /internal/v1/messages`),调用后立即返回,不等平台确认。
|
|
319
|
+
|
|
320
|
+
- `message.type` 取 `message` / `tool` / `note` / `widget`;`idempotencyKey` 必填。
|
|
321
|
+
- 会话已经删除或淘汰时直接抛出异常。
|
|
322
|
+
|
|
323
|
+
**图片**
|
|
324
|
+
|
|
325
|
+
用户发的图不需要开发者处理。平台在拉取消息时把图片内容一起给运行时,运行时把它放进会话历史,之后每次请求模型原样带上:
|
|
326
|
+
|
|
327
|
+
- 图片在用户上传时由平台统一处理成 JPEG,最长边不超过 2048 px、不超过 5 MiB。
|
|
328
|
+
- 图片消息交给模型时是一条 user 消息:正文前标着「[图片 #消息 id]」,用户配了文字接在后面,再跟图片本身。
|
|
329
|
+
- 引用了别的消息的,开头加一行:引用文字写「[引用 发言人:原话]」,引用图片写「[引用 发言人的图片 #被引消息 id]」,模型按 id 对上是哪一张。
|
|
330
|
+
- 转发进聊天记录卡的图,跟在卡里那条正文后面。
|
|
331
|
+
- 图片已经读不到时换成「[图片无法读取]」;`model` 选的模型不支持看图时换成「[图片:当前模型不支持看图]」。
|
|
332
|
+
|
|
256
333
|
## 4. 会话历史:MLBMessage → AgentMessage → LLMMessage
|
|
257
334
|
|
|
258
335
|
开发者不需要保存会话历史,也不需要把聊天里的消息换成模型消息,这些由运行时完成。每场会话的消息都保存在平台上,搭子进程重启后历史照样在。从平台上的 `MLBMessage` 到模型收到的 `LLMMessage`:
|
|
259
336
|
|
|
260
337
|
```
|
|
261
|
-
MLBMessage[] → AgentMessage[] →
|
|
262
|
-
▲ (1) │ ▲
|
|
263
|
-
└─────(
|
|
338
|
+
MLBMessage[] → AgentMessage[] → convertToLlm() → LLMMessage[] → LLM
|
|
339
|
+
▲ (1) │ ▲ (2) │
|
|
340
|
+
└─────(4)──────┘ └──────────────────(3)─────────────────────┘
|
|
264
341
|
```
|
|
265
342
|
|
|
266
|
-
1. 平台上的 `MLBMessage` 换成 pi 的 `AgentMessage
|
|
267
|
-
2. `
|
|
268
|
-
3.
|
|
269
|
-
4.
|
|
270
|
-
5. 经 (4) 追加进历史的消息,运行时再换成 `MLBMessage` 写回平台;经 (1) 从平台来的不会写回。用户在聊天里看到的回复就是这一步写上去的。用过授权数据的那一轮,写回前先脱敏,见 3.4.2。搭子重启后,这些消息经 (1) 取回。
|
|
343
|
+
1. 平台上的 `MLBMessage` 换成 pi 的 `AgentMessage`,追加进会话历史。一条消息只换一次:会话起来时换一遍平台上已有的全部消息,之后来一条换一条。图片、引用和聊天记录卡怎么换,见 3.3.5。
|
|
344
|
+
2. `convertToLlm()`:运行时实现,开发者不能替换。每次请求模型都拿完整的会话历史来换:运行时补的提醒(note)换成 user,其余原样,按原来的先后一条对一条,相邻的 user 不合并。换出来的 `LLMMessage`(即 pi-ai 的 `Message`)连同 `systemPrompt` 与 `tools` 发给模型。
|
|
345
|
+
3. 模型的回复,以及它调用工具得到的结果,直接追加进会话历史,不经过 (1)。
|
|
346
|
+
4. 经 (3) 追加进历史的消息,运行时再换成 `MLBMessage` 写回平台;经 (1) 从平台来的不会写回。用户在聊天里看到的回复就是这一步写上去的。用过授权数据的那一轮,写回前先脱敏,见 3.3.2。搭子重启后,这些消息经 (1) 取回。
|
|
271
347
|
|
|
272
|
-
(1) 在消息到来时发生;(2)
|
|
348
|
+
(1) 在消息到来时发生;(2) 在每次请求模型前都完整跑一遍,一轮回复里模型可能请求好几次,比如调完工具接着说,每次都从当时完整的会话历史重新算;(3)、(4) 在模型每说完一段、每跑完一步工具时发生。
|
|
273
349
|
|
|
274
350
|
## 5. 环境变量
|
|
275
351
|
|
|
@@ -278,12 +354,11 @@ MLBMessage[] → AgentMessage[] → transformContext() → AgentMessage[] → co
|
|
|
278
354
|
| `MLB_BUDDY_ID` | 搭子 id,注册时由平台分配 |
|
|
279
355
|
| `MLB_BUDDY_TOKEN` | 搭子令牌,注册时由平台分配 |
|
|
280
356
|
|
|
281
|
-
|
|
357
|
+
`MLB_GATEWAY_URL` 可覆盖平台网关。当前缺省为 ecs-test(`http://47.116.168.81:8788`);本地测试必须显式配置回环地址及虚拟凭据,不能用正式搭子 Token 启动第二个进程。
|
|
282
358
|
|
|
283
359
|
搭子的档案(名称、头像、简介)由平台保存:注册时通过 `POST /cli/buddies` 提交,修改通过 `PUT /cli/buddies/:id/meta`。搭子进程不监听端口、不接收入站请求。从非回环地址连接网关时,平台将其识别为 `<id>-dev` 分身,会话与正式身份隔离。
|
|
284
360
|
|
|
285
361
|
## 6. 开发
|
|
286
362
|
|
|
287
|
-
- `
|
|
363
|
+
- 改完代码需要自动重启时,自行在 `package.json` 里用 `node --watch` 启动。
|
|
288
364
|
- 会话消息拉取遇到临时网络错误时会主动指数退避重试(1 秒起步、最高 30 秒);新的 SSE 通知会取消等待并立即重拉。
|
|
289
|
-
- 运行时仓库:`npm test`、`npm run typecheck`、`npm run build`。
|
|
@@ -4,8 +4,11 @@ import type { Api, Model } from "@earendil-works/pi-ai";
|
|
|
4
4
|
import type { MLBClient, WidgetType } from "../mlbClient.ts";
|
|
5
5
|
import { DataAccessUsageTracker } from "../tools/dataAccess.ts";
|
|
6
6
|
import type { Resource } from "../tools/resource.ts";
|
|
7
|
-
import type { BuddyFactory, BuddyLogger, DataRequirement } from "../types.ts";
|
|
7
|
+
import type { BuddyFactory, BuddyLogger, DataRequirement, MLBMessageToWrite } from "../types.ts";
|
|
8
8
|
export interface AgentDeps {
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
enqueueMessage?: (message: MLBMessageToWrite) => void;
|
|
11
|
+
onPolicyStop?: () => void;
|
|
9
12
|
key: string;
|
|
10
13
|
buddyId: string;
|
|
11
14
|
client: MLBClient;
|
|
@@ -28,4 +31,6 @@ export interface CreatedAgent {
|
|
|
28
31
|
dataAccessUsage: DataAccessUsageTracker;
|
|
29
32
|
}
|
|
30
33
|
export declare function createAgent(deps: AgentDeps): Promise<CreatedAgent>;
|
|
34
|
+
/** 打日志用的副本:图片块的 data、请求体里 base64 的 data URL 换成字节数,其余原样 */
|
|
35
|
+
export declare function withoutImageData(value: unknown): unknown;
|
|
31
36
|
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAa,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAErF,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAkB,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAa,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAErF,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAkB,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAIrD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAA8B,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE7H,MAAM,WAAW,SAAS;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACtD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACzC,sDAAsD;IACtD,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACnD,OAAO,EAAE,YAAY,CAAC;IACtB,GAAG,EAAE,WAAW,CAAC;IACjB,oCAAoC;IACpC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CACnD;AAED,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,EAAE,sBAAsB,CAAC;CACxC;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CA+FxE;AAED,0DAA0D;AAC1D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAUxD"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// 一场会话的 Agent 怎么建:运行时先把该填的填好,开发者的配置叠上去。
|
|
2
|
-
// 管:拼好交给开发者工厂的 Conversation
|
|
3
|
-
//
|
|
2
|
+
// 管:拼好交给开发者工厂的 Conversation(会话键、日志、绑好会话键的平台能力),跑工厂拿配置;
|
|
3
|
+
// 显式选中的平台工具排在开发者工具前面并查重名;选择 read_resource 时才追加资料清单;
|
|
4
4
|
// 开发者显式设的 thinkingLevel 这个模型不支持时打一条 warn,写明支持哪几档、实际会怎么发;
|
|
5
|
-
// 钩子叠加——开发者的
|
|
6
|
-
//
|
|
5
|
+
// 钩子叠加——开发者的 beforeToolCall 包 try/catch 抛了按放行;其余直通;不装 transformContext,整段历史交给 convertToLlm;
|
|
6
|
+
// 每次请求模型,convertToLlm 出参和请求体(onPayload)全量打日志,用户说的话和工具结果都原样打出来,图片内容换成字节数。
|
|
7
7
|
// 不管:Agent 建好之后的事(回合、事件分发在 index.ts)。
|
|
8
8
|
import { Agent } from "@earendil-works/pi-agent-core";
|
|
9
9
|
import { clampThinkingLevel, getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
@@ -11,36 +11,36 @@ import { agentMessagesToLLMMessages } from "./agentMessageLLMMessageConverter.js
|
|
|
11
11
|
import { errorText } from "../log.js";
|
|
12
12
|
import { dataAccessTool, DataAccessUsageTracker } from "../tools/dataAccess.js";
|
|
13
13
|
import { readResourceTool, resourceCatalog } from "../tools/resource.js";
|
|
14
|
+
import { createPlatformClient } from "../platform/client.js";
|
|
15
|
+
import { validatePlatformTools, registerTools } from "../tools/registry.js";
|
|
14
16
|
import { widgetTools } from "../tools/widgets.js";
|
|
15
17
|
export async function createAgent(deps) {
|
|
16
18
|
const dataAccessUsage = new DataAccessUsageTracker();
|
|
19
|
+
const lifetimeSignal = deps.signal ?? new AbortController().signal;
|
|
20
|
+
const platform = createPlatformClient({
|
|
21
|
+
client: deps.client, key: deps.key, signal: lifetimeSignal,
|
|
22
|
+
enqueueMessage: message => { if (!deps.enqueueMessage)
|
|
23
|
+
throw new Error("消息队列不可用"); deps.enqueueMessage(message); },
|
|
24
|
+
queryDataAccess: ({ datasets, memberId }) => dataAccessUsage.query(deps.client, deps.key, datasets, memberId, lifetimeSignal),
|
|
25
|
+
});
|
|
17
26
|
const conversation = {
|
|
18
|
-
|
|
19
|
-
log: deps.log,
|
|
20
|
-
memory:
|
|
21
|
-
list: () => deps.client.memoryList(deps.key),
|
|
22
|
-
write: (key, text) => deps.client.memoryWrite(deps.key, key, text),
|
|
23
|
-
delete: (key) => deps.client.memoryDelete(deps.key, key),
|
|
24
|
-
},
|
|
25
|
-
dataAccess: {
|
|
26
|
-
query: ({ datasets, memberId }) => dataAccessUsage.query(deps.client, deps.key, datasets, memberId),
|
|
27
|
-
},
|
|
27
|
+
signal: lifetimeSignal, platform,
|
|
28
|
+
key: deps.key, log: deps.log,
|
|
29
|
+
memory: platform.memory, dataAccess: platform.dataAccess,
|
|
28
30
|
};
|
|
29
31
|
const options = await deps.factory(conversation);
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
seen.add(tool.name);
|
|
43
|
-
}
|
|
32
|
+
const selected = validatePlatformTools(options.platformTools);
|
|
33
|
+
const [types, requirements] = await Promise.all([
|
|
34
|
+
[...selected].some(name => name.startsWith("widget_")) ? deps.widgetTypes() : [],
|
|
35
|
+
selected.has("data_access_query") ? deps.dataRequirements() : [],
|
|
36
|
+
]);
|
|
37
|
+
const resources = selected.has("read_resource") ? deps.resources : [];
|
|
38
|
+
const available = [
|
|
39
|
+
...(requirements.length ? [dataAccessTool(deps.client, deps.key, dataAccessUsage, requirements, lifetimeSignal)] : []),
|
|
40
|
+
...(types.length ? widgetTools(platform.widgets, types) : []),
|
|
41
|
+
...(resources.length ? [readResourceTool(resources)] : []),
|
|
42
|
+
];
|
|
43
|
+
const tools = registerTools(selected, available, options.initialState.tools ?? []).map(tool => dataAccessUsage.track(tool));
|
|
44
44
|
const { log, key } = deps;
|
|
45
45
|
const thinkingLevel = options.initialState.thinkingLevel;
|
|
46
46
|
if (thinkingLevel !== undefined) {
|
|
@@ -55,8 +55,7 @@ export async function createAgent(deps) {
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
const catalog = resourceCatalog(
|
|
59
|
-
const devTransform = options.transformContext;
|
|
58
|
+
const catalog = resourceCatalog(resources);
|
|
60
59
|
const devBefore = options.beforeToolCall;
|
|
61
60
|
const devAfter = options.afterToolCall;
|
|
62
61
|
const agent = new Agent({
|
|
@@ -72,31 +71,16 @@ export async function createAgent(deps) {
|
|
|
72
71
|
},
|
|
73
72
|
steeringMode: "all",
|
|
74
73
|
followUpMode: "all",
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
transformContext: async (messages, signal) => {
|
|
78
|
-
log.info("transformContext 入参", { convoKey: key, messageCount: messages.length });
|
|
79
|
-
if (devTransform === undefined)
|
|
80
|
-
return messages;
|
|
81
|
-
let base = messages;
|
|
82
|
-
try {
|
|
83
|
-
base = await devTransform(messages, signal);
|
|
84
|
-
}
|
|
85
|
-
catch (e) {
|
|
86
|
-
log.warn("开发者的 transformContext 抛错,用原历史", { convoKey: key, error: errorText(e) });
|
|
87
|
-
base = messages;
|
|
88
|
-
}
|
|
89
|
-
log.info("开发者 transformContext 出参", { convoKey: key, messageCount: base.length });
|
|
90
|
-
return base;
|
|
91
|
-
},
|
|
74
|
+
// 下面两个钩子按 pi 每次请求模型时的调用顺序排:convertToLlm → onPayload,出参和请求体全量打日志。
|
|
75
|
+
// 不装 transformContext:场合交代和图片都已是历史里的记录,每次请求把整段历史交给 convertToLlm
|
|
92
76
|
convertToLlm: (messages) => {
|
|
93
77
|
const llmMessages = agentMessagesToLLMMessages(messages);
|
|
94
|
-
log.info("convertToLlm 出参", { convoKey: key,
|
|
78
|
+
log.info("convertToLlm 出参", { convoKey: key, messages: withoutImageData(llmMessages) });
|
|
95
79
|
return llmMessages;
|
|
96
80
|
},
|
|
97
81
|
// 只打日志、返回 undefined:返回别的值 pi-ai 会拿它顶替请求体发出去
|
|
98
|
-
onPayload: () => {
|
|
99
|
-
log.info("
|
|
82
|
+
onPayload: (payload) => {
|
|
83
|
+
log.info("模型请求体", { convoKey: key, payload: withoutImageData(payload) });
|
|
100
84
|
return undefined;
|
|
101
85
|
},
|
|
102
86
|
beforeToolCall: devBefore === undefined
|
|
@@ -113,7 +97,12 @@ export async function createAgent(deps) {
|
|
|
113
97
|
afterToolCall: devAfter === undefined
|
|
114
98
|
? undefined
|
|
115
99
|
: (context, signal) => dataAccessUsage.runForTool(context.toolCall.id, () => devAfter(context, signal)),
|
|
116
|
-
shouldStopAfterTurn: options.shouldStopAfterTurn,
|
|
100
|
+
shouldStopAfterTurn: options.shouldStopAfterTurn ? async (context, signal) => {
|
|
101
|
+
const stop = await options.shouldStopAfterTurn(context, signal);
|
|
102
|
+
if (stop)
|
|
103
|
+
deps.onPolicyStop?.();
|
|
104
|
+
return stop;
|
|
105
|
+
} : undefined,
|
|
117
106
|
prepareNextTurnWithContext: options.prepareNextTurnWithContext,
|
|
118
107
|
thinkingBudgets: options.thinkingBudgets,
|
|
119
108
|
toolExecution: options.toolExecution,
|
|
@@ -121,4 +110,22 @@ export async function createAgent(deps) {
|
|
|
121
110
|
agent.subscribe((event) => deps.onEvent(agent, event));
|
|
122
111
|
return { agent, dataAccessUsage };
|
|
123
112
|
}
|
|
113
|
+
/** 打日志用的副本:图片块的 data、请求体里 base64 的 data URL 换成字节数,其余原样 */
|
|
114
|
+
export function withoutImageData(value) {
|
|
115
|
+
if (typeof value === "string") {
|
|
116
|
+
const marker = value.startsWith("data:") ? value.indexOf(";base64,") : -1;
|
|
117
|
+
return marker === -1 ? value : `${value.slice(0, marker)};base64,[${base64Bytes(value.slice(marker + 8))} 字节]`;
|
|
118
|
+
}
|
|
119
|
+
if (Array.isArray(value))
|
|
120
|
+
return value.map(withoutImageData);
|
|
121
|
+
if (typeof value !== "object" || value === null)
|
|
122
|
+
return value;
|
|
123
|
+
const record = value;
|
|
124
|
+
if (record.type === "image" && typeof record.data === "string")
|
|
125
|
+
return { ...record, data: `[${base64Bytes(record.data)} 字节]` };
|
|
126
|
+
return Object.fromEntries(Object.entries(record).map(([name, field]) => [name, withoutImageData(field)]));
|
|
127
|
+
}
|
|
128
|
+
function base64Bytes(data) {
|
|
129
|
+
return Math.floor((data.length * 3) / 4) - (data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0);
|
|
130
|
+
}
|
|
124
131
|
//# sourceMappingURL=agent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { Message } from "@earendil-works/pi-ai";
|
|
3
3
|
/**
|
|
4
|
-
* note 变 user
|
|
5
|
-
*
|
|
4
|
+
* note 变 user;user / assistant / toolResult 原样。一条对一条、按原来的先后,相邻的 user 不合并。
|
|
5
|
+
* 返回的是新数组,不改传进来的消息。
|
|
6
6
|
*/
|
|
7
7
|
export declare const agentMessagesToLLMMessages: (messages: AgentMessage[]) => Message[];
|
|
8
8
|
//# sourceMappingURL=agentMessageLLMMessageConverter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentMessageLLMMessageConverter.d.ts","sourceRoot":"","sources":["../../src/conversation/agentMessageLLMMessageConverter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"agentMessageLLMMessageConverter.d.ts","sourceRoot":"","sources":["../../src/conversation/agentMessageLLMMessageConverter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,YAAY,EAAE,KAAG,OAAO,EAI1E,CAAC"}
|