@codehz/ai 0.4.5 → 0.7.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.
Files changed (46) hide show
  1. package/README.md +223 -77
  2. package/dist/index.d.mts +664 -525
  3. package/dist/index.mjs +3632 -2205
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +19 -8
  6. package/.github/workflows/publish.yml +0 -56
  7. package/.oxfmtrc.json +0 -12
  8. package/.oxlintrc.json +0 -34
  9. package/AGENTS.md +0 -37
  10. package/src/adapters/chat-completions.ts +0 -624
  11. package/src/adapters/index.ts +0 -44
  12. package/src/adapters/messages.ts +0 -635
  13. package/src/adapters/mock.ts +0 -934
  14. package/src/adapters/ollama.ts +0 -526
  15. package/src/adapters/responses.ts +0 -818
  16. package/src/core/aggregator.ts +0 -428
  17. package/src/core/client.ts +0 -36
  18. package/src/core/collect-stream.ts +0 -19
  19. package/src/core/errors.ts +0 -105
  20. package/src/core/event-factory.ts +0 -151
  21. package/src/core/index.ts +0 -18
  22. package/src/core/merge-auxiliary.ts +0 -22
  23. package/src/core/normalize.ts +0 -65
  24. package/src/core/validation.ts +0 -404
  25. package/src/helpers/adapter-auxiliary.ts +0 -155
  26. package/src/helpers/adapter-base.ts +0 -218
  27. package/src/helpers/adapter-security.ts +0 -126
  28. package/src/helpers/auxiliary-collector.ts +0 -166
  29. package/src/helpers/incremental-stream-parser.ts +0 -142
  30. package/src/helpers/index.ts +0 -87
  31. package/src/helpers/mapping.ts +0 -192
  32. package/src/helpers/provider-request-options.ts +0 -25
  33. package/src/helpers/provider-stream.ts +0 -147
  34. package/src/helpers/reasoning-level.ts +0 -85
  35. package/src/helpers/request-mapper.ts +0 -94
  36. package/src/helpers/synthetic-stream.ts +0 -188
  37. package/src/helpers/usage-mapping.ts +0 -110
  38. package/src/index.ts +0 -17
  39. package/src/types/adapter.ts +0 -42
  40. package/src/types/content.ts +0 -15
  41. package/src/types/events.ts +0 -138
  42. package/src/types/index.ts +0 -49
  43. package/src/types/items.ts +0 -57
  44. package/src/types/request.ts +0 -52
  45. package/src/types/response.ts +0 -68
  46. package/tsdown.config.ts +0 -10
@@ -1,428 +0,0 @@
1
- /**
2
- * 流聚合器
3
- *
4
- * 将 AIStreamEvent 序列聚合为统一的 AIResponse。
5
- *
6
- * 职责:
7
- * - 严格 item 状态机:started 创建 active item,delta 累积,completed 构建 OutputItem
8
- * - 校验:未 started 的 delta/completed、ID 重用、类型错配、response.completed 时 active items 非空
9
- * - 响应级 sequence/responseId/started/completed 校验
10
- * - usage/billing/auxiliary 仅来自 response.auxiliary
11
- * - warning 仅来自 response.warning
12
- * - 最终 AIResponse 由聚合器唯一构建
13
- *
14
- * 约束:
15
- * - replay 由 adapter 显式提供,聚合器不猜测
16
- * - 不伪造 reasoning
17
- * - 不解释 opaque payload
18
- */
19
-
20
- import type {
21
- AIStreamEvent,
22
- AIResponse,
23
- MessageItem,
24
- ToolCallItem,
25
- OutputItem,
26
- Usage,
27
- BillingInfo,
28
- AuxiliaryInfo,
29
- BackendTrace,
30
- StopReason,
31
- ContentBlock,
32
- } from "../types/index.js";
33
- import { AIStreamError } from "./errors.js";
34
- import { mergeAuxiliary } from "./merge-auxiliary.js";
35
-
36
- // ── Active item types ─────────────────────────────────────────
37
-
38
- type ActiveMessage = {
39
- type: "message";
40
- id: string;
41
- role: "assistant";
42
- content: ContentBlock[];
43
- };
44
-
45
- type ActiveReasoning = {
46
- type: "reasoning";
47
- id: string;
48
- visibility: "full" | "summary" | "redacted" | "opaque";
49
- content: ContentBlock[];
50
- };
51
-
52
- type ActiveToolCall = {
53
- type: "tool_call";
54
- id: string;
55
- name: string;
56
- argumentsText: string;
57
- };
58
-
59
- type ActiveItem = ActiveMessage | ActiveReasoning | ActiveToolCall;
60
-
61
- // ── 聚合器状态 ────────────────────────────────────────────────
62
-
63
- export interface AggregatorState {
64
- responseId?: string;
65
- model?: string;
66
- backendInfo?: { kind: BackendTrace["adapter"]; isSynthetic: boolean };
67
-
68
- usage?: Usage;
69
- billing?: BillingInfo;
70
- auxiliary: AuxiliaryInfo;
71
- warnings: string[];
72
- warningSet: Set<string>;
73
- output: OutputItem[];
74
- textParts: string[];
75
- toolCalls: ToolCallItem[];
76
- lastEventType?: AIStreamEvent["type"];
77
- started: boolean;
78
- completed: boolean;
79
- nextSequence?: number;
80
- activeItems: Map<string, ActiveItem>;
81
- itemOrder: string[];
82
- completedItems: Map<string, OutputItem>;
83
-
84
- /** adapter 在 response.completed 中提供的 replay */
85
- replayFromAdapter?: import("../types/index.js").ReplayItem[];
86
- stopReasonFromAdapter?: StopReason;
87
- backendFromAdapter?: Partial<BackendTrace>;
88
- }
89
-
90
- export function createAggregatorState(): AggregatorState {
91
- return {
92
- auxiliary: {},
93
- warnings: [],
94
- warningSet: new Set(),
95
- output: [],
96
- textParts: [],
97
- toolCalls: [],
98
- started: false,
99
- completed: false,
100
- activeItems: new Map(),
101
- itemOrder: [],
102
- completedItems: new Map(),
103
- };
104
- }
105
-
106
- // ── Active item helpers ───────────────────────────────────────
107
-
108
- function getActiveItem(state: AggregatorState, itemId: string, expectedType: ActiveItem["type"]): ActiveItem {
109
- const item = state.activeItems.get(itemId);
110
- if (!item) {
111
- throw streamProtocolError(`Received ${expectedType} delta/completed for unknown item: ${itemId}`);
112
- }
113
- if (item.type !== expectedType) {
114
- throw streamProtocolError(`Item ${itemId} started as ${item.type} but received ${expectedType} event`);
115
- }
116
- return item;
117
- }
118
-
119
- function coalesceContentBlocks(blocks: readonly ContentBlock[]): ContentBlock[] {
120
- const result: ContentBlock[] = [];
121
- for (const block of blocks) {
122
- const previous = result[result.length - 1];
123
- if (block.type === "text" && previous?.type === "text") {
124
- previous.text += block.text;
125
- } else {
126
- result.push({ ...block });
127
- }
128
- }
129
- return result;
130
- }
131
-
132
- function finalizeMessage(active: ActiveMessage): MessageItem {
133
- return {
134
- type: "message",
135
- id: active.id,
136
- role: active.role,
137
- content: coalesceContentBlocks(active.content),
138
- };
139
- }
140
-
141
- function finalizeReasoning(active: ActiveReasoning): import("../types/index.js").ReasoningItem {
142
- return {
143
- type: "reasoning",
144
- id: active.id,
145
- visibility: active.visibility,
146
- content: coalesceContentBlocks(active.content),
147
- };
148
- }
149
-
150
- function finalizeToolCall(active: ActiveToolCall): ToolCallItem {
151
- return {
152
- type: "tool_call",
153
- id: active.id,
154
- name: active.name,
155
- argumentsText: active.argumentsText,
156
- };
157
- }
158
-
159
- // ── Event handlers ────────────────────────────────────────────
160
-
161
- function handleResponseStarted(state: AggregatorState, event: AIStreamEvent & { type: "response.started" }): void {
162
- if (state.started) {
163
- throw streamProtocolError("Stream must contain exactly one response.started event");
164
- }
165
- state.started = true;
166
- state.responseId = event.responseId;
167
- state.model = event.model;
168
- state.backendInfo = event.backend;
169
- }
170
-
171
- function handleResponseWarning(state: AggregatorState, event: AIStreamEvent & { type: "response.warning" }): void {
172
- pushWarnings(state, [event.message]);
173
- }
174
-
175
- function handleResponseAuxiliary(state: AggregatorState, event: AIStreamEvent & { type: "response.auxiliary" }): void {
176
- if (event.usage) {
177
- state.usage = { ...state.usage, ...event.usage };
178
- }
179
- if (event.billing) {
180
- state.billing = { ...state.billing, ...event.billing };
181
- }
182
- if (event.auxiliary) {
183
- state.auxiliary = mergeAuxiliary(state.auxiliary, event.auxiliary) ?? {};
184
- }
185
- }
186
-
187
- function handleMessageStarted(state: AggregatorState, event: AIStreamEvent & { type: "message.started" }): void {
188
- const id = event.item.id;
189
- if (state.activeItems.has(id)) {
190
- throw streamProtocolError(`Item with id ${id} is already active`);
191
- }
192
- state.activeItems.set(id, {
193
- type: "message",
194
- id,
195
- role: event.item.role,
196
- content: [],
197
- });
198
- state.itemOrder.push(id);
199
- }
200
-
201
- function handleMessageDelta(state: AggregatorState, event: AIStreamEvent & { type: "message.delta" }): void {
202
- const active = getActiveItem(state, event.itemId, "message") as ActiveMessage;
203
- active.content.push(event.delta);
204
- }
205
-
206
- function handleMessageCompleted(state: AggregatorState, event: AIStreamEvent & { type: "message.completed" }): void {
207
- const active = getActiveItem(state, event.itemId, "message") as ActiveMessage;
208
- state.activeItems.delete(event.itemId);
209
- const item = finalizeMessage(active);
210
- state.completedItems.set(event.itemId, item);
211
- pushMessageText(state, item);
212
- }
213
-
214
- function handleReasoningStarted(state: AggregatorState, event: AIStreamEvent & { type: "reasoning.started" }): void {
215
- const id = event.item.id;
216
- if (state.activeItems.has(id)) {
217
- throw streamProtocolError(`Item with id ${id} is already active`);
218
- }
219
- state.activeItems.set(id, {
220
- type: "reasoning",
221
- id,
222
- visibility: event.item.visibility,
223
- content: [],
224
- });
225
- state.itemOrder.push(id);
226
- }
227
-
228
- function handleReasoningDelta(state: AggregatorState, event: AIStreamEvent & { type: "reasoning.delta" }): void {
229
- const active = getActiveItem(state, event.itemId, "reasoning") as ActiveReasoning;
230
- active.content.push(event.delta);
231
- }
232
-
233
- function handleReasoningCompleted(
234
- state: AggregatorState,
235
- event: AIStreamEvent & { type: "reasoning.completed" },
236
- ): void {
237
- const active = getActiveItem(state, event.itemId, "reasoning") as ActiveReasoning;
238
- state.activeItems.delete(event.itemId);
239
- state.completedItems.set(event.itemId, finalizeReasoning(active));
240
- }
241
-
242
- function handleToolCallStarted(state: AggregatorState, event: AIStreamEvent & { type: "tool_call.started" }): void {
243
- const id = event.item.id;
244
- if (state.activeItems.has(id)) {
245
- throw streamProtocolError(`Item with id ${id} is already active`);
246
- }
247
- state.activeItems.set(id, {
248
- type: "tool_call",
249
- id,
250
- name: event.item.name,
251
- argumentsText: "",
252
- });
253
- state.itemOrder.push(id);
254
- }
255
-
256
- function handleToolCallDelta(state: AggregatorState, event: AIStreamEvent & { type: "tool_call.delta" }): void {
257
- const active = getActiveItem(state, event.itemId, "tool_call") as ActiveToolCall;
258
- if (event.delta.argumentsText) {
259
- active.argumentsText += event.delta.argumentsText;
260
- }
261
- }
262
-
263
- function handleToolCallCompleted(state: AggregatorState, event: AIStreamEvent & { type: "tool_call.completed" }): void {
264
- const active = getActiveItem(state, event.itemId, "tool_call") as ActiveToolCall;
265
- state.activeItems.delete(event.itemId);
266
- const item = finalizeToolCall(active);
267
- state.completedItems.set(event.itemId, item);
268
- state.toolCalls.push(item);
269
- }
270
-
271
- function handleResponseCompleted(state: AggregatorState, event: AIStreamEvent & { type: "response.completed" }): void {
272
- if (state.activeItems.size > 0) {
273
- throw streamProtocolError("response.completed received while active items still pending");
274
- }
275
- state.completed = true;
276
- state.replayFromAdapter = event.replay;
277
- state.stopReasonFromAdapter = event.stopReason;
278
- state.backendFromAdapter = event.trace;
279
- if (event.usage) state.usage = { ...state.usage, ...event.usage };
280
- if (event.billing) state.billing = { ...state.billing, ...event.billing };
281
- if (event.auxiliary) state.auxiliary = mergeAuxiliary(state.auxiliary, event.auxiliary) ?? {};
282
- if (event.warnings) pushWarnings(state, event.warnings);
283
- if (event.opaqueOutput) state.output.push(...event.opaqueOutput);
284
- }
285
-
286
- // ── 从聚合状态构建最终 AIResponse ─────────────────────────────
287
-
288
- function buildResponse(state: AggregatorState): AIResponse {
289
- const backendFromCompleted = state.backendFromAdapter;
290
- const backend: BackendTrace = {
291
- adapter: backendFromCompleted?.adapter ?? state.backendInfo?.kind ?? ("unknown" as BackendTrace["adapter"]),
292
- isSyntheticStream: backendFromCompleted?.isSyntheticStream ?? state.backendInfo?.isSynthetic ?? false,
293
- requestId: backendFromCompleted?.requestId ?? state.responseId,
294
- rawResponseId: backendFromCompleted?.rawResponseId,
295
- metadataSources: backendFromCompleted?.metadataSources,
296
- warnings: backendFromCompleted?.warnings,
297
- };
298
- const orderedOutput = state.itemOrder.map((id) => {
299
- const item = state.completedItems.get(id);
300
- if (!item) {
301
- throw streamProtocolError(`Item ${id} was started but not completed`);
302
- }
303
- return item;
304
- });
305
-
306
- return {
307
- id: state.responseId,
308
- output: [...orderedOutput, ...state.output],
309
- replay: state.replayFromAdapter ?? [],
310
- text: state.textParts.join(""),
311
- toolCalls: state.toolCalls,
312
- stopReason: state.stopReasonFromAdapter,
313
- usage: state.usage,
314
- billing: state.billing,
315
- auxiliary: state.auxiliary,
316
- warnings: state.warnings.length > 0 ? state.warnings : undefined,
317
- backend,
318
- };
319
- }
320
-
321
- // ── 公开 API ──────────────────────────────────────────────────
322
-
323
- /**
324
- * 将事件数组聚合为 AIResponse。
325
- * 适用于测试和离线处理场景。
326
- */
327
- export function aggregateEvents(events: readonly AIStreamEvent[]): AIResponse {
328
- const state = createAggregatorState();
329
- for (const event of events) {
330
- aggregateEvent(state, event);
331
- }
332
- return finalizeAggregation(state);
333
- }
334
-
335
- export function aggregateEvent(state: AggregatorState, event: AIStreamEvent): void {
336
- validateEventEnvelope(state, event);
337
- state.lastEventType = event.type;
338
-
339
- switch (event.type) {
340
- case "response.started":
341
- handleResponseStarted(state, event);
342
- break;
343
- case "response.warning":
344
- handleResponseWarning(state, event);
345
- break;
346
- case "response.auxiliary":
347
- handleResponseAuxiliary(state, event);
348
- break;
349
- case "message.started":
350
- handleMessageStarted(state, event);
351
- break;
352
- case "message.delta":
353
- handleMessageDelta(state, event);
354
- break;
355
- case "message.completed":
356
- handleMessageCompleted(state, event);
357
- break;
358
- case "reasoning.started":
359
- handleReasoningStarted(state, event);
360
- break;
361
- case "reasoning.delta":
362
- handleReasoningDelta(state, event);
363
- break;
364
- case "reasoning.completed":
365
- handleReasoningCompleted(state, event);
366
- break;
367
- case "tool_call.started":
368
- handleToolCallStarted(state, event);
369
- break;
370
- case "tool_call.delta":
371
- handleToolCallDelta(state, event);
372
- break;
373
- case "tool_call.completed":
374
- handleToolCallCompleted(state, event);
375
- break;
376
- case "response.completed":
377
- handleResponseCompleted(state, event);
378
- break;
379
- }
380
- }
381
-
382
- export function finalizeAggregation(state: AggregatorState): AIResponse {
383
- if (!state.started) {
384
- throw streamProtocolError("Stream must start with response.started event");
385
- }
386
- if (!state.completed || state.lastEventType !== "response.completed") {
387
- throw streamProtocolError("Stream must end with response.completed event to produce a valid AIResponse");
388
- }
389
-
390
- return buildResponse(state);
391
- }
392
-
393
- function pushWarnings(state: AggregatorState, warnings: readonly string[]): void {
394
- for (const warning of warnings) {
395
- if (!state.warningSet.has(warning)) {
396
- state.warningSet.add(warning);
397
- state.warnings.push(warning);
398
- }
399
- }
400
- }
401
-
402
- function pushMessageText(state: AggregatorState, item: MessageItem): void {
403
- for (const block of item.content) {
404
- if (block.type === "text") {
405
- state.textParts.push(block.text);
406
- }
407
- }
408
- }
409
-
410
- function validateEventEnvelope(state: AggregatorState, event: AIStreamEvent): void {
411
- if (state.completed) {
412
- throw streamProtocolError("response.completed must be the final stream event");
413
- }
414
- if (!state.started && event.type !== "response.started") {
415
- throw streamProtocolError("Stream must start with response.started event");
416
- }
417
- if (state.responseId !== undefined && event.responseId !== state.responseId) {
418
- throw streamProtocolError("All stream events must use the same responseId");
419
- }
420
- if (state.nextSequence !== undefined && event.sequence !== state.nextSequence) {
421
- throw streamProtocolError(`Expected event sequence ${state.nextSequence}, received ${event.sequence}`);
422
- }
423
- state.nextSequence = event.sequence + 1;
424
- }
425
-
426
- function streamProtocolError(message: string): AIStreamError {
427
- return new AIStreamError(message, "STREAM_PROTOCOL_ERROR");
428
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * AI 客户端入口
3
- *
4
- * 打通 createAIClient() 到 adapter 调用之间的公共入口。
5
- */
6
-
7
- import type { AIRequest, AIStreamEvent, AIClient, CreateAIClientOptions } from "../types/index.js";
8
- import { normalizeRequest } from "./normalize.js";
9
-
10
- export function createAIClient(options: CreateAIClientOptions): AIClient {
11
- const { adapter, model, defaults, signal: defaultSignal } = options;
12
-
13
- const client: AIClient = {
14
- stream(request: AIRequest): AsyncIterable<AIStreamEvent> {
15
- // 合并 client 级别的默认 signal 和请求级别的 signal
16
- const signal = mergeAbortSignals(defaultSignal, request.signal);
17
- const normalized = normalizeRequest({ ...request, signal }, { model, defaults });
18
- return adapter.stream(normalized);
19
- },
20
- };
21
-
22
- return client;
23
- }
24
-
25
- /**
26
- * 合并多个 AbortSignal:任一 signal abort 即触发。
27
- * 如果没有 signal 需要合并则返回 undefined。
28
- */
29
- function mergeAbortSignals(...signals: (AbortSignal | undefined)[]): AbortSignal | undefined {
30
- const valid = signals.filter((s): s is AbortSignal => s != null);
31
- if (valid.length === 0) return undefined;
32
- if (valid.length === 1) return valid[0];
33
- return AbortSignal.any(valid);
34
- }
35
-
36
- export type { AIClient, CreateAIClientOptions } from "../types/index.js";
@@ -1,19 +0,0 @@
1
- /**
2
- * collectStream — 流收集 helper
3
- *
4
- * 将 AsyncIterable<AIStreamEvent> 消费完毕并聚合力 AIResponse。
5
- * 适用于不需要逐事件处理的调用方。
6
- */
7
-
8
- import type { AIStreamEvent, AIResponse } from "../types/index.js";
9
- import { aggregateEvent, createAggregatorState, finalizeAggregation } from "./aggregator.js";
10
-
11
- export async function collectStream(stream: AsyncIterable<AIStreamEvent>): Promise<AIResponse> {
12
- const state = createAggregatorState();
13
-
14
- for await (const event of stream) {
15
- aggregateEvent(state, event);
16
- }
17
-
18
- return finalizeAggregation(state);
19
- }
@@ -1,105 +0,0 @@
1
- /**
2
- * 公共错误模型
3
- *
4
- * 把失败、降级、断流三类情况明确区分:
5
- * - 致命错误 → 同步抛错或迭代器抛错
6
- * - 非致命差异 → warning 通道
7
- * - 流中断 → 不伪造 response.completed
8
- */
9
-
10
- // ── 错误类型 ──────────────────────────────────────────────────
11
-
12
- export type ErrorCode =
13
- | "INPUT_EMPTY"
14
- | "TEMPERATURE_OUT_OF_RANGE"
15
- | "MAX_OUTPUT_TOKENS_INVALID"
16
- | "TOOL_CHOICE_NO_TOOLS"
17
- | "TOOL_CHOICE_UNKNOWN_TOOL"
18
- | "PROVIDER_ERROR"
19
- | "AUTH_ERROR"
20
- | "STREAM_ERROR"
21
- | "MAPPING_ERROR"
22
- | "STREAM_INCOMPLETE"
23
- | "LOOKUP_FAILED"
24
- | "LOOKUP_TIMEOUT"
25
- | string;
26
-
27
- export class AIError extends Error {
28
- override readonly name: string;
29
-
30
- constructor(
31
- message: string,
32
- public readonly code: ErrorCode,
33
- name?: string,
34
- ) {
35
- super(message);
36
- this.name = name ?? "AIError";
37
- Object.setPrototypeOf(this, new.target.prototype);
38
- }
39
- }
40
-
41
- /** 请求构造失败 — 参数校验不通过。在进入 adapter 前同步抛错。 */
42
- export class AIRequestError extends AIError {
43
- constructor(
44
- message: string,
45
- code: ErrorCode,
46
- public readonly issues?: readonly { field: string; code: string; message: string }[],
47
- ) {
48
- super(message, code, "AIRequestError");
49
- }
50
- }
51
-
52
- /** Provider 调用失败 — HTTP 非 2xx、网络错误。由 AdapterBase 捕获转为 warning。 */
53
- export class AIProviderError extends AIError {
54
- constructor(
55
- message: string,
56
- code: ErrorCode,
57
- public readonly statusCode?: number,
58
- public readonly responseBody?: string,
59
- ) {
60
- super(message, code, "AIProviderError");
61
- }
62
- }
63
-
64
- /** 流协议损坏 — SSE 解析失败、chunk 格式异常。 */
65
- export class AIStreamError extends AIError {
66
- constructor(message: string, code: ErrorCode) {
67
- super(message, code, "AIStreamError");
68
- }
69
- }
70
-
71
- /** Canonical 映射失败 — 无法将 provider 响应映射到 canonical 类型。 */
72
- export class AIMappingError extends AIError {
73
- constructor(message: string, code: ErrorCode) {
74
- super(message, code, "AIMappingError");
75
- }
76
- }
77
-
78
- // ── Warning 辅助 ──────────────────────────────────────────────
79
-
80
- /**
81
- * 标准 warning 代码列表。
82
- * 用于非致命差异的记录。
83
- */
84
- export const WarningCode = {
85
- /** replay fidelity 低于预期 */
86
- REPLAY_FIDELITY_LOW: "REPLAY_FIDELITY_LOW",
87
- /** usage 字段缺失 */
88
- USAGE_MISSING: "USAGE_MISSING",
89
- /** billing 字段缺失 */
90
- BILLING_MISSING: "BILLING_MISSING",
91
- /** billing 只能给估算值 */
92
- BILLING_ESTIMATED: "BILLING_ESTIMATED",
93
- /** follow-up lookup 失败 */
94
- LOOKUP_FAILED: "LOOKUP_FAILED",
95
- /** lookup 超时 */
96
- LOOKUP_TIMEOUT: "LOOKUP_TIMEOUT",
97
- /** 流提前中断 */
98
- STREAM_INCOMPLETE: "STREAM_INCOMPLETE",
99
- /** 能力降级 */
100
- CAPABILITY_DOWNGRADE: "CAPABILITY_DOWNGRADE",
101
- /** 模拟流式 */
102
- SYNTHETIC_STREAM: "SYNTHETIC_STREAM",
103
- /** 工具调用以批量方式到达(非 token 级流式) */
104
- TOOL_CALL_BATCHED: "TOOL_CALL_BATCHED",
105
- } as const;