@huyooo/ai-chat-core 0.2.19 → 0.2.20

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.
@@ -0,0 +1,452 @@
1
+ /**
2
+ * AI Chat 事件系统
3
+ *
4
+ * 模块化的事件类型定义,用于 Agent 与前端之间的通信。
5
+ *
6
+ * 设计原则:
7
+ * 1. 每种事件类型有明确的数据结构
8
+ * 2. 事件分组便于管理和扩展
9
+ * 3. 类型安全,避免 unknown
10
+ * 4. 统一的时间追踪(开始/结束/耗时)
11
+ */
12
+ /**
13
+ * 搜索结果项(统一契约)
14
+ * 无论搜索来源是厂商原生(ARK annotation)还是 Tavily 工具,协议/编排层都会归一为此结构,
15
+ * 前端只需依赖 title/url/snippet 即可。
16
+ */
17
+ interface SearchResult {
18
+ title: string;
19
+ url: string;
20
+ snippet: string;
21
+ }
22
+ /** 工具调用状态 */
23
+ type ToolCallStatus = 'pending' | 'running' | 'success' | 'error';
24
+ /** 工具调用信息(用于前端状态管理) */
25
+ interface ToolCallInfo {
26
+ id: string;
27
+ name: string;
28
+ args: Record<string, unknown>;
29
+ status: ToolCallStatus;
30
+ result?: string;
31
+ error?: string;
32
+ startedAt?: number;
33
+ duration?: number;
34
+ }
35
+ /** Token 使用统计 */
36
+ interface TokenUsage {
37
+ promptTokens: number;
38
+ completionTokens: number;
39
+ totalTokens: number;
40
+ /** 思考 token 数(如果有) */
41
+ reasoningTokens?: number;
42
+ /** 缓存命中 token 数(如果有) */
43
+ cachedTokens?: number;
44
+ }
45
+ /** 错误类别 - 参考 AI SDK 设计 */
46
+ type ErrorCategory = 'api' | 'rate_limit' | 'validation' | 'tool' | 'timeout' | 'abort' | 'parse' | 'unknown';
47
+ /** 错误详情 - 结构化错误信息 */
48
+ interface ErrorDetails {
49
+ /** 错误分类 */
50
+ category: ErrorCategory;
51
+ /** 人类可读的错误信息 */
52
+ message: string;
53
+ /** 错误代码(如 'RATE_LIMIT', 'NETWORK_ERROR') */
54
+ code?: string;
55
+ /** HTTP 状态码(如果适用) */
56
+ statusCode?: number;
57
+ /** HTTP 状态文本 */
58
+ statusText?: string;
59
+ /** 是否可重试 */
60
+ retryable?: boolean;
61
+ /** 重试等待时间(秒) */
62
+ retryAfter?: number;
63
+ /** 原始错误信息(用于调试) */
64
+ cause?: string;
65
+ /** 错误发生的上下文(如模型名、工具名) */
66
+ context?: string;
67
+ }
68
+ /** 思考开始事件 */
69
+ interface ThinkingStartEvent {
70
+ type: 'thinking_start';
71
+ data: {
72
+ /** 开始时间戳(毫秒) */
73
+ startedAt: number;
74
+ };
75
+ }
76
+ /** 思考内容增量事件 */
77
+ interface ThinkingDeltaEvent {
78
+ type: 'thinking_delta';
79
+ data: {
80
+ /** 增量内容 */
81
+ content: string;
82
+ };
83
+ }
84
+ /** 思考结束事件 */
85
+ interface ThinkingEndEvent {
86
+ type: 'thinking_end';
87
+ data: {
88
+ /** 结束时间戳(毫秒) */
89
+ endedAt: number;
90
+ /** 思考耗时(毫秒) */
91
+ duration: number;
92
+ };
93
+ }
94
+ /** 思考相关事件联合类型 */
95
+ type ThinkingEvent = ThinkingStartEvent | ThinkingDeltaEvent | ThinkingEndEvent;
96
+ /** 搜索开始事件 */
97
+ interface SearchStartEvent {
98
+ type: 'search_start';
99
+ data: {
100
+ /** 搜索查询词 */
101
+ query: string;
102
+ /** 开始时间戳(毫秒) */
103
+ startedAt: number;
104
+ };
105
+ }
106
+ /** 搜索结果事件(成功完成) */
107
+ interface SearchResultEvent {
108
+ type: 'search_result';
109
+ data: {
110
+ /** 搜索结果列表 */
111
+ results: SearchResult[];
112
+ /** 结束时间戳(毫秒) */
113
+ endedAt: number;
114
+ /** 搜索耗时(毫秒) */
115
+ duration: number;
116
+ };
117
+ }
118
+ /** 搜索结束事件(失败或取消) */
119
+ interface SearchEndEvent {
120
+ type: 'search_end';
121
+ data: {
122
+ /** 是否成功 */
123
+ success: boolean;
124
+ /** 错误信息(失败时) */
125
+ error?: string;
126
+ /** 结束时间戳(毫秒) */
127
+ endedAt: number;
128
+ /** 搜索耗时(毫秒) */
129
+ duration: number;
130
+ };
131
+ }
132
+ /** 搜索相关事件联合类型 */
133
+ type SearchEvent = SearchStartEvent | SearchResultEvent | SearchEndEvent;
134
+ /** 工具调用开始事件 */
135
+ interface ToolCallStartEvent {
136
+ type: 'tool_call_start';
137
+ data: {
138
+ /** 工具调用 ID */
139
+ id: string;
140
+ /** 工具名称 */
141
+ name: string;
142
+ /** 工具参数 */
143
+ args: Record<string, unknown>;
144
+ /** 开始时间戳(毫秒) */
145
+ startedAt: number;
146
+ };
147
+ }
148
+ /** 副作用定义(从 types.ts 复制,避免循环依赖) */
149
+ interface SideEffect {
150
+ type: string;
151
+ success: boolean;
152
+ data?: unknown;
153
+ message?: string;
154
+ }
155
+ /** 工具调用结果事件 */
156
+ interface ToolCallResultEvent {
157
+ type: 'tool_call_result';
158
+ data: {
159
+ /** 工具调用 ID */
160
+ id: string;
161
+ /** 工具名称 */
162
+ name: string;
163
+ /** 执行结果 */
164
+ result: string;
165
+ /** 是否成功 */
166
+ success: boolean;
167
+ /** 错误信息(失败时) */
168
+ error?: string;
169
+ /** 结束时间戳(毫秒) */
170
+ endedAt: number;
171
+ /** 执行耗时(毫秒) */
172
+ duration: number;
173
+ /**
174
+ * 工具副作用
175
+ * 前端可根据此字段处理通知、刷新文件列表等
176
+ */
177
+ sideEffects?: SideEffect[];
178
+ /**
179
+ * 结果类型(用于前端渲染)
180
+ *
181
+ * 当工具定义了 resultType 且执行成功时,前端会根据此类型生成对应的 ContentPart
182
+ * 例如:resultType: 'weather' 会生成 { type: 'weather', ...result }
183
+ */
184
+ resultType?: string;
185
+ };
186
+ }
187
+ /** 工具输出增量事件(用于 stdout/stderr 流式展示) */
188
+ interface ToolCallOutputEvent {
189
+ type: 'tool_call_output';
190
+ data: {
191
+ /** 工具调用 ID */
192
+ id: string;
193
+ /** 工具名称 */
194
+ name: string;
195
+ /** 输出流类型 */
196
+ stream: 'stdout' | 'stderr';
197
+ /** 输出增量内容 */
198
+ chunk: string;
199
+ /** 时间戳 */
200
+ at: number;
201
+ };
202
+ }
203
+ /** 工具执行批准请求事件(manual 模式) */
204
+ interface ToolApprovalRequestEvent {
205
+ type: 'tool_approval_request';
206
+ data: {
207
+ /** 工具调用 ID */
208
+ id: string;
209
+ /** 工具名称 */
210
+ name: string;
211
+ /** 工具参数 */
212
+ args: Record<string, unknown>;
213
+ /** 请求时间戳 */
214
+ requestedAt: number;
215
+ };
216
+ }
217
+ /**
218
+ * 客户端工具调用请求事件(透传模式)
219
+ *
220
+ * 用于用户自定义工具:
221
+ * - AI 返回 tool_call
222
+ * - 服务端不执行,透传给客户端
223
+ * - 客户端执行后,发新请求继续对话
224
+ */
225
+ interface ToolCallRequestEvent {
226
+ type: 'tool_call_request';
227
+ data: {
228
+ /** 工具调用 ID */
229
+ id: string;
230
+ /** 工具名称 */
231
+ name: string;
232
+ /** 工具参数 */
233
+ args: Record<string, unknown>;
234
+ /** 请求时间戳 */
235
+ requestedAt: number;
236
+ };
237
+ }
238
+ /** 工具相关事件联合类型 */
239
+ type ToolEvent = ToolCallStartEvent | ToolCallResultEvent | ToolApprovalRequestEvent | ToolCallOutputEvent | ToolCallRequestEvent;
240
+ /** 文本增量事件 */
241
+ interface TextDeltaEvent {
242
+ type: 'text_delta';
243
+ data: {
244
+ /** 增量文本 */
245
+ content: string;
246
+ };
247
+ }
248
+ /** 文本相关事件联合类型 */
249
+ type TextEvent = TextDeltaEvent;
250
+ /** 计划步骤状态 */
251
+ type PlanStepStatus = 'pending' | 'in_progress' | 'done' | 'failed';
252
+ /** 计划步骤 */
253
+ interface PlanStep {
254
+ /** 步骤 ID(模型生成,如 "1", "scan", "move-images") */
255
+ id: string;
256
+ /** 步骤描述 */
257
+ title: string;
258
+ /** 步骤状态 */
259
+ status: PlanStepStatus;
260
+ }
261
+ /** 完成事件 */
262
+ interface DoneEvent {
263
+ type: 'done';
264
+ data: {
265
+ /** 完整的最终文本 */
266
+ text: string;
267
+ /** Token 使用统计 */
268
+ usage?: TokenUsage;
269
+ /** 总耗时(毫秒) */
270
+ duration?: number;
271
+ };
272
+ }
273
+ /** 错误事件 - 结构化错误信息 */
274
+ interface ErrorEvent {
275
+ type: 'error';
276
+ data: ErrorDetails;
277
+ }
278
+ /** 中止事件 - 用户主动取消 */
279
+ interface AbortEvent {
280
+ type: 'abort';
281
+ data: {
282
+ /** 中止原因 */
283
+ reason?: string;
284
+ /** 中止时间戳 */
285
+ abortedAt: number;
286
+ };
287
+ }
288
+ /** 状态相关事件联合类型 */
289
+ type StatusEvent = DoneEvent | ErrorEvent | AbortEvent;
290
+ /** 步骤开始事件 */
291
+ interface StepStartEvent {
292
+ type: 'step_start';
293
+ data: {
294
+ /** 步骤编号(从 1 开始) */
295
+ stepNumber: number;
296
+ /** 步骤描述(可选) */
297
+ description?: string;
298
+ /** 开始时间戳 */
299
+ startedAt: number;
300
+ };
301
+ }
302
+ /** 步骤结束事件 */
303
+ interface StepEndEvent {
304
+ type: 'step_end';
305
+ data: {
306
+ /** 步骤编号 */
307
+ stepNumber: number;
308
+ /** 结束时间戳 */
309
+ endedAt: number;
310
+ /** 耗时(毫秒) */
311
+ duration: number;
312
+ };
313
+ }
314
+ /** 步骤相关事件联合类型 */
315
+ type StepEvent = StepStartEvent | StepEndEvent;
316
+ /**
317
+ * Agent 当前阶段:前端根据此字段直接渲染 loading 状态,不需要「猜」。
318
+ *
319
+ * - thinking:模型正在思考(初始请求 / 工具执行完后等待下一轮)→ 显示「正在思考...」
320
+ * - null:有可见活动(思考/搜索/工具/文字流),不需要额外 loading
321
+ */
322
+ type AgentPhase = 'thinking' | null;
323
+ /** Agent 状态事件(前端用于渲染 loading 提示) */
324
+ interface AgentStatusEvent {
325
+ type: 'agent_status';
326
+ data: {
327
+ /** 当前阶段 */
328
+ phase: AgentPhase;
329
+ };
330
+ }
331
+ /** 创建 agent_status 事件 */
332
+ declare function createAgentStatus(phase: AgentPhase): AgentStatusEvent;
333
+ /** 所有事件类型 */
334
+ type ChatEvent = ThinkingEvent | SearchEvent | ToolEvent | TextEvent | StatusEvent | StepEvent | AgentStatusEvent;
335
+ /** 事件类型字符串 */
336
+ type ChatEventType = ChatEvent['type'];
337
+ /** 所有合法事件类型(用于契约测试:Orchestrator/适配器产出必须在此集合内) */
338
+ declare const CHAT_EVENT_TYPES: readonly ChatEventType[];
339
+ /**
340
+ * 创建思考开始事件
341
+ */
342
+ declare function createThinkingStart(): ThinkingStartEvent;
343
+ /**
344
+ * 创建思考增量事件
345
+ */
346
+ declare function createThinkingDelta(content: string): ThinkingDeltaEvent;
347
+ /**
348
+ * 创建思考结束事件
349
+ */
350
+ declare function createThinkingEnd(startedAt: number): ThinkingEndEvent;
351
+ /**
352
+ * 创建搜索开始事件
353
+ */
354
+ declare function createSearchStart(query: string): SearchStartEvent;
355
+ /**
356
+ * 创建搜索结果事件(成功)
357
+ */
358
+ declare function createSearchResult(results: SearchResult[], startedAt: number): SearchResultEvent;
359
+ /**
360
+ * 创建搜索结束事件(失败或取消)
361
+ */
362
+ declare function createSearchEnd(success: boolean, startedAt: number, error?: string): SearchEndEvent;
363
+ /**
364
+ * 创建工具调用开始事件
365
+ */
366
+ declare function createToolCallStart(id: string, name: string, args: Record<string, unknown>): ToolCallStartEvent;
367
+ /**
368
+ * 创建工具调用结果事件
369
+ */
370
+ declare function createToolCallResult(id: string, name: string, result: string, success: boolean, startedAt: number, error?: string, sideEffects?: SideEffect[], resultType?: string): ToolCallResultEvent;
371
+ /**
372
+ * 创建工具输出增量事件
373
+ */
374
+ declare function createToolCallOutput(id: string, name: string, stream: 'stdout' | 'stderr', chunk: string): ToolCallOutputEvent;
375
+ /**
376
+ * 创建客户端工具调用请求事件(透传模式)
377
+ *
378
+ * 用于用户自定义工具,服务端不执行,透传给客户端
379
+ */
380
+ declare function createToolCallRequest(id: string, name: string, args: Record<string, unknown>): ToolCallRequestEvent;
381
+ /**
382
+ * 创建文本增量事件
383
+ */
384
+ declare function createTextDelta(content: string): TextDeltaEvent;
385
+ /**
386
+ * 创建完成事件
387
+ */
388
+ declare function createDone(text: string, usage?: TokenUsage, duration?: number): DoneEvent;
389
+ /**
390
+ * 创建错误事件(完整版)
391
+ */
392
+ declare function createError(details: ErrorDetails): ErrorEvent;
393
+ /**
394
+ * 创建 API 错误事件
395
+ */
396
+ declare function createApiError(message: string, options?: {
397
+ code?: string;
398
+ statusCode?: number;
399
+ statusText?: string;
400
+ retryable?: boolean;
401
+ retryAfter?: number;
402
+ cause?: string;
403
+ context?: string;
404
+ }): ErrorEvent;
405
+ /**
406
+ * 创建速率限制错误事件
407
+ */
408
+ declare function createRateLimitError(message: string, retryAfter?: number, context?: string): ErrorEvent;
409
+ /**
410
+ * 创建工具错误事件
411
+ */
412
+ declare function createToolError(message: string, toolName: string, cause?: string): ErrorEvent;
413
+ /**
414
+ * 创建超时错误事件
415
+ */
416
+ declare function createTimeoutError(message: string, context?: string): ErrorEvent;
417
+ /**
418
+ * 创建解析错误事件
419
+ */
420
+ declare function createParseError(message: string, cause?: string): ErrorEvent;
421
+ /**
422
+ * 创建中止事件
423
+ */
424
+ declare function createAbort(reason?: string): AbortEvent;
425
+ /**
426
+ * 创建步骤开始事件
427
+ */
428
+ declare function createStepStart(stepNumber: number, description?: string): StepStartEvent;
429
+ /**
430
+ * 创建步骤结束事件
431
+ */
432
+ declare function createStepEnd(stepNumber: number, startedAt: number): StepEndEvent;
433
+ /** 检查是否为思考事件 */
434
+ declare function isThinkingEvent(event: ChatEvent): event is ThinkingEvent;
435
+ /** 检查是否为搜索事件 */
436
+ declare function isSearchEvent(event: ChatEvent): event is SearchEvent;
437
+ /** 检查是否为工具事件 */
438
+ declare function isToolEvent(event: ChatEvent): event is ToolEvent;
439
+ /** 检查是否为文本事件 */
440
+ declare function isTextEvent(event: ChatEvent): event is TextEvent;
441
+ /** 检查是否为状态事件 */
442
+ declare function isStatusEvent(event: ChatEvent): event is StatusEvent;
443
+ /** 检查是否为错误事件 */
444
+ declare function isErrorEvent(event: ChatEvent): event is ErrorEvent;
445
+ /** 检查是否为中止事件 */
446
+ declare function isAbortEvent(event: ChatEvent): event is AbortEvent;
447
+ /** 检查是否为步骤事件 */
448
+ declare function isStepEvent(event: ChatEvent): event is StepEvent;
449
+ /** 检查错误是否可重试 */
450
+ declare function isRetryableError(event: ChatEvent): boolean;
451
+
452
+ export { type AbortEvent, type AgentPhase, type AgentStatusEvent, CHAT_EVENT_TYPES, type ChatEvent, type ChatEventType, type DoneEvent, type ErrorCategory, type ErrorDetails, type ErrorEvent, type PlanStep, type PlanStepStatus, type SearchEndEvent, type SearchEvent, type SearchResult, type SearchResultEvent, type SearchStartEvent, type SideEffect, type StatusEvent, type StepEndEvent, type StepEvent, type StepStartEvent, type TextDeltaEvent, type TextEvent, type ThinkingDeltaEvent, type ThinkingEndEvent, type ThinkingEvent, type ThinkingStartEvent, type TokenUsage, type ToolApprovalRequestEvent, type ToolCallInfo, type ToolCallOutputEvent, type ToolCallRequestEvent, type ToolCallResultEvent, type ToolCallStartEvent, type ToolCallStatus, type ToolEvent, createAbort, createAgentStatus, createApiError, createDone, createError, createParseError, createRateLimitError, createSearchEnd, createSearchResult, createSearchStart, createStepEnd, createStepStart, createTextDelta, createThinkingDelta, createThinkingEnd, createThinkingStart, createTimeoutError, createToolCallOutput, createToolCallRequest, createToolCallResult, createToolCallStart, createToolError, isAbortEvent, isErrorEvent, isRetryableError, isSearchEvent, isStatusEvent, isStepEvent, isTextEvent, isThinkingEvent, isToolEvent };
package/dist/events.js ADDED
@@ -0,0 +1 @@
1
+ function t(t){return{type:"agent_status",data:{phase:t}}}var e=["thinking_start","thinking_delta","thinking_end","search_start","search_result","search_end","tool_call_start","tool_call_result","tool_call_output","tool_approval_request","tool_call_request","text_delta","done","error","abort","step_start","step_end","agent_status"];function n(){return{type:"thinking_start",data:{startedAt:Date.now()}}}function r(t){return{type:"thinking_delta",data:{content:t}}}function a(t){const e=Date.now();return{type:"thinking_end",data:{endedAt:e,duration:e-t}}}function o(t){return{type:"search_start",data:{query:t,startedAt:Date.now()}}}function u(t,e){const n=Date.now();return{type:"search_result",data:{results:t,endedAt:n,duration:n-e}}}function s(t,e,n){const r=Date.now();return{type:"search_end",data:{success:t,error:n,endedAt:r,duration:r-e}}}function c(t,e,n){return{type:"tool_call_start",data:{id:t,name:e,args:n,startedAt:Date.now()}}}function i(t,e,n,r,a,o,u,s){const c=Date.now();return{type:"tool_call_result",data:{id:t,name:e,result:n,success:r,error:o,endedAt:c,duration:c-a,sideEffects:u,resultType:s}}}function d(t,e,n,r){return{type:"tool_call_output",data:{id:t,name:e,stream:n,chunk:r,at:Date.now()}}}function _(t,e,n){return{type:"tool_call_request",data:{id:t,name:e,args:n,requestedAt:Date.now()}}}function l(t){return{type:"text_delta",data:{content:t}}}function p(t,e,n){return{type:"done",data:{text:t,usage:e,duration:n}}}function y(t){return{type:"error",data:t}}function f(t,e={}){return y({category:"api",message:t,...e})}function g(t,e,n){return y({category:"rate_limit",message:t,code:"RATE_LIMIT",statusCode:429,retryable:!0,retryAfter:e,context:n})}function h(t,e,n){return y({category:"tool",message:t,code:"TOOL_ERROR",context:e,cause:n,retryable:!1})}function m(t,e){return y({category:"timeout",message:t,code:"TIMEOUT",retryable:!0,context:e})}function A(t,e){return y({category:"parse",message:t,code:"PARSE_ERROR",cause:e,retryable:!1})}function b(t){return{type:"abort",data:{reason:t,abortedAt:Date.now()}}}function w(t,e){return{type:"step_start",data:{stepNumber:t,description:e,startedAt:Date.now()}}}function D(t,e){const n=Date.now();return{type:"step_end",data:{stepNumber:t,endedAt:n,duration:n-e}}}function k(t){return t.type.startsWith("thinking_")}function x(t){return t.type.startsWith("search_")}function R(t){return t.type.startsWith("tool_")}function E(t){return"text_delta"===t.type}function T(t){return"done"===t.type||"error"===t.type||"abort"===t.type}function q(t){return"error"===t.type}function O(t){return"abort"===t.type}function W(t){return t.type.startsWith("step_")}function I(t){return"error"===t.type&&!0===t.data.retryable}export{e as CHAT_EVENT_TYPES,b as createAbort,t as createAgentStatus,f as createApiError,p as createDone,y as createError,A as createParseError,g as createRateLimitError,s as createSearchEnd,u as createSearchResult,o as createSearchStart,D as createStepEnd,w as createStepStart,l as createTextDelta,r as createThinkingDelta,a as createThinkingEnd,n as createThinkingStart,m as createTimeoutError,d as createToolCallOutput,_ as createToolCallRequest,i as createToolCallResult,c as createToolCallStart,h as createToolError,O as isAbortEvent,q as isErrorEvent,I as isRetryableError,x as isSearchEvent,T as isStatusEvent,W as isStepEvent,E as isTextEvent,k as isThinkingEvent,R as isToolEvent};