@ai-zen/agents-host 0.0.0-stage → 0.1.0-alpha.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/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +759 -2
- package/dist/app.d.ts +384 -0
- package/dist/app.js +1148 -0
- package/dist/cli.d.ts +22 -0
- package/dist/cli.js +354 -0
- package/dist/config.d.ts +97 -0
- package/dist/config.js +171 -0
- package/dist/handlers/agentHandlers.d.ts +18 -0
- package/dist/handlers/agentHandlers.js +20 -0
- package/dist/handlers/assetHandlers.d.ts +29 -0
- package/dist/handlers/assetHandlers.js +35 -0
- package/dist/handlers/capabilities.d.ts +26 -0
- package/dist/handlers/capabilities.js +138 -0
- package/dist/handlers/completionHandlers.d.ts +18 -0
- package/dist/handlers/completionHandlers.js +13 -0
- package/dist/handlers/configHandlers.d.ts +31 -0
- package/dist/handlers/configHandlers.js +59 -0
- package/dist/handlers/conversationHandlers.d.ts +118 -0
- package/dist/handlers/conversationHandlers.js +314 -0
- package/dist/handlers/handoffHandlers.d.ts +17 -0
- package/dist/handlers/handoffHandlers.js +14 -0
- package/dist/handlers/hostHandlers.d.ts +39 -0
- package/dist/handlers/hostHandlers.js +56 -0
- package/dist/handlers/index.d.ts +128 -0
- package/dist/handlers/index.js +92 -0
- package/dist/handlers/messageHandlers.d.ts +11 -0
- package/dist/handlers/messageHandlers.js +10 -0
- package/dist/handlers/modelHandlers.d.ts +17 -0
- package/dist/handlers/modelHandlers.js +19 -0
- package/dist/handlers/noticeHandlers.d.ts +24 -0
- package/dist/handlers/noticeHandlers.js +24 -0
- package/dist/handlers/questionHandlers.d.ts +16 -0
- package/dist/handlers/questionHandlers.js +16 -0
- package/dist/handlers/searchHandlers.d.ts +12 -0
- package/dist/handlers/searchHandlers.js +12 -0
- package/dist/handlers/workspaceHandlers.d.ts +29 -0
- package/dist/handlers/workspaceHandlers.js +47 -0
- package/dist/i18n/en.d.ts +243 -0
- package/dist/i18n/en.js +270 -0
- package/dist/i18n/index.d.ts +43 -0
- package/dist/i18n/index.js +72 -0
- package/dist/i18n/zh.d.ts +240 -0
- package/dist/i18n/zh.js +257 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +15 -0
- package/dist/net/UploadReceiver.d.ts +186 -0
- package/dist/net/UploadReceiver.js +494 -0
- package/dist/net/WsHub.d.ts +231 -0
- package/dist/net/WsHub.js +689 -0
- package/dist/net/auth.d.ts +73 -0
- package/dist/net/auth.js +74 -0
- package/dist/net/discovery.d.ts +72 -0
- package/dist/net/discovery.js +171 -0
- package/dist/net/index.d.ts +14 -0
- package/dist/net/index.js +10 -0
- package/dist/runtime/ChatRunner.d.ts +374 -0
- package/dist/runtime/ChatRunner.js +1204 -0
- package/dist/runtime/ConversationDrafts.d.ts +43 -0
- package/dist/runtime/ConversationDrafts.js +47 -0
- package/dist/runtime/RunRegistry.d.ts +79 -0
- package/dist/runtime/RunRegistry.js +108 -0
- package/dist/runtime/ScopeRegistry.d.ts +153 -0
- package/dist/runtime/ScopeRegistry.js +259 -0
- package/dist/runtime/TransientRunner.d.ts +90 -0
- package/dist/runtime/TransientRunner.js +179 -0
- package/dist/runtime/index.d.ts +14 -0
- package/dist/runtime/index.js +10 -0
- package/dist/services/AgentService.d.ts +150 -0
- package/dist/services/AgentService.js +718 -0
- package/dist/services/AssetService.d.ts +338 -0
- package/dist/services/AssetService.js +739 -0
- package/dist/services/AutoNaming.d.ts +92 -0
- package/dist/services/AutoNaming.js +235 -0
- package/dist/services/CompletionService.d.ts +122 -0
- package/dist/services/CompletionService.js +442 -0
- package/dist/services/ConfigService.d.ts +171 -0
- package/dist/services/ConfigService.js +695 -0
- package/dist/services/ConversationService.d.ts +274 -0
- package/dist/services/ConversationService.js +902 -0
- package/dist/services/CredentialService.d.ts +33 -0
- package/dist/services/CredentialService.js +86 -0
- package/dist/services/DiscoveryService.d.ts +182 -0
- package/dist/services/DiscoveryService.js +998 -0
- package/dist/services/HandoffService.d.ts +189 -0
- package/dist/services/HandoffService.js +414 -0
- package/dist/services/Logger.d.ts +130 -0
- package/dist/services/Logger.js +256 -0
- package/dist/services/MessageService.d.ts +88 -0
- package/dist/services/MessageService.js +280 -0
- package/dist/services/ModelService.d.ts +89 -0
- package/dist/services/ModelService.js +489 -0
- package/dist/services/NoticeService.d.ts +93 -0
- package/dist/services/NoticeService.js +217 -0
- package/dist/services/QuestionService.d.ts +184 -0
- package/dist/services/QuestionService.js +457 -0
- package/dist/services/SearchService.d.ts +54 -0
- package/dist/services/SearchService.js +117 -0
- package/dist/services/WorkspaceService.d.ts +96 -0
- package/dist/services/WorkspaceService.js +213 -0
- package/dist/services/imageSize.d.ts +41 -0
- package/dist/services/imageSize.js +146 -0
- package/dist/services/index.d.ts +20 -0
- package/dist/services/index.js +15 -0
- package/dist/services/secretMask.d.ts +24 -0
- package/dist/services/secretMask.js +34 -0
- package/dist/storage/AssetRepository.d.ts +87 -0
- package/dist/storage/AssetRepository.js +180 -0
- package/dist/storage/ConversationRepository.d.ts +55 -0
- package/dist/storage/ConversationRepository.js +128 -0
- package/dist/storage/CredentialRepository.d.ts +28 -0
- package/dist/storage/CredentialRepository.js +47 -0
- package/dist/storage/Database.d.ts +95 -0
- package/dist/storage/Database.js +180 -0
- package/dist/storage/MessageRepository.d.ts +104 -0
- package/dist/storage/MessageRepository.js +323 -0
- package/dist/storage/MetaRepository.d.ts +21 -0
- package/dist/storage/MetaRepository.js +33 -0
- package/dist/storage/NoticeRepository.d.ts +31 -0
- package/dist/storage/NoticeRepository.js +89 -0
- package/dist/storage/QuestionRepository.d.ts +34 -0
- package/dist/storage/QuestionRepository.js +96 -0
- package/dist/storage/WorkspaceRepository.d.ts +25 -0
- package/dist/storage/WorkspaceRepository.js +44 -0
- package/dist/storage/dbWorker.d.ts +10 -0
- package/dist/storage/dbWorker.js +132 -0
- package/dist/storage/index.d.ts +20 -0
- package/dist/storage/index.js +16 -0
- package/dist/storage/migrations.d.ts +26 -0
- package/dist/storage/migrations.js +207 -0
- package/dist/storage/types.d.ts +298 -0
- package/dist/storage/types.js +14 -0
- package/dist/wire/errors.d.ts +105 -0
- package/dist/wire/errors.js +143 -0
- package/dist/wire/events.d.ts +102 -0
- package/dist/wire/events.js +13 -0
- package/dist/wire/frames.d.ts +106 -0
- package/dist/wire/frames.js +156 -0
- package/dist/wire/index.d.ts +31 -0
- package/dist/wire/index.js +31 -0
- package/dist/wire/methods.d.ts +1896 -0
- package/dist/wire/methods.js +99 -0
- package/dist/wire/states.d.ts +487 -0
- package/dist/wire/states.js +61 -0
- package/package.json +63 -3
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息服务:按对话读消息,以及把库里的一行还原成 core 的消息对象。
|
|
3
|
+
*
|
|
4
|
+
* 对应能力编号:A12 / L5(按消息 id 续拉:命中则从它开始且含它自身的最新内容,不命中返回全部)、
|
|
5
|
+
* F-C6(`around` 定位取窗,供客户端把搜索命中的那条摆到眼前)、F-I7(`toolCallId` 取子 Agent
|
|
6
|
+
* 自己的消息)、F-D9(会话创建时那条 `hidden` 的 cwd 消息默认不出网)、
|
|
7
|
+
* R21 / R22(消息原样用 core 的 `AgentNS.Message`,不额外挂时间戳这类外壳字段)。
|
|
8
|
+
*
|
|
9
|
+
* `toAgentMessage` 是这个仓库里「存储行 → core 消息」的唯一一份实现:对话服务算用量、判断
|
|
10
|
+
* 历史里有没有图也要走它,免得同一行在两处被读出两个样子。
|
|
11
|
+
*
|
|
12
|
+
* 正文不做转换(R30):仓储反序列化出来的就是 core 的 `AgentNS.MessageContent`,这里原样
|
|
13
|
+
* 搬上消息。剩下要做的是把「列里的字符串」收敛成 core 的取值(角色 / 状态 / 结束原因)
|
|
14
|
+
* 与把 `data` 列的 JSON 逐字段收窄到 core 的类型。
|
|
15
|
+
*/
|
|
16
|
+
import { AgentNS } from "@ai-zen/agents-core";
|
|
17
|
+
import { HostError } from "../wire/errors.js";
|
|
18
|
+
/** 角色取值表:库里的字符串认不出来时归 `Unknown`,不猜 */
|
|
19
|
+
const ROLES = [
|
|
20
|
+
AgentNS.Role.Unknown,
|
|
21
|
+
AgentNS.Role.System,
|
|
22
|
+
AgentNS.Role.User,
|
|
23
|
+
AgentNS.Role.Assistant,
|
|
24
|
+
AgentNS.Role.Function,
|
|
25
|
+
AgentNS.Role.Tool,
|
|
26
|
+
];
|
|
27
|
+
/** 状态取值表(D15:`status` 落盘,读回来要还原成 core 的那一套) */
|
|
28
|
+
const STATUSES = [
|
|
29
|
+
AgentNS.MessageStatus.Unknown,
|
|
30
|
+
AgentNS.MessageStatus.Pending,
|
|
31
|
+
AgentNS.MessageStatus.Completed,
|
|
32
|
+
AgentNS.MessageStatus.Error,
|
|
33
|
+
AgentNS.MessageStatus.Aborted,
|
|
34
|
+
AgentNS.MessageStatus.Writing,
|
|
35
|
+
];
|
|
36
|
+
/** 结束原因取值表 */
|
|
37
|
+
const FINISH_REASONS = [
|
|
38
|
+
AgentNS.FinishReason.Unknown,
|
|
39
|
+
AgentNS.FinishReason.Stop,
|
|
40
|
+
AgentNS.FinishReason.Length,
|
|
41
|
+
AgentNS.FinishReason.ContentFilter,
|
|
42
|
+
AgentNS.FinishReason.FunctionCall,
|
|
43
|
+
AgentNS.FinishReason.ToolCalls,
|
|
44
|
+
];
|
|
45
|
+
function asString(value) {
|
|
46
|
+
return typeof value === "string" ? value : undefined;
|
|
47
|
+
}
|
|
48
|
+
/** 可选的消息 id 参数:没给 / 空串一律当「没给」 */
|
|
49
|
+
function idParam(value) {
|
|
50
|
+
const id = asString(value)?.trim() ?? "";
|
|
51
|
+
return id.length > 0 ? id : undefined;
|
|
52
|
+
}
|
|
53
|
+
function asBoolean(value) {
|
|
54
|
+
return typeof value === "boolean" ? value : undefined;
|
|
55
|
+
}
|
|
56
|
+
/** 用量:三个字段都是数字才算数(core 的 `Usage` 只有这三个字段) */
|
|
57
|
+
function asUsage(value) {
|
|
58
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
59
|
+
return undefined;
|
|
60
|
+
const source = value;
|
|
61
|
+
const prompt = source.prompt_tokens;
|
|
62
|
+
const completion = source.completion_tokens;
|
|
63
|
+
const total = source.total_tokens;
|
|
64
|
+
if (typeof prompt !== "number" || typeof completion !== "number" || typeof total !== "number") {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
return { prompt_tokens: prompt, completion_tokens: completion, total_tokens: total };
|
|
68
|
+
}
|
|
69
|
+
function asFunctionCall(value) {
|
|
70
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
71
|
+
return undefined;
|
|
72
|
+
const source = value;
|
|
73
|
+
const call = {};
|
|
74
|
+
const name = asString(source.name);
|
|
75
|
+
const args = asString(source.arguments);
|
|
76
|
+
if (name !== undefined)
|
|
77
|
+
call.name = name;
|
|
78
|
+
if (args !== undefined)
|
|
79
|
+
call.arguments = args;
|
|
80
|
+
return call;
|
|
81
|
+
}
|
|
82
|
+
/** 工具调用数组:逐项按 core 的字段抄,认不出的字段直接丢(不往消息上塞未知键) */
|
|
83
|
+
function asToolCalls(value) {
|
|
84
|
+
if (!Array.isArray(value))
|
|
85
|
+
return undefined;
|
|
86
|
+
const calls = [];
|
|
87
|
+
for (const item of value) {
|
|
88
|
+
if (typeof item !== "object" || item === null)
|
|
89
|
+
continue;
|
|
90
|
+
const source = item;
|
|
91
|
+
const call = {};
|
|
92
|
+
if (typeof source.index === "number")
|
|
93
|
+
call.index = source.index;
|
|
94
|
+
const id = asString(source.id);
|
|
95
|
+
const type = asString(source.type);
|
|
96
|
+
const fn = asFunctionCall(source.function);
|
|
97
|
+
if (id !== undefined)
|
|
98
|
+
call.id = id;
|
|
99
|
+
if (type !== undefined)
|
|
100
|
+
call.type = type;
|
|
101
|
+
if (fn !== undefined)
|
|
102
|
+
call.function = fn;
|
|
103
|
+
calls.push(call);
|
|
104
|
+
}
|
|
105
|
+
return calls;
|
|
106
|
+
}
|
|
107
|
+
/** 库里的角色字符串 → core 的角色;搜索命中项也要用它,故对外导出 */
|
|
108
|
+
export function toCoreRole(value) {
|
|
109
|
+
return ROLES.includes(value) ? value : AgentNS.Role.Unknown;
|
|
110
|
+
}
|
|
111
|
+
function toStatus(value) {
|
|
112
|
+
return STATUSES.includes(value)
|
|
113
|
+
? value
|
|
114
|
+
: AgentNS.MessageStatus.Unknown;
|
|
115
|
+
}
|
|
116
|
+
function toFinishReason(value) {
|
|
117
|
+
return FINISH_REASONS.includes(value)
|
|
118
|
+
? value
|
|
119
|
+
: AgentNS.FinishReason.Unknown;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 库里一行 → core 的消息对象。
|
|
123
|
+
*
|
|
124
|
+
* 正文原样透传(R30):仓储反序列化出来的就是 core 的 `MessageContent`,逐段重建会把 core
|
|
125
|
+
* 新增的段类型 / 字段丢掉,也会让同一条消息在不同读取路径上读出两个样子。
|
|
126
|
+
*
|
|
127
|
+
* 其余字段名与取值也全部照 core(R21):`role` / `status` / `finish_reason` 从列里的字符串
|
|
128
|
+
* 收敛成 core 的取值,`data` 列的 JSON 逐字段收窄,认不出的不往消息上搬。
|
|
129
|
+
*/
|
|
130
|
+
export function toAgentMessage(row) {
|
|
131
|
+
const data = row.data ?? {};
|
|
132
|
+
const message = {
|
|
133
|
+
id: row.id,
|
|
134
|
+
role: toCoreRole(row.role),
|
|
135
|
+
status: toStatus(row.status),
|
|
136
|
+
omit: row.omit,
|
|
137
|
+
};
|
|
138
|
+
// `null` 在库里表示「这条消息没有正文」,core 的 `content` 是可选的,所以不搬空值
|
|
139
|
+
if (row.content !== null)
|
|
140
|
+
message.content = row.content;
|
|
141
|
+
const name = asString(data.name);
|
|
142
|
+
if (name !== undefined)
|
|
143
|
+
message.name = name;
|
|
144
|
+
const toolCallId = asString(data.tool_call_id);
|
|
145
|
+
if (toolCallId !== undefined)
|
|
146
|
+
message.tool_call_id = toolCallId;
|
|
147
|
+
const reasoning = asString(data.reasoning_content);
|
|
148
|
+
if (reasoning !== undefined)
|
|
149
|
+
message.reasoning_content = reasoning;
|
|
150
|
+
const toolCalls = asToolCalls(data.tool_calls);
|
|
151
|
+
if (toolCalls !== undefined)
|
|
152
|
+
message.tool_calls = toolCalls;
|
|
153
|
+
const functionCall = asFunctionCall(data.function_call);
|
|
154
|
+
if (functionCall !== undefined)
|
|
155
|
+
message.function_call = functionCall;
|
|
156
|
+
const finishReason = asString(data.finish_reason);
|
|
157
|
+
if (finishReason !== undefined)
|
|
158
|
+
message.finish_reason = toFinishReason(finishReason);
|
|
159
|
+
const usage = asUsage(data.usage);
|
|
160
|
+
if (usage !== undefined)
|
|
161
|
+
message.usage = usage;
|
|
162
|
+
const subAgentUsage = asUsage(data.subAgentUsage);
|
|
163
|
+
if (subAgentUsage !== undefined)
|
|
164
|
+
message.subAgentUsage = subAgentUsage;
|
|
165
|
+
const hidden = asBoolean(data.hidden);
|
|
166
|
+
if (hidden !== undefined)
|
|
167
|
+
message.hidden = hidden;
|
|
168
|
+
return message;
|
|
169
|
+
}
|
|
170
|
+
/** 从消息的其余字段里读最近一次请求的用量;没有就是 `null`(对话服务算上下文占用用) */
|
|
171
|
+
export function readUsage(data) {
|
|
172
|
+
return asUsage(data?.usage) ?? null;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* `around` 取的窗口条数(PRD F-C6 的定位)。
|
|
176
|
+
*
|
|
177
|
+
* `message.list` 没有 `limit`:它一直是「一段对话全给」,`around` 只是把这一段收成一窗,
|
|
178
|
+
* 40 条足够看够上下文而不会把屏刷满。消费者是桌面端的搜索命中跳转(PLAN W4)。
|
|
179
|
+
*/
|
|
180
|
+
const AROUND_WINDOW = 40;
|
|
181
|
+
/**
|
|
182
|
+
* 以某条消息为中心取一窗(PRD F-C6 的定位)。
|
|
183
|
+
*
|
|
184
|
+
* `rows` 是这段对话(或这个子 Agent)的全部消息、已按先后排序。命中那条一定在窗口里:
|
|
185
|
+
* 前面先给一半(`floor((size-1)/2)` 条),剩下的额度全给后面;命中靠近两端时窗口自然短一些。
|
|
186
|
+
* 命中不在 `rows` 里时回 `null` —— 调用方给空页(这条可能刚被撤回/换过库)。
|
|
187
|
+
*/
|
|
188
|
+
function windowAround(rows, around, size) {
|
|
189
|
+
const index = rows.findIndex((row) => row.id === around);
|
|
190
|
+
if (index < 0)
|
|
191
|
+
return null;
|
|
192
|
+
const before = Math.floor((size - 1) / 2);
|
|
193
|
+
const start = Math.max(0, index - before);
|
|
194
|
+
const end = Math.min(rows.length, index + (size - before));
|
|
195
|
+
return rows.slice(start, end);
|
|
196
|
+
}
|
|
197
|
+
export class MessageService {
|
|
198
|
+
conversations;
|
|
199
|
+
messages;
|
|
200
|
+
drafts;
|
|
201
|
+
constructor(deps) {
|
|
202
|
+
this.conversations = deps.conversations;
|
|
203
|
+
this.messages = deps.messages;
|
|
204
|
+
this.drafts = deps.drafts;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 读一段对话的消息(architecture A12 / PRD F-C6)。
|
|
208
|
+
*
|
|
209
|
+
* - 不给 `after`、也不给 `around`:整段历史;
|
|
210
|
+
* - 给了 `after` 且该 id 就在这段对话里:**从这条开始**(含它,内容是最新的,可能是流式中那条的
|
|
211
|
+
* 部分内容)+ 其后全部,并把 `anchor` 报成它;
|
|
212
|
+
* - 给了 `after` 但这段对话里没有这个 id(已删、或换过库):退回整段历史,`anchor` 为空 ——
|
|
213
|
+
* 客户端据此知道这是一次全量;
|
|
214
|
+
* - 给了 `around`:以这条为中心取 {@link AROUND_WINDOW} 条(命中那条一定在里面),
|
|
215
|
+
* `anchor` 报成它;命中不在可见集里时给空页 + 空 `anchor`,**不报错**(消息可能刚被撤回)。
|
|
216
|
+
* `around` 与 `after` 同时给时**只认 `around`**:两者是两种取法(一个「从这里往下」、一个
|
|
217
|
+
* 「围绕这里」),混在一起没有唯一答案。
|
|
218
|
+
*
|
|
219
|
+
* `toolCallId` 取某个子 Agent 自己的消息(PRD F-I7):它按消息落库时记下的
|
|
220
|
+
* `parentToolCallId` 取(那次委派的工具调用 id),走的是另一条查询 —— 子 Agent 的消息不在
|
|
221
|
+
* 主线历史里(`listByConversation` 只给主线),所以两个参数同时给时 `after` 只用来定 `anchor`,
|
|
222
|
+
* 结果集仍是那个子 Agent 的全部消息。`around` 同此:`toolCallId` 决定读哪一段,`around`
|
|
223
|
+
* 只在那一段里定位。
|
|
224
|
+
*
|
|
225
|
+
* `hidden` 的消息(会话创建时那条 cwd 消息、交接摘要)默认不回:core 的这个字段就是
|
|
226
|
+
* 「这条不给人看」。要看到它们(排查、或客户端显式要)就传 `includeHidden`。
|
|
227
|
+
*/
|
|
228
|
+
async list(params) {
|
|
229
|
+
const conversation = await this.requireConversation(params.conversationId);
|
|
230
|
+
const options = { includeHidden: params.includeHidden === true };
|
|
231
|
+
const around = idParam(params.around);
|
|
232
|
+
const after = idParam(params.after);
|
|
233
|
+
// `around` 优先:它给了就完全不管 `after`(连 `anchor` 也不由 `after` 定)
|
|
234
|
+
let anchor = null;
|
|
235
|
+
if (around === undefined && after !== undefined) {
|
|
236
|
+
const anchorRow = await this.messages.get(after);
|
|
237
|
+
if (anchorRow && anchorRow.conversationId === conversation.id) {
|
|
238
|
+
anchor = anchorRow.id;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const toolCallId = idParam(params.toolCallId);
|
|
242
|
+
if (toolCallId !== undefined) {
|
|
243
|
+
const delegated = await this.messages.listByParentToolCallId(conversation.id, toolCallId, options);
|
|
244
|
+
return this.respond(conversation.id, delegated, anchor, around);
|
|
245
|
+
}
|
|
246
|
+
// `around` 要的是「整段之后再取窗」,所以这一跳不带 `after`(带了就只剩后半段)
|
|
247
|
+
const rows = await this.messages.listByConversation(conversation.id, around === undefined ? after : undefined, options);
|
|
248
|
+
return this.respond(conversation.id, rows, anchor, around);
|
|
249
|
+
}
|
|
250
|
+
/** 把一次读到的行拼成出参;`around` 给了就收成一窗(命中不在里面就是空页) */
|
|
251
|
+
respond(conversationId, rows, anchor, around) {
|
|
252
|
+
if (around === undefined) {
|
|
253
|
+
return { conversationId, anchor, messages: rows.map((row) => toAgentMessage(row)) };
|
|
254
|
+
}
|
|
255
|
+
const window = windowAround(rows, around, AROUND_WINDOW);
|
|
256
|
+
if (window === null) {
|
|
257
|
+
return { conversationId, anchor: null, messages: [] };
|
|
258
|
+
}
|
|
259
|
+
return { conversationId, anchor: around, messages: window.map((row) => toAgentMessage(row)) };
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* 这段对话存不存在:库里那一行,或者一份还没落库的草稿(R56)。
|
|
263
|
+
*
|
|
264
|
+
* 草稿还没有任何消息,所以读它总是得到空集 —— 但它不该被读成「找不到这段对话」:
|
|
265
|
+
* 客户端刚 `create` 完就会拉一次消息与现场。
|
|
266
|
+
*/
|
|
267
|
+
async requireConversation(conversationId) {
|
|
268
|
+
const id = typeof conversationId === "string" ? conversationId.trim() : "";
|
|
269
|
+
const row = id.length === 0 ? undefined : await this.conversations.get(id);
|
|
270
|
+
if (row)
|
|
271
|
+
return row;
|
|
272
|
+
const draft = id.length === 0 ? undefined : this.drafts?.get(id);
|
|
273
|
+
if (draft)
|
|
274
|
+
return draft;
|
|
275
|
+
throw new HostError("not_found", {
|
|
276
|
+
params: { entity: "conversation" },
|
|
277
|
+
detail: { conversationId },
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型服务:模型清单、图片模型、模型增删改、恢复出厂(F-F3 / F-F4 / F-F7 / D6 / D27)。
|
|
3
|
+
*
|
|
4
|
+
* 配置只有一处真相(D21):`config.json`,读写都经 SDK 的 `ConfigManager`。这个服务不缓存配置,
|
|
5
|
+
* 每次调用现读现写 —— 配置是几百行的 JSON,读一次比「缓存与磁盘谁更新」的排查便宜得多。
|
|
6
|
+
*
|
|
7
|
+
* 两条与产品行为直接相关的口径:
|
|
8
|
+
*
|
|
9
|
+
* - **编辑即转自定义**(F-F3):改过的模型要打上 `custom: true`,否则 SDK 下次初始化会拿出厂
|
|
10
|
+
* 定义把它冲回去(`syncManagedModels` 只保留 `custom: true` 的条目)。
|
|
11
|
+
* - **删除要算引用**(F-F2 / F-F7):被助手引用的模型不能删,并且要列出「还有谁指它」;
|
|
12
|
+
* 默认项指向被删的那个模型时要改指或清空,不留悬空引用。
|
|
13
|
+
*/
|
|
14
|
+
import type { AgentRepository } from "@ai-zen/agents-sdk";
|
|
15
|
+
import type { AppConfig, ConfigManager, Endpoint, ImageModel, Model, ReasoningPreset, ReasoningPresets } from "@ai-zen/agents-sdk";
|
|
16
|
+
import type { ModelCreateParams, ModelCreateResult, ModelDeleteImageParams, ModelDeleteImageResult, ModelDeleteParams, ModelDeleteResult, ModelListImagesResult, ModelListParams, ModelListResult, ModelRestoreFactoryParams, ModelRestoreFactoryResult, ModelSaveImageParams, ModelSaveImageResult, ModelUpdateParams, ModelUpdateResult, ModelView, ProblemFact } from "../wire/index.js";
|
|
17
|
+
/** 模型服务的依赖;由单根组装后注入 */
|
|
18
|
+
export interface ModelServiceOptions {
|
|
19
|
+
/** 配置唯一入口(SDK) */
|
|
20
|
+
configManager: ConfigManager;
|
|
21
|
+
/** 助手定义仓储(`<basePath>/agents/`):模型被谁引用要读它 */
|
|
22
|
+
agents?: AgentRepository;
|
|
23
|
+
}
|
|
24
|
+
/** 按 id 找端点 */
|
|
25
|
+
export declare function findEndpoint(config: AppConfig, endpointId: string): Endpoint | undefined;
|
|
26
|
+
/** 按 id 找模型 */
|
|
27
|
+
export declare function findModel(config: AppConfig, modelId: string): Model | undefined;
|
|
28
|
+
/** 按 id 找图片模型 */
|
|
29
|
+
export declare function findImageModel(config: AppConfig, imageModelId: string): ImageModel | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* 这个模型现在能不能用;能用回 `null`,不能用回一条带原因的事实(F-B5:藏着不如说清楚)。
|
|
32
|
+
*
|
|
33
|
+
* 两种用不了的原因都在配置层:端点没了(模型指向一个已删的端点),或端点还没配密钥
|
|
34
|
+
* (F-A7 的「这个端点配好了没」)。模型自身的字段没有第三种「用不了」的形态。
|
|
35
|
+
*/
|
|
36
|
+
export declare function modelUnavailableProblem(config: AppConfig, model: Model): ProblemFact | null;
|
|
37
|
+
/** 模型引用的思考策略(表里没有、或者一条档位都没有,就是这个模型不可调,PRD F-B7) */
|
|
38
|
+
export declare function reasoningPresetOf(model: Model | undefined, presets: ReasoningPresets): ReasoningPreset | undefined;
|
|
39
|
+
/** 这个模型可选的思考档位名(客户端据此渲染「能调哪几档」) */
|
|
40
|
+
export declare function thinkingLevelsOf(model: Model | undefined, presets: ReasoningPresets): string[];
|
|
41
|
+
export declare class ModelService {
|
|
42
|
+
private readonly configManager;
|
|
43
|
+
private readonly agents?;
|
|
44
|
+
constructor(options: ModelServiceOptions);
|
|
45
|
+
/** 读一次配置(写方法都从这里出发,避免「基于旧快照覆盖别人刚写的东西」) */
|
|
46
|
+
readConfig(): Promise<AppConfig>;
|
|
47
|
+
/**
|
|
48
|
+
* 模型清单(F-F3)。
|
|
49
|
+
*
|
|
50
|
+
* 默认连不可用的也列(F-B5 的口径:藏起来会让人以为它没了),`includeUnavailable: false`
|
|
51
|
+
* 时才把它们筛掉。
|
|
52
|
+
*/
|
|
53
|
+
list(params?: ModelListParams): Promise<ModelListResult>;
|
|
54
|
+
/** 图片模型与那个「默认图片模型」(F-F4 / D6) */
|
|
55
|
+
listImages(): Promise<ModelListImagesResult>;
|
|
56
|
+
/** 新建模型(建出来就是自定义,F-F3) */
|
|
57
|
+
create(params: ModelCreateParams): Promise<ModelCreateResult>;
|
|
58
|
+
/** 编辑模型(编辑即转自定义,F-F3 —— 否则下轮初始化会被出厂定义冲回去) */
|
|
59
|
+
update(params: ModelUpdateParams): Promise<ModelUpdateResult>;
|
|
60
|
+
/**
|
|
61
|
+
* 删除模型(F-F2 / F-F7)。
|
|
62
|
+
*
|
|
63
|
+
* 被助手引用时拒删并列出引用者;默认项(兜底模型 / 迁移模型 / 取名模型)指向它时改指或清空
|
|
64
|
+
* —— 留在配置里指向一个不存在的 id,会在下一次真要跑的时候才炸出来。
|
|
65
|
+
*/
|
|
66
|
+
delete(params: ModelDeleteParams): Promise<ModelDeleteResult>;
|
|
67
|
+
/**
|
|
68
|
+
* 恢复出厂(F-F3:整条替换)。
|
|
69
|
+
*
|
|
70
|
+
* 只有出厂清单里有的模型才有「出厂版本」可恢复;用户自建的模型没有,回
|
|
71
|
+
* `error.notFactoryModel`。恢复之后这一条重新归 SDK 托管(去掉 `custom`),
|
|
72
|
+
* 所以下次初始化会跟着出厂清单走。
|
|
73
|
+
*/
|
|
74
|
+
restoreFactory(params: ModelRestoreFactoryParams): Promise<ModelRestoreFactoryResult>;
|
|
75
|
+
/** 图片模型的创建与修改(F-F4:同一个方法兼两件事,`id` 在就改、不在就建) */
|
|
76
|
+
saveImage(params: ModelSaveImageParams): Promise<ModelSaveImageResult>;
|
|
77
|
+
/** 删除图片模型;它正被「默认图片模型」指着时拒删(F-F4 / F-F7) */
|
|
78
|
+
deleteImage(params: ModelDeleteImageParams): Promise<ModelDeleteImageResult>;
|
|
79
|
+
/** 一行模型定义 → 对外形状 */
|
|
80
|
+
toView(config: AppConfig, model: Model, presets: ReasoningPresets): ModelView;
|
|
81
|
+
/** 入参(新建)→ 一条模型定义;校验全在这里,写盘之前就拦下 */
|
|
82
|
+
private buildModel;
|
|
83
|
+
/**
|
|
84
|
+
* 谁在引用这个模型:读助手定义(一实体一文件,F-G3)。
|
|
85
|
+
*
|
|
86
|
+
* 默认项不列在这里:它们会跟着改指或清空(F-F7),是「删完自动收拾」而不是「拦住删除」。
|
|
87
|
+
*/
|
|
88
|
+
private referencesOf;
|
|
89
|
+
}
|