@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/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# @my-life-buddies/buddy-runtime
|
|
2
|
+
|
|
3
|
+
MLB 搭子运行时。基于 [`@earendil-works/pi-agent-core`](https://www.npmjs.com/package/@earendil-works/pi-agent-core) 构建,负责搭子与 MLB 平台之间的全部交互:订阅事件流、拉取消息、驱动模型、写回回复、推送在线状态。开发者只需提供搭子的配置。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @my-life-buddies/buddy-runtime typebox
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
要求 Node.js ≥ 22.19。
|
|
12
|
+
|
|
13
|
+
## 快速开始
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
my-buddy/
|
|
17
|
+
├─ package.json
|
|
18
|
+
├─ .env MLB_BUDDY_ID / MLB_BUDDY_TOKEN
|
|
19
|
+
└─ src/main.ts
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
24
|
+
import { BuddyServer } from "@my-life-buddies/buddy-runtime";
|
|
25
|
+
import { Type } from "typebox";
|
|
26
|
+
|
|
27
|
+
const PLAN_MENU_PARAMS = Type.Object({ date: Type.String() });
|
|
28
|
+
|
|
29
|
+
const planMenu: AgentTool<typeof PLAN_MENU_PARAMS, undefined> = {
|
|
30
|
+
name: "plan_menu",
|
|
31
|
+
label: "正在排菜单",
|
|
32
|
+
description: "给某一天排三餐。",
|
|
33
|
+
parameters: PLAN_MENU_PARAMS,
|
|
34
|
+
execute: async (_id, { date }) => ({
|
|
35
|
+
content: [{ type: "text", text: `${date} 的三餐排好了。` }],
|
|
36
|
+
details: undefined,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
await BuddyServer.start({
|
|
41
|
+
model: "kimi-k3", // 可选,不填为 kimi-k3;可选值见开发者接口 GET /cli/models
|
|
42
|
+
buddy: () => ({
|
|
43
|
+
initialState: {
|
|
44
|
+
systemPrompt: "你是「小涂阿姨」——一位管做饭的阿姨……",
|
|
45
|
+
tools: [planMenu],
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"scripts": {
|
|
54
|
+
"start": "node --env-file-if-exists=.env src/main.ts"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
完整示例见 [examples/aunt-tu/](examples/aunt-tu/)。
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
### `BuddyServer.start(options)`
|
|
64
|
+
|
|
65
|
+
启动搭子进程,连接平台事件流。返回 `{ stop(): Promise<void> }`。
|
|
66
|
+
|
|
67
|
+
| 参数 | 类型 | 说明 |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `options.model` | `string`,可选 | 模型 id,取值见平台 `GET /cli/models`;不填为 `kimi-k3`。进程内所有会话共用。id 在运行时内置的模型目录中不存在时启动失败 |
|
|
70
|
+
| `options.buddy` | `(conversation: Conversation) => BuddyOptions \| Promise<BuddyOptions>` | 搭子配置工厂。每场会话在第一条消息到达时调用一次 |
|
|
71
|
+
|
|
72
|
+
### `BuddyOptions`
|
|
73
|
+
|
|
74
|
+
配置的形状与字段名与 pi-agent-core 的 `AgentOptions` / `AgentState` 一致,签名请参考 pi 的文档。
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
type BuddyOptions =
|
|
78
|
+
Pick<AgentOptions,
|
|
79
|
+
| "transformContext" | "beforeToolCall" | "afterToolCall"
|
|
80
|
+
| "shouldStopAfterTurn" | "prepareNextTurnWithContext"
|
|
81
|
+
| "thinkingBudgets" | "toolExecution">
|
|
82
|
+
& { initialState: Pick<AgentState, "systemPrompt"> & Partial<Pick<AgentState, "tools" | "thinkingLevel">> };
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| 字段 | 说明 |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `initialState.systemPrompt` | 系统提示。静态内容;每回合变化的信息不应写在这里 |
|
|
88
|
+
| `initialState.tools` | 工具列表,类型为 pi 的 `AgentTool`。`label` 为必填,用作用户可见的状态行文案(空字符串表示不显示);`execute` 返回值的 `details` 为必填(可为 `undefined`);执行失败应直接抛出异常。工具名不得与官方工具或其他工具重复。先单独声明为 `AgentTool<typeof 参数 schema>` 再放入数组,`execute` 的参数才有类型;直接写在数组里时参数类型为 `unknown` |
|
|
89
|
+
| `initialState.thinkingLevel` | 思考级别,默认 `off`。`off` 时 kimi-k3 不发送思考参数,由模型默认行为决定;kimi-k2.6、deepseek-v4-flash、deepseek-v4-pro 发送关闭思考的参数 |
|
|
90
|
+
| `transformContext` | 历史裁剪。开发者的函数先执行,运行时随后插入平台下发的会话上下文 |
|
|
91
|
+
| `beforeToolCall` / `afterToolCall` / `shouldStopAfterTurn` / `prepareNextTurnWithContext` / `thinkingBudgets` / `toolExecution` | 原样传递给 pi |
|
|
92
|
+
|
|
93
|
+
密钥、会话历史、会话标识与插话策略由运行时统一管理,不对开发者开放。模型通过 `BuddyServer.start` 的 `model` 选择。
|
|
94
|
+
|
|
95
|
+
### `Conversation`
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
interface Conversation {
|
|
99
|
+
readonly key: string; // 会话键:私聊为 p_ 前缀,群聊为 g_ 前缀
|
|
100
|
+
readonly log: BuddyLogger;
|
|
101
|
+
readonly memory: {
|
|
102
|
+
list(): Promise<MemoryEntry[]>; // 全量列出,按 key 字典序
|
|
103
|
+
write(key: string, text: string): Promise<void>; // 覆盖写一条
|
|
104
|
+
delete(key: string): Promise<void>; // 删一条,删不存在的也算成功
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface MemoryEntry {
|
|
109
|
+
key: string; // 主题名
|
|
110
|
+
text: string;
|
|
111
|
+
updatedAt: number; // 最后写入时刻,毫秒
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
传入配置工厂,可在工具闭包中使用。会话之间的数据应以 `key` 隔离。
|
|
116
|
+
|
|
117
|
+
`memory` 是这场会话的长期记忆,由平台保存,只在本会话内可见,会话删除时平台一并清除;会话键已经绑好,调用时无需传入。`key` 为主题名(非空、不含空白与 `/`,最长 64 字),`text` 单条不超过 16KB;每场会话最多 64 条、合计不超过 256KB。超出配额、`key` 不合法或网络出错时直接抛出异常,异常信息为平台给出的原因;在工具的 `execute` 中调用时,模型能看到这个原因。
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const REMEMBER_PARAMS = Type.Object({ topic: Type.String(), text: Type.String() });
|
|
121
|
+
|
|
122
|
+
await BuddyServer.start({
|
|
123
|
+
buddy: (conversation) => {
|
|
124
|
+
const remember: AgentTool<typeof REMEMBER_PARAMS, undefined> = {
|
|
125
|
+
name: "remember",
|
|
126
|
+
label: "正在记下",
|
|
127
|
+
description: "把一条关于对方的印象记下来,同一主题覆盖旧的。",
|
|
128
|
+
parameters: REMEMBER_PARAMS,
|
|
129
|
+
execute: async (_id, { topic, text }) => {
|
|
130
|
+
await conversation.memory.write(topic, text);
|
|
131
|
+
return { content: [{ type: "text", text: "记下了。" }], details: undefined };
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
return { initialState: { systemPrompt: PERSONA, tools: [remember] } };
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
完整示例见 [examples/aunt-tu/](examples/aunt-tu/):`taste_read` / `taste_write` 两个工具用 `memory` 记每个人爱吃什么、不爱吃什么。
|
|
140
|
+
|
|
141
|
+
### Buddy 内置 Tools
|
|
142
|
+
|
|
143
|
+
**小挂件**:运行时根据搭子在平台上登记的小挂件类型自动装配,无需配置:`widget_create`、`widget_write`、`widget_read`、`widget_delete`、`widget_send`(把小挂件发进聊天流)。系统提示中可按名称引用。
|
|
144
|
+
|
|
145
|
+
**资料读取**:`loadResources` / `resourceCatalog` / `readResourceTool` 三个函数,用于让模型按需读取 markdown 资料。
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { BuddyServer, loadResources, readResourceTool, resourceCatalog } from "@my-life-buddies/buddy-runtime";
|
|
149
|
+
|
|
150
|
+
const resources = loadResources("resources"); // 递归收集目录下全部 .md 文件
|
|
151
|
+
await BuddyServer.start({
|
|
152
|
+
buddy: () => ({
|
|
153
|
+
initialState: {
|
|
154
|
+
systemPrompt: `${PERSONA}\n\n${resourceCatalog(resources)}`, // 资料清单附在系统提示之后
|
|
155
|
+
tools: [readResourceTool(resources)], // 装配 read_resource 工具
|
|
156
|
+
},
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 环境变量
|
|
162
|
+
|
|
163
|
+
| 变量 | 说明 |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `MLB_BUDDY_ID` | 搭子 id,注册时由平台分配 |
|
|
166
|
+
| `MLB_BUDDY_TOKEN` | 搭子令牌,注册时由平台分配 |
|
|
167
|
+
|
|
168
|
+
网关地址无需配置,搭子连接测试平台 ecs-test(`http://47.116.168.81:8788`)。
|
|
169
|
+
|
|
170
|
+
搭子的档案(名称、头像、简介)由平台保存:注册时通过 `POST /cli/buddies` 提交,修改通过 `PUT /cli/buddies/:id/meta`。搭子进程不监听端口、不接收入站请求。从非回环地址连接网关时,平台将其识别为 `<id>-dev` 分身,会话与正式身份隔离。
|
|
171
|
+
|
|
172
|
+
## 开发
|
|
173
|
+
|
|
174
|
+
- `npm run dev`:以 `node --watch` 启动,文件变更后自动重启。停止时需终止整个进程树(`pkill -f "node --watch .* src/main.ts"`),仅终止子进程会被父进程重新拉起。
|
|
175
|
+
- 运行时仓库:`npm test`、`npm run typecheck`、`npm run build`。
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { AgentEvent, StreamFn } from "@earendil-works/pi-agent-core";
|
|
3
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
4
|
+
import type { MLBClient, WidgetType } from "../mlbClient.ts";
|
|
5
|
+
import type { BuddyFactory, BuddyLogger } from "../types.ts";
|
|
6
|
+
export interface AgentDeps {
|
|
7
|
+
key: string;
|
|
8
|
+
buddyId: string;
|
|
9
|
+
client: MLBClient;
|
|
10
|
+
model: Model<Api>;
|
|
11
|
+
streamFn: StreamFn;
|
|
12
|
+
/** 搭子令牌:经 Agent 的 getApiKey 交给模型通道,变成 Authorization: Bearer 头 */
|
|
13
|
+
token: string;
|
|
14
|
+
widgetTypes: () => Promise<WidgetType[]>;
|
|
15
|
+
factory: BuddyFactory;
|
|
16
|
+
log: BuddyLogger;
|
|
17
|
+
/** 每次模型调用前现取平台 context 段;空就不插 */
|
|
18
|
+
contextOf: () => string | undefined;
|
|
19
|
+
/** Agent 事件的去处。pi 会等它返回,所以只能同步做事 */
|
|
20
|
+
onEvent: (agent: Agent, event: AgentEvent) => void;
|
|
21
|
+
}
|
|
22
|
+
/** 跑开发者工厂拿配置,叠上运行时预置,建 Agent。工具名跟小挂件工具重名或互相重名 → throw。 */
|
|
23
|
+
export declare function createAgent(deps: AgentDeps): Promise<Agent>;
|
|
24
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAa,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAA8B,MAAM,aAAa,CAAC;AAEzF,MAAM,WAAW,SAAS;IACzB,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,OAAO,EAAE,YAAY,CAAC;IACtB,GAAG,EAAE,WAAW,CAAC;IACjB,iCAAiC;IACjC,SAAS,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACpC,oCAAoC;IACpC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CACnD;AAED,0DAA0D;AAC1D,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAgFjE"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// 一场会话的 Agent 怎么建:运行时先把该填的填好,开发者的配置叠上去。
|
|
2
|
+
// 管:拼好交给开发者工厂的 Conversation(会话键、日志、绑好会话键的 memory 三个方法),跑工厂拿配置;小挂件工具排在开发者工具前面并查重名;
|
|
3
|
+
// 钩子叠加——开发者的 transformContext 先跑、运行时最后插平台 context 段;开发者的 beforeToolCall 包 try/catch 抛了按放行;其余直通;
|
|
4
|
+
// 每次请求模型,transformContext 入参 / 出参、convertToLlm 出参、请求体(onPayload)全量打日志。
|
|
5
|
+
// 不管:Agent 建好之后的事(回合、事件分发在 index.ts)。
|
|
6
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
7
|
+
import { convertToLlm, injectContext } from "./convert.js";
|
|
8
|
+
import { errorText } from "../log.js";
|
|
9
|
+
import { widgetTools } from "../tools/widgets.js";
|
|
10
|
+
/** 跑开发者工厂拿配置,叠上运行时预置,建 Agent。工具名跟小挂件工具重名或互相重名 → throw。 */
|
|
11
|
+
export async function createAgent(deps) {
|
|
12
|
+
const conversation = {
|
|
13
|
+
key: deps.key,
|
|
14
|
+
log: deps.log,
|
|
15
|
+
memory: {
|
|
16
|
+
list: () => deps.client.memoryList(deps.key),
|
|
17
|
+
write: (key, text) => deps.client.memoryWrite(deps.key, key, text),
|
|
18
|
+
delete: (key) => deps.client.memoryDelete(deps.key, key),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const options = await deps.factory(conversation);
|
|
22
|
+
const types = await deps.widgetTypes();
|
|
23
|
+
const tools = [...widgetTools(deps.client, deps.key, types), ...(options.initialState.tools ?? [])];
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
for (const tool of tools) {
|
|
26
|
+
if (seen.has(tool.name))
|
|
27
|
+
throw new Error(`工具重名:${tool.name}`);
|
|
28
|
+
seen.add(tool.name);
|
|
29
|
+
}
|
|
30
|
+
const { log, key } = deps;
|
|
31
|
+
const devTransform = options.transformContext;
|
|
32
|
+
const devBefore = options.beforeToolCall;
|
|
33
|
+
const agent = new Agent({
|
|
34
|
+
streamFn: deps.streamFn,
|
|
35
|
+
getApiKey: () => deps.token,
|
|
36
|
+
sessionId: `buddy:${deps.buddyId}:${key}`,
|
|
37
|
+
initialState: {
|
|
38
|
+
model: deps.model,
|
|
39
|
+
systemPrompt: options.initialState.systemPrompt,
|
|
40
|
+
tools,
|
|
41
|
+
thinkingLevel: options.initialState.thinkingLevel ?? "off",
|
|
42
|
+
},
|
|
43
|
+
steeringMode: "all",
|
|
44
|
+
followUpMode: "all",
|
|
45
|
+
// 下面三个钩子按 pi 每次请求模型时的调用顺序排:transformContext → convertToLlm → onPayload,每一步的全量都打一行日志
|
|
46
|
+
// 返回的是新数组(injectContext 不动传进来的);context 为空就原样交回去,不插
|
|
47
|
+
transformContext: async (messages, signal) => {
|
|
48
|
+
log.info("transformContext 入参", { convoKey: key, messages });
|
|
49
|
+
let base = messages;
|
|
50
|
+
if (devTransform !== undefined) {
|
|
51
|
+
try {
|
|
52
|
+
base = await devTransform(messages, signal);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
log.warn("开发者的 transformContext 抛错,用原历史", { convoKey: key, error: errorText(e) });
|
|
56
|
+
base = messages;
|
|
57
|
+
}
|
|
58
|
+
log.info("开发者 transformContext 出参", { convoKey: key, messages: base });
|
|
59
|
+
}
|
|
60
|
+
const injected = injectContext(base, deps.contextOf());
|
|
61
|
+
log.info("injectContext 出参", { convoKey: key, messages: injected });
|
|
62
|
+
return injected;
|
|
63
|
+
},
|
|
64
|
+
convertToLlm: (messages) => {
|
|
65
|
+
const llmMessages = convertToLlm(messages);
|
|
66
|
+
log.info("convertToLlm 出参", { convoKey: key, messages: llmMessages });
|
|
67
|
+
return llmMessages;
|
|
68
|
+
},
|
|
69
|
+
// 只打日志、返回 undefined:返回别的值 pi-ai 会拿它顶替请求体发出去
|
|
70
|
+
onPayload: (payload) => {
|
|
71
|
+
log.info("模型请求体", { convoKey: key, payload });
|
|
72
|
+
return undefined;
|
|
73
|
+
},
|
|
74
|
+
beforeToolCall: devBefore === undefined
|
|
75
|
+
? undefined
|
|
76
|
+
: async (context, signal) => {
|
|
77
|
+
try {
|
|
78
|
+
return await devBefore(context, signal);
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
log.warn("开发者的 beforeToolCall 抛错,按放行", { convoKey: key, tool: context.toolCall.name, error: errorText(e) });
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
afterToolCall: options.afterToolCall,
|
|
86
|
+
shouldStopAfterTurn: options.shouldStopAfterTurn,
|
|
87
|
+
prepareNextTurnWithContext: options.prepareNextTurnWithContext,
|
|
88
|
+
thinkingBudgets: options.thinkingBudgets,
|
|
89
|
+
toolExecution: options.toolExecution,
|
|
90
|
+
});
|
|
91
|
+
agent.subscribe((event) => deps.onEvent(agent, event));
|
|
92
|
+
return agent;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/conversation/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { AssistantMessage, Message, ToolResultMessage } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { NoteMessage, OutgoingMessage, PlatformMessage } from "../types.ts";
|
|
4
|
+
/** 重建 assistant 消息时抄的模型身份(model.ts 的 modelIdentity 给) */
|
|
5
|
+
export interface ModelIdentity {
|
|
6
|
+
api: string;
|
|
7
|
+
provider: string;
|
|
8
|
+
model: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 把一批平台记录换成 pi 的历史。跳过的:interrupted 的半截话、question / widget、空正文又没工具调用的搭子记录。
|
|
12
|
+
* 对方的 message 变 user(群里带成员前缀)、system 变 user(带「群里发生了一件事」)、messagesCard 拍平成 user;
|
|
13
|
+
* 搭子的 message / checkin 变 assistant,tool 变 assistant + 每个调用一条 toolResult,note 变 note。
|
|
14
|
+
*/
|
|
15
|
+
export declare function toAgentMessages(records: PlatformMessage[], identity: ModelIdentity): AgentMessage[];
|
|
16
|
+
/**
|
|
17
|
+
* 不带工具调用的 assistant 消息写成 message 记录。
|
|
18
|
+
* 正常收尾的照写(带 thinking);error / aborted 收尾但已经说了半截的写成 interrupted;其余不写。
|
|
19
|
+
*/
|
|
20
|
+
export declare function outboundFromAssistant(m: AssistantMessage): OutgoingMessage | null;
|
|
21
|
+
/**
|
|
22
|
+
* 带工具调用的 assistant 消息连同它的结果写成一条 tool 记录;每个调用的 summary 取工具的 label,没有 label 用名字。
|
|
23
|
+
* 没有工具调用的返回 null(那种走 outboundFromAssistant)。
|
|
24
|
+
*/
|
|
25
|
+
export declare function outboundFromTurn(m: AssistantMessage, toolResults: ToolResultMessage[], tools: {
|
|
26
|
+
name: string;
|
|
27
|
+
label: string;
|
|
28
|
+
}[]): OutgoingMessage | null;
|
|
29
|
+
/** note 消息写成 note 记录 */
|
|
30
|
+
export declare function outboundFromNote(n: NoteMessage): OutgoingMessage;
|
|
31
|
+
/** assistant 消息里所有 thinking 块的推理原文拼起来;一个字都没有返回 undefined */
|
|
32
|
+
export declare function thinkingOf(m: AssistantMessage): string | undefined;
|
|
33
|
+
/** assistant 消息里所有 text 块的正文拼起来 */
|
|
34
|
+
export declare function textOf(m: AssistantMessage): string;
|
|
35
|
+
/**
|
|
36
|
+
* 交给 pi 的 convertToLlm:note 变 user;相邻两条 user 合并成一条(正文中间隔一个换行);user / assistant / toolResult 原样。
|
|
37
|
+
* 返回的是新数组,合并出来的也是新对象,不改传进来的消息。
|
|
38
|
+
*/
|
|
39
|
+
export declare const convertToLlm: (messages: AgentMessage[]) => Message[];
|
|
40
|
+
/**
|
|
41
|
+
* 把平台随拉取下发的 context 段插进历史:找到末尾那段连续的 user(note 也算,它到模型那边就是 user),插在这段前面,
|
|
42
|
+
* 后面的 convertToLlm 会把它跟这段合并;末尾是 assistant / toolResult 时,往前找最后一段 user 插在它前面。
|
|
43
|
+
* context 为空原样返回;否则返回新数组,不改传进来的。
|
|
44
|
+
*/
|
|
45
|
+
export declare function injectContext(messages: AgentMessage[], context: string | undefined): AgentMessage[];
|
|
46
|
+
//# sourceMappingURL=convert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/conversation/convert.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EACX,gBAAgB,EAEhB,OAAO,EAIP,iBAAiB,EAGjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAgB,WAAW,EAAE,eAAe,EAAE,eAAe,EAAoB,MAAM,aAAa,CAAC;AAEjH,yDAAyD;AACzD,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AA6BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,QAAQ,EAAE,aAAa,GAAG,YAAY,EAAE,CAgCnG;AAgFD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,gBAAgB,GAAG,eAAe,GAAG,IAAI,CAQjF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,CAAC,EAAE,gBAAgB,EACnB,WAAW,EAAE,iBAAiB,EAAE,EAChC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GACtC,eAAe,GAAG,IAAI,CAkBxB;AAED,wBAAwB;AACxB,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,GAAG,eAAe,CAEhE;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAMlE;AAED,mCAAmC;AACnC,wBAAgB,MAAM,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAKlD;AAYD;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,UAAU,YAAY,EAAE,KAAG,OAAO,EAa9D,CAAC;AA8BF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,EAAE,CAQnG"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
// 平台记录与 pi 消息之间的换算。只管形状换算,不碰网络、不碰 Agent、不管游标和幂等键。
|
|
2
|
+
// 入站:把 GET /internal/v1/messages 拉回来的记录换成 pi 的 AgentMessage(重建历史、灌对方的话都用它)。
|
|
3
|
+
// 出站:把 pi 跑出来的 assistant / toolResult / note 换成 POST /internal/v1/messages 的每条 body。
|
|
4
|
+
// 另外两件给 Agent 用的事也在这:convertToLlm(note 变 user、相邻 user 合并)和 injectContext(把平台 context 段插进历史末尾)。
|
|
5
|
+
/** 工具结果没回来(回合被中断)时,写给模型和平台的占位文本 */
|
|
6
|
+
const MISSING_TOOL_RESULT = "(工具结果未返回:回合被中断)";
|
|
7
|
+
/** system 记录换成 user 消息时加在正文前的说明 */
|
|
8
|
+
const SYSTEM_EVENT_PREFIX = "(群里发生了一件事)";
|
|
9
|
+
/** 转发卡最多展开几层,再深的一层写成一句省略 */
|
|
10
|
+
const CARD_MAX_DEPTH = 3;
|
|
11
|
+
const CARD_TOO_DEEP = "(更深的转发略)";
|
|
12
|
+
/** 重建出来的 thinking 块用的签名:pi 的 openai-completions 回放时按它把推理原文写回 reasoning_content 字段 */
|
|
13
|
+
const THINKING_SIGNATURE = "reasoning_content";
|
|
14
|
+
/** 重建的 assistant 消息没有真实用量,全零;形状照 pi-agent-core 的 EMPTY_USAGE */
|
|
15
|
+
function emptyUsage() {
|
|
16
|
+
return {
|
|
17
|
+
input: 0,
|
|
18
|
+
output: 0,
|
|
19
|
+
cacheRead: 0,
|
|
20
|
+
cacheWrite: 0,
|
|
21
|
+
totalTokens: 0,
|
|
22
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// —— 入站:平台记录 → pi AgentMessage
|
|
26
|
+
/**
|
|
27
|
+
* 把一批平台记录换成 pi 的历史。跳过的:interrupted 的半截话、question / widget、空正文又没工具调用的搭子记录。
|
|
28
|
+
* 对方的 message 变 user(群里带成员前缀)、system 变 user(带「群里发生了一件事」)、messagesCard 拍平成 user;
|
|
29
|
+
* 搭子的 message / checkin 变 assistant,tool 变 assistant + 每个调用一条 toolResult,note 变 note。
|
|
30
|
+
*/
|
|
31
|
+
export function toAgentMessages(records, identity) {
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const record of records) {
|
|
34
|
+
if (record.interrupted)
|
|
35
|
+
continue;
|
|
36
|
+
const text = record.text ?? "";
|
|
37
|
+
switch (record.type) {
|
|
38
|
+
case "system":
|
|
39
|
+
out.push(userMessage(SYSTEM_EVENT_PREFIX + text, record.timestamp));
|
|
40
|
+
break;
|
|
41
|
+
case "messagesCard":
|
|
42
|
+
if (record.messagesCard)
|
|
43
|
+
out.push(userMessage(flattenCard(record.messagesCard, 1).join("\n"), record.timestamp));
|
|
44
|
+
break;
|
|
45
|
+
case "message":
|
|
46
|
+
case "checkin":
|
|
47
|
+
if (record.author === "buddy") {
|
|
48
|
+
if (text)
|
|
49
|
+
out.push(assistantMessage(record, [...thinkingBlocks(record.thinking), { type: "text", text }], "stop", identity));
|
|
50
|
+
}
|
|
51
|
+
else if (record.type === "message" && text) {
|
|
52
|
+
out.push(userMessage(memberPrefix(record) + text, record.timestamp));
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case "tool":
|
|
56
|
+
if (record.author === "buddy")
|
|
57
|
+
out.push(...toolTurn(record, text, identity));
|
|
58
|
+
break;
|
|
59
|
+
case "note":
|
|
60
|
+
if (record.author === "buddy" && text)
|
|
61
|
+
out.push({ role: "note", text, timestamp: record.timestamp });
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
// question / widget 和别的类型:模型不需要
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
/** 搭子的 tool 记录:一条带 toolCall 块的 assistant,后面每个调用跟一条 toolResult */
|
|
71
|
+
function toolTurn(record, text, identity) {
|
|
72
|
+
const calls = record.toolCalls ?? [];
|
|
73
|
+
if (!text && calls.length === 0)
|
|
74
|
+
return [];
|
|
75
|
+
const content = [...thinkingBlocks(record.thinking)];
|
|
76
|
+
if (text)
|
|
77
|
+
content.push({ type: "text", text });
|
|
78
|
+
for (const call of calls)
|
|
79
|
+
content.push({ type: "toolCall", id: call.callId, name: call.name, arguments: argumentsOf(call) });
|
|
80
|
+
const assistant = assistantMessage(record, content, calls.length > 0 ? "toolUse" : "stop", identity);
|
|
81
|
+
const results = calls.map((call) => ({
|
|
82
|
+
role: "toolResult",
|
|
83
|
+
toolCallId: call.callId,
|
|
84
|
+
toolName: call.name,
|
|
85
|
+
content: [{ type: "text", text: call.result ?? MISSING_TOOL_RESULT }],
|
|
86
|
+
isError: call.result === undefined,
|
|
87
|
+
timestamp: record.timestamp,
|
|
88
|
+
}));
|
|
89
|
+
return [assistant, ...results];
|
|
90
|
+
}
|
|
91
|
+
function assistantMessage(record, content, stopReason, identity) {
|
|
92
|
+
return {
|
|
93
|
+
role: "assistant",
|
|
94
|
+
content,
|
|
95
|
+
api: identity.api,
|
|
96
|
+
provider: identity.provider,
|
|
97
|
+
model: identity.model,
|
|
98
|
+
usage: emptyUsage(),
|
|
99
|
+
stopReason,
|
|
100
|
+
timestamp: record.timestamp,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function userMessage(text, timestamp) {
|
|
104
|
+
return { role: "user", content: [{ type: "text", text }], timestamp };
|
|
105
|
+
}
|
|
106
|
+
/** 有推理原文才放 thinking 块,没有就一个不放 */
|
|
107
|
+
function thinkingBlocks(thinking) {
|
|
108
|
+
return thinking ? [{ type: "thinking", thinking, thinkingSignature: THINKING_SIGNATURE }] : [];
|
|
109
|
+
}
|
|
110
|
+
/** pi 的 toolCall 块要 arguments 是对象;平台存的不是对象就当空参数 */
|
|
111
|
+
function argumentsOf(call) {
|
|
112
|
+
const value = call.arguments;
|
|
113
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
114
|
+
}
|
|
115
|
+
/** 群成员说的话前面标谁说的:有成员号写 `[名字 #m_…] `,没成员号只写 `[名字] `;私聊没名字不标 */
|
|
116
|
+
function memberPrefix(record) {
|
|
117
|
+
const name = record.name;
|
|
118
|
+
const memberId = record.memberId;
|
|
119
|
+
if (name && memberId)
|
|
120
|
+
return `[${name} #${memberId}] `;
|
|
121
|
+
if (name)
|
|
122
|
+
return `[${name}] `;
|
|
123
|
+
if (memberId)
|
|
124
|
+
return `[#${memberId}] `;
|
|
125
|
+
return "";
|
|
126
|
+
}
|
|
127
|
+
/** 转发卡拍平成行:每条先一行 `【名字】`,正文原样跟一行;嵌套卡接着展开,超过层数上限写一句省略 */
|
|
128
|
+
function flattenCard(card, depth) {
|
|
129
|
+
const lines = [];
|
|
130
|
+
for (const item of card.items ?? []) {
|
|
131
|
+
lines.push(`【${item.name ?? ""}】`);
|
|
132
|
+
if (item.text)
|
|
133
|
+
lines.push(item.text);
|
|
134
|
+
if (item.messagesCard) {
|
|
135
|
+
if (depth >= CARD_MAX_DEPTH)
|
|
136
|
+
lines.push(CARD_TOO_DEEP);
|
|
137
|
+
else
|
|
138
|
+
lines.push(...flattenCard(item.messagesCard, depth + 1));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return lines;
|
|
142
|
+
}
|
|
143
|
+
// —— 出站:pi 消息 → 平台写口的每条 body
|
|
144
|
+
/**
|
|
145
|
+
* 不带工具调用的 assistant 消息写成 message 记录。
|
|
146
|
+
* 正常收尾的照写(带 thinking);error / aborted 收尾但已经说了半截的写成 interrupted;其余不写。
|
|
147
|
+
*/
|
|
148
|
+
export function outboundFromAssistant(m) {
|
|
149
|
+
if (m.content.some((block) => block.type === "toolCall"))
|
|
150
|
+
return null;
|
|
151
|
+
const text = textOf(m);
|
|
152
|
+
if (m.stopReason === "error" || m.stopReason === "aborted") {
|
|
153
|
+
return text ? { type: "message", text, interrupted: true } : null;
|
|
154
|
+
}
|
|
155
|
+
const thinking = thinkingOf(m);
|
|
156
|
+
return thinking === undefined ? { type: "message", text } : { type: "message", text, thinking };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 带工具调用的 assistant 消息连同它的结果写成一条 tool 记录;每个调用的 summary 取工具的 label,没有 label 用名字。
|
|
160
|
+
* 没有工具调用的返回 null(那种走 outboundFromAssistant)。
|
|
161
|
+
*/
|
|
162
|
+
export function outboundFromTurn(m, toolResults, tools) {
|
|
163
|
+
const calls = m.content.filter((block) => block.type === "toolCall");
|
|
164
|
+
if (calls.length === 0)
|
|
165
|
+
return null;
|
|
166
|
+
const toolCalls = calls.map((call) => {
|
|
167
|
+
const result = toolResults.find((r) => r.toolCallId === call.id);
|
|
168
|
+
const label = tools.find((t) => t.name === call.name)?.label;
|
|
169
|
+
return {
|
|
170
|
+
callId: call.id,
|
|
171
|
+
name: call.name,
|
|
172
|
+
summary: label || call.name,
|
|
173
|
+
arguments: call.arguments,
|
|
174
|
+
result: result === undefined ? MISSING_TOOL_RESULT : resultText(result),
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
const thinking = thinkingOf(m);
|
|
178
|
+
const record = { type: "tool", text: textOf(m), toolCalls };
|
|
179
|
+
if (thinking !== undefined)
|
|
180
|
+
record.thinking = thinking;
|
|
181
|
+
return record;
|
|
182
|
+
}
|
|
183
|
+
/** note 消息写成 note 记录 */
|
|
184
|
+
export function outboundFromNote(n) {
|
|
185
|
+
return { type: "note", text: n.text };
|
|
186
|
+
}
|
|
187
|
+
/** assistant 消息里所有 thinking 块的推理原文拼起来;一个字都没有返回 undefined */
|
|
188
|
+
export function thinkingOf(m) {
|
|
189
|
+
const thinking = m.content
|
|
190
|
+
.filter((block) => block.type === "thinking")
|
|
191
|
+
.map((block) => block.thinking)
|
|
192
|
+
.join("");
|
|
193
|
+
return thinking === "" ? undefined : thinking;
|
|
194
|
+
}
|
|
195
|
+
/** assistant 消息里所有 text 块的正文拼起来 */
|
|
196
|
+
export function textOf(m) {
|
|
197
|
+
return m.content
|
|
198
|
+
.filter((block) => block.type === "text")
|
|
199
|
+
.map((block) => block.text)
|
|
200
|
+
.join("");
|
|
201
|
+
}
|
|
202
|
+
/** 工具结果的文本:多个 text 块用换行接起来(跟 pi 的 openai-completions 回放时一样) */
|
|
203
|
+
function resultText(result) {
|
|
204
|
+
return result.content
|
|
205
|
+
.filter((block) => block.type === "text")
|
|
206
|
+
.map((block) => block.text)
|
|
207
|
+
.join("\n");
|
|
208
|
+
}
|
|
209
|
+
// —— 给 Agent 用的两个钩子
|
|
210
|
+
/**
|
|
211
|
+
* 交给 pi 的 convertToLlm:note 变 user;相邻两条 user 合并成一条(正文中间隔一个换行);user / assistant / toolResult 原样。
|
|
212
|
+
* 返回的是新数组,合并出来的也是新对象,不改传进来的消息。
|
|
213
|
+
*/
|
|
214
|
+
export const convertToLlm = (messages) => {
|
|
215
|
+
const out = [];
|
|
216
|
+
for (const m of messages) {
|
|
217
|
+
const next = toLlmMessage(m);
|
|
218
|
+
if (next === undefined)
|
|
219
|
+
continue;
|
|
220
|
+
const prev = out[out.length - 1];
|
|
221
|
+
if (next.role === "user" && prev !== undefined && prev.role === "user") {
|
|
222
|
+
out[out.length - 1] = mergeUsers(prev, next);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
out.push(next);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
};
|
|
230
|
+
/** 一条 AgentMessage 到模型那边是什么:note 变 user;pi harness 自己并进来的那几种 role(bashExecution / custom / …)这个运行时不产生,照 pi 默认 convertToLlm 的做法不给模型 */
|
|
231
|
+
function toLlmMessage(m) {
|
|
232
|
+
switch (m.role) {
|
|
233
|
+
case "note":
|
|
234
|
+
return userMessage(m.text, m.timestamp);
|
|
235
|
+
case "user":
|
|
236
|
+
case "assistant":
|
|
237
|
+
case "toolResult":
|
|
238
|
+
return m;
|
|
239
|
+
default:
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function mergeUsers(a, b) {
|
|
244
|
+
const blocks = [...userBlocks(a), ...userBlocks(b)];
|
|
245
|
+
const text = blocks
|
|
246
|
+
.filter((block) => block.type === "text")
|
|
247
|
+
.map((block) => block.text)
|
|
248
|
+
.join("\n");
|
|
249
|
+
const images = blocks.filter((block) => block.type === "image");
|
|
250
|
+
return { role: "user", content: [{ type: "text", text }, ...images], timestamp: a.timestamp };
|
|
251
|
+
}
|
|
252
|
+
function userBlocks(m) {
|
|
253
|
+
return typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* 把平台随拉取下发的 context 段插进历史:找到末尾那段连续的 user(note 也算,它到模型那边就是 user),插在这段前面,
|
|
257
|
+
* 后面的 convertToLlm 会把它跟这段合并;末尾是 assistant / toolResult 时,往前找最后一段 user 插在它前面。
|
|
258
|
+
* context 为空原样返回;否则返回新数组,不改传进来的。
|
|
259
|
+
*/
|
|
260
|
+
export function injectContext(messages, context) {
|
|
261
|
+
if (!context)
|
|
262
|
+
return messages;
|
|
263
|
+
let last = messages.length - 1;
|
|
264
|
+
while (last >= 0 && !isUserSide(messages[last]))
|
|
265
|
+
last--;
|
|
266
|
+
let start = last < 0 ? messages.length : last;
|
|
267
|
+
while (start > 0 && isUserSide(messages[start - 1]))
|
|
268
|
+
start--;
|
|
269
|
+
const injected = userMessage(context, Date.now());
|
|
270
|
+
return [...messages.slice(0, start), injected, ...messages.slice(start)];
|
|
271
|
+
}
|
|
272
|
+
function isUserSide(m) {
|
|
273
|
+
return m.role === "user" || m.role === "note";
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=convert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/conversation/convert.ts"],"names":[],"mappings":"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"}
|