Skip to content

Commit b3705f8

Browse files
committed
fix: prevent steering during final response streaming
1 parent 62e86a3 commit b3705f8

3 files changed

Lines changed: 25 additions & 3 deletions

File tree

custom/SteerQueue.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@
2828
</span>
2929
<button
3030
type="button"
31-
class="shrink-0 rounded-md px-2 py-1 text-xs font-medium text-lightPrimary transition-colors hover:bg-lightPrimary/10 dark:text-darkPrimary dark:hover:bg-darkPrimary/10"
32-
:title="$t('Steer into the current response')"
31+
class="shrink-0 rounded-md px-2 py-1 text-xs font-medium text-lightPrimary transition-colors hover:bg-lightPrimary/10 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent dark:text-darkPrimary dark:hover:bg-darkPrimary/10 dark:disabled:hover:bg-transparent"
32+
:disabled="agentStore.isFinalResponseStreaming"
33+
:title="agentStore.isFinalResponseStreaming
34+
? $t('The final response is streaming; this message will be sent after it finishes')
35+
: $t('Steer into the current response')"
3336
@click="agentStore.steerQueuedMessage(item.id)"
3437
>
3538
{{ $t('Steer') }}

custom/composables/agentStore/useAgentSteerQueue.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ export type QueuedMessage = {
1313
type CreateAgentSteerQueueOptions = {
1414
activeSessionId: Ref<string | null>;
1515
currentChat: ShallowRef<Chat<any> | null | undefined>;
16+
isFinalResponseStreaming: Ref<boolean>;
1617
sendMessage: (text: string) => void | Promise<void>;
1718
};
1819

1920
export function createAgentSteerQueue({
2021
activeSessionId,
2122
currentChat,
23+
isFinalResponseStreaming,
2224
sendMessage,
2325
}: CreateAgentSteerQueueOptions) {
2426
const queue = ref<QueuedMessage[]>([]);
@@ -146,7 +148,10 @@ export function createAgentSteerQueue({
146148
async function steerQueuedMessage(id: string) {
147149
const item = queue.value.find((candidate: QueuedMessage) => candidate.id === id);
148150
const sessionId = activeSessionId.value;
149-
if (!item || !sessionId || sessionId === PRE_SESSION_ID) {
151+
// A steer is only consumed before a subsequent model call. Once the assistant is
152+
// streaming its final text there is no such call left, so retain this item for the
153+
// normal FIFO follow-up instead of silently losing the instruction server-side.
154+
if (!item || !sessionId || sessionId === PRE_SESSION_ID || isFinalResponseStreaming.value) {
150155
return;
151156
}
152157
removeQueuedMessage(id);

custom/composables/useAgentStore.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,22 @@ export const useAgentStore = defineStore('agent', () => {
238238
return status === 'submitted' || status === 'streaming' || hasPendingToolApproval.value;
239239
});
240240

241+
// During an assistant text stream, a newly submitted steer cannot be consumed: the
242+
// steer middleware only runs before another model call. Keep it in the queue so it
243+
// becomes a normal follow-up when the current turn ends.
244+
const isFinalResponseStreaming = computed(() => {
245+
const chat = currentChat.value as any;
246+
const lastMessage = chat?.lastMessage;
247+
248+
return chat?.status === 'streaming'
249+
&& lastMessage?.role === 'assistant'
250+
&& lastMessage.parts?.some((part: any) => part.type === 'text' && part.state === 'streaming');
251+
});
252+
241253
const steerBuffer = createAgentSteerQueue({
242254
activeSessionId,
243255
currentChat,
256+
isFinalResponseStreaming,
244257
sendMessage,
245258
});
246259

@@ -646,6 +659,7 @@ export const useAgentStore = defineStore('agent', () => {
646659
//_________-Steer queue-_____________
647660
submitUserMessage,
648661
isTurnActive,
662+
isFinalResponseStreaming,
649663
steerQueue: steerBuffer.queue,
650664
steerQueuedMessage: steerBuffer.steerQueuedMessage,
651665
removeQueuedMessage: steerBuffer.removeQueuedMessage,

0 commit comments

Comments
 (0)