@codehz/ai 0.2.1 → 0.2.3
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 +4 -0
- package/dist/index.mjs +27 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/chat-completions.ts +1 -9
- package/src/core/aggregator.ts +33 -6
package/package.json
CHANGED
|
@@ -458,9 +458,7 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
458
458
|
if (hasMessageStarted) {
|
|
459
459
|
const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
|
|
460
460
|
events.push(factory.messageCompleted(currentMessageId));
|
|
461
|
-
|
|
462
|
-
output.push(message);
|
|
463
|
-
}
|
|
461
|
+
output.push(message);
|
|
464
462
|
}
|
|
465
463
|
|
|
466
464
|
for (const pending of finalizedToolCalls) {
|
|
@@ -599,12 +597,6 @@ export class ChatCompletionsAdapter extends AdapterBase {
|
|
|
599
597
|
accumulatedContent = "";
|
|
600
598
|
};
|
|
601
599
|
|
|
602
|
-
// 处理 role: assistant(首块标识;不要求 content)
|
|
603
|
-
if (delta.role === "assistant" && !hasMessageStarted) {
|
|
604
|
-
ensureMessageStarted();
|
|
605
|
-
yield factory.messageStarted(currentMessageId);
|
|
606
|
-
}
|
|
607
|
-
|
|
608
600
|
// 处理 third-party reasoning delta
|
|
609
601
|
if (reasoningDeltas.length > 0) {
|
|
610
602
|
if (!hasReasoningStarted) {
|
package/src/core/aggregator.ts
CHANGED
|
@@ -78,6 +78,8 @@ export interface AggregatorState {
|
|
|
78
78
|
completed: boolean;
|
|
79
79
|
nextSequence?: number;
|
|
80
80
|
activeItems: Map<string, ActiveItem>;
|
|
81
|
+
itemOrder: string[];
|
|
82
|
+
completedItems: Map<string, OutputItem>;
|
|
81
83
|
|
|
82
84
|
/** adapter 在 response.completed 中提供的 replay */
|
|
83
85
|
replayFromAdapter?: import("../types/index.js").ReplayItem[];
|
|
@@ -96,6 +98,8 @@ export function createAggregatorState(): AggregatorState {
|
|
|
96
98
|
started: false,
|
|
97
99
|
completed: false,
|
|
98
100
|
activeItems: new Map(),
|
|
101
|
+
itemOrder: [],
|
|
102
|
+
completedItems: new Map(),
|
|
99
103
|
};
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -112,12 +116,25 @@ function getActiveItem(state: AggregatorState, itemId: string, expectedType: Act
|
|
|
112
116
|
return item;
|
|
113
117
|
}
|
|
114
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
|
+
|
|
115
132
|
function finalizeMessage(active: ActiveMessage): MessageItem {
|
|
116
133
|
return {
|
|
117
134
|
type: "message",
|
|
118
135
|
id: active.id,
|
|
119
136
|
role: active.role,
|
|
120
|
-
content: active.content,
|
|
137
|
+
content: coalesceContentBlocks(active.content),
|
|
121
138
|
};
|
|
122
139
|
}
|
|
123
140
|
|
|
@@ -126,7 +143,7 @@ function finalizeReasoning(active: ActiveReasoning): import("../types/index.js")
|
|
|
126
143
|
type: "reasoning",
|
|
127
144
|
id: active.id,
|
|
128
145
|
visibility: active.visibility,
|
|
129
|
-
content: active.content,
|
|
146
|
+
content: coalesceContentBlocks(active.content),
|
|
130
147
|
};
|
|
131
148
|
}
|
|
132
149
|
|
|
@@ -178,6 +195,7 @@ function handleMessageStarted(state: AggregatorState, event: AIStreamEvent & { t
|
|
|
178
195
|
role: event.item.role,
|
|
179
196
|
content: [],
|
|
180
197
|
});
|
|
198
|
+
state.itemOrder.push(id);
|
|
181
199
|
}
|
|
182
200
|
|
|
183
201
|
function handleMessageDelta(state: AggregatorState, event: AIStreamEvent & { type: "message.delta" }): void {
|
|
@@ -189,7 +207,7 @@ function handleMessageCompleted(state: AggregatorState, event: AIStreamEvent & {
|
|
|
189
207
|
const active = getActiveItem(state, event.itemId, "message") as ActiveMessage;
|
|
190
208
|
state.activeItems.delete(event.itemId);
|
|
191
209
|
const item = finalizeMessage(active);
|
|
192
|
-
state.
|
|
210
|
+
state.completedItems.set(event.itemId, item);
|
|
193
211
|
pushMessageText(state, item);
|
|
194
212
|
}
|
|
195
213
|
|
|
@@ -204,6 +222,7 @@ function handleReasoningStarted(state: AggregatorState, event: AIStreamEvent & {
|
|
|
204
222
|
visibility: event.item.visibility,
|
|
205
223
|
content: [],
|
|
206
224
|
});
|
|
225
|
+
state.itemOrder.push(id);
|
|
207
226
|
}
|
|
208
227
|
|
|
209
228
|
function handleReasoningDelta(state: AggregatorState, event: AIStreamEvent & { type: "reasoning.delta" }): void {
|
|
@@ -217,7 +236,7 @@ function handleReasoningCompleted(
|
|
|
217
236
|
): void {
|
|
218
237
|
const active = getActiveItem(state, event.itemId, "reasoning") as ActiveReasoning;
|
|
219
238
|
state.activeItems.delete(event.itemId);
|
|
220
|
-
state.
|
|
239
|
+
state.completedItems.set(event.itemId, finalizeReasoning(active));
|
|
221
240
|
}
|
|
222
241
|
|
|
223
242
|
function handleToolCallStarted(state: AggregatorState, event: AIStreamEvent & { type: "tool_call.started" }): void {
|
|
@@ -231,6 +250,7 @@ function handleToolCallStarted(state: AggregatorState, event: AIStreamEvent & {
|
|
|
231
250
|
name: event.item.name,
|
|
232
251
|
argumentsText: "",
|
|
233
252
|
});
|
|
253
|
+
state.itemOrder.push(id);
|
|
234
254
|
}
|
|
235
255
|
|
|
236
256
|
function handleToolCallDelta(state: AggregatorState, event: AIStreamEvent & { type: "tool_call.delta" }): void {
|
|
@@ -244,7 +264,7 @@ function handleToolCallCompleted(state: AggregatorState, event: AIStreamEvent &
|
|
|
244
264
|
const active = getActiveItem(state, event.itemId, "tool_call") as ActiveToolCall;
|
|
245
265
|
state.activeItems.delete(event.itemId);
|
|
246
266
|
const item = finalizeToolCall(active);
|
|
247
|
-
state.
|
|
267
|
+
state.completedItems.set(event.itemId, item);
|
|
248
268
|
state.toolCalls.push(item);
|
|
249
269
|
}
|
|
250
270
|
|
|
@@ -275,10 +295,17 @@ function buildResponse(state: AggregatorState): AIResponse {
|
|
|
275
295
|
metadataSources: backendFromCompleted?.metadataSources,
|
|
276
296
|
warnings: backendFromCompleted?.warnings,
|
|
277
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
|
+
});
|
|
278
305
|
|
|
279
306
|
return {
|
|
280
307
|
id: state.responseId,
|
|
281
|
-
output: state.output,
|
|
308
|
+
output: [...orderedOutput, ...state.output],
|
|
282
309
|
replay: state.replayFromAdapter ?? [],
|
|
283
310
|
text: state.textParts.join(""),
|
|
284
311
|
toolCalls: state.toolCalls,
|