mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-03 06:00:49 +00:00
Fix SDK message event pairing and improve content block handling
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
eea92fc8db
commit
79083ffd50
8 changed files with 545 additions and 102 deletions
|
|
@ -36,6 +36,8 @@ export class StreamJsonOutputAdapter
|
|||
extends BaseJsonOutputAdapter
|
||||
implements JsonOutputAdapterInterface
|
||||
{
|
||||
private mainTurnMessageStartEmitted = false;
|
||||
|
||||
constructor(
|
||||
config: Config,
|
||||
private readonly includePartialMessages: boolean,
|
||||
|
|
@ -68,47 +70,27 @@ export class StreamJsonOutputAdapter
|
|||
return this.includePartialMessages;
|
||||
}
|
||||
|
||||
override startAssistantMessage(): void {
|
||||
this.mainTurnMessageStartEmitted = false;
|
||||
super.startAssistantMessage();
|
||||
}
|
||||
|
||||
finalizeAssistantMessage(): CLIAssistantMessage {
|
||||
return this.finalizeAssistantMessageInternal(
|
||||
const message = this.finalizeAssistantMessageInternal(
|
||||
this.mainAgentMessageState,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides base class to emit message_stop event when message is finalized.
|
||||
* This ensures message_start and message_stop are always paired.
|
||||
*/
|
||||
protected override finalizeAssistantMessageInternal(
|
||||
state: MessageState,
|
||||
parentToolUseId: string | null,
|
||||
): CLIAssistantMessage {
|
||||
if (state.finalized) {
|
||||
return this.buildMessage(parentToolUseId);
|
||||
if (this.mainTurnMessageStartEmitted && this.includePartialMessages) {
|
||||
const partial: CLIPartialAssistantMessage = {
|
||||
type: 'stream_event',
|
||||
uuid: randomUUID(),
|
||||
session_id: this.getSessionId(),
|
||||
parent_tool_use_id: null,
|
||||
event: { type: 'message_stop' },
|
||||
};
|
||||
this.emitMessageImpl(partial);
|
||||
}
|
||||
state.finalized = true;
|
||||
|
||||
this.finalizePendingBlocks(state, parentToolUseId);
|
||||
const orderedOpenBlocks = Array.from(state.openBlocks).sort(
|
||||
(a, b) => a - b,
|
||||
);
|
||||
for (const index of orderedOpenBlocks) {
|
||||
this.onBlockClosed(state, index, parentToolUseId);
|
||||
this.closeBlock(state, index);
|
||||
}
|
||||
|
||||
// Emit message_stop for main agent when message was started and partial messages are enabled
|
||||
if (
|
||||
state.messageStarted &&
|
||||
this.includePartialMessages &&
|
||||
parentToolUseId === null
|
||||
) {
|
||||
this.emitStreamEventIfEnabled({ type: 'message_stop' }, null);
|
||||
}
|
||||
|
||||
const message = this.buildMessage(parentToolUseId);
|
||||
this.updateLastAssistantMessage(message);
|
||||
this.emitMessageImpl(message);
|
||||
this.mainTurnMessageStartEmitted = false;
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
@ -267,14 +249,15 @@ export class StreamJsonOutputAdapter
|
|||
|
||||
/**
|
||||
* Overrides base class hook to emit message_start event when message is started.
|
||||
* Only emits for main agent, not for subagents.
|
||||
* Only emits once per turn for the main agent (guarded by mainTurnMessageStartEmitted),
|
||||
* so block-type transitions inside a single turn do not produce spurious message_start events.
|
||||
*/
|
||||
protected override onEnsureMessageStarted(
|
||||
state: MessageState,
|
||||
parentToolUseId: string | null,
|
||||
): void {
|
||||
// Only emit message_start for main agent, not for subagents
|
||||
if (parentToolUseId === null) {
|
||||
if (parentToolUseId === null && !this.mainTurnMessageStartEmitted) {
|
||||
this.mainTurnMessageStartEmitted = true;
|
||||
this.emitStreamEventIfEnabled(
|
||||
{
|
||||
type: 'message_start',
|
||||
|
|
@ -282,6 +265,7 @@ export class StreamJsonOutputAdapter
|
|||
id: state.messageId!,
|
||||
role: 'assistant',
|
||||
model: this.config.getModel(),
|
||||
content: [],
|
||||
},
|
||||
},
|
||||
null,
|
||||
|
|
@ -329,19 +313,12 @@ export class StreamJsonOutputAdapter
|
|||
return;
|
||||
}
|
||||
|
||||
const state = this.getMessageState(parentToolUseId);
|
||||
const enrichedEvent = state.messageStarted
|
||||
? ({ ...event, message_id: state.messageId } as StreamEvent & {
|
||||
message_id: string;
|
||||
})
|
||||
: event;
|
||||
|
||||
const partial: CLIPartialAssistantMessage = {
|
||||
type: 'stream_event',
|
||||
uuid: randomUUID(),
|
||||
session_id: this.getSessionId(),
|
||||
parent_tool_use_id: parentToolUseId,
|
||||
event: enrichedEvent,
|
||||
event,
|
||||
};
|
||||
this.emitMessageImpl(partial);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue