fix(server): sse abort behavior (#13153)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved handling of aborted client connections during streaming,
ensuring that session messages accurately reflect if a request was
aborted.
* Enhanced consistency and reliability across all streaming endpoints
when saving session messages after streaming.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2025-07-11 12:46:55 +08:00
committed by GitHub
parent 2dacba9011
commit b79439b01d

View File

@@ -299,6 +299,13 @@ export class CopilotController implements BeforeApplicationShutdown {
this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1); this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1);
const { signal, onConnectionClosed } = getSignal(req); const { signal, onConnectionClosed } = getSignal(req);
let endBeforePromiseResolve = false;
onConnectionClosed(isAborted => {
if (isAborted) {
endBeforePromiseResolve = true;
}
});
const { messageId, reasoning, webSearch } = ChatQuerySchema.parse(query); const { messageId, reasoning, webSearch } = ChatQuerySchema.parse(query);
const source$ = from( const source$ = from(
@@ -322,10 +329,11 @@ export class CopilotController implements BeforeApplicationShutdown {
shared$.pipe( shared$.pipe(
reduce((acc, chunk) => acc + chunk, ''), reduce((acc, chunk) => acc + chunk, ''),
tap(buffer => { tap(buffer => {
onConnectionClosed(isAborted => {
session.push({ session.push({
role: 'assistant', role: 'assistant',
content: isAborted ? '> Request aborted' : buffer, content: endBeforePromiseResolve
? '> Request aborted'
: buffer,
createdAt: new Date(), createdAt: new Date(),
}); });
void session void session
@@ -336,7 +344,6 @@ export class CopilotController implements BeforeApplicationShutdown {
err err
) )
); );
});
}), }),
ignoreElements() ignoreElements()
) )
@@ -384,6 +391,13 @@ export class CopilotController implements BeforeApplicationShutdown {
this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1); this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1);
const { signal, onConnectionClosed } = getSignal(req); const { signal, onConnectionClosed } = getSignal(req);
let endBeforePromiseResolve = false;
onConnectionClosed(isAborted => {
if (isAborted) {
endBeforePromiseResolve = true;
}
});
const { messageId, reasoning, webSearch } = ChatQuerySchema.parse(query); const { messageId, reasoning, webSearch } = ChatQuerySchema.parse(query);
const source$ = from( const source$ = from(
@@ -407,14 +421,15 @@ export class CopilotController implements BeforeApplicationShutdown {
shared$.pipe( shared$.pipe(
reduce((acc, chunk) => acc.concat([chunk]), [] as StreamObject[]), reduce((acc, chunk) => acc.concat([chunk]), [] as StreamObject[]),
tap(result => { tap(result => {
onConnectionClosed(isAborted => {
const parser = new StreamObjectParser(); const parser = new StreamObjectParser();
const streamObjects = parser.mergeTextDelta(result); const streamObjects = parser.mergeTextDelta(result);
const content = parser.mergeContent(streamObjects); const content = parser.mergeContent(streamObjects);
session.push({ session.push({
role: 'assistant', role: 'assistant',
content: isAborted ? '> Request aborted' : content, content: endBeforePromiseResolve
streamObjects: isAborted ? null : streamObjects, ? '> Request aborted'
: content,
streamObjects: endBeforePromiseResolve ? null : streamObjects,
createdAt: new Date(), createdAt: new Date(),
}); });
void session void session
@@ -425,7 +440,6 @@ export class CopilotController implements BeforeApplicationShutdown {
err err
) )
); );
});
}), }),
ignoreElements() ignoreElements()
) )
@@ -477,6 +491,13 @@ export class CopilotController implements BeforeApplicationShutdown {
this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1); this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1);
const { signal, onConnectionClosed } = getSignal(req); const { signal, onConnectionClosed } = getSignal(req);
let endBeforePromiseResolve = false;
onConnectionClosed(isAborted => {
if (isAborted) {
endBeforePromiseResolve = true;
}
});
const source$ = from( const source$ = from(
this.workflow.runGraph(params, session.model, { this.workflow.runGraph(params, session.model, {
...session.config.promptConfig, ...session.config.promptConfig,
@@ -526,10 +547,11 @@ export class CopilotController implements BeforeApplicationShutdown {
return acc; return acc;
}, ''), }, ''),
tap(content => { tap(content => {
onConnectionClosed(isAborted => {
session.push({ session.push({
role: 'assistant', role: 'assistant',
content: isAborted ? '> Request aborted' : content, content: endBeforePromiseResolve
? '> Request aborted'
: content,
createdAt: new Date(), createdAt: new Date(),
}); });
void session void session
@@ -540,7 +562,6 @@ export class CopilotController implements BeforeApplicationShutdown {
err err
) )
); );
});
}), }),
ignoreElements() ignoreElements()
) )
@@ -604,6 +625,13 @@ export class CopilotController implements BeforeApplicationShutdown {
this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1); this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1);
const { signal, onConnectionClosed } = getSignal(req); const { signal, onConnectionClosed } = getSignal(req);
let endBeforePromiseResolve = false;
onConnectionClosed(isAborted => {
if (isAborted) {
endBeforePromiseResolve = true;
}
});
const source$ = from( const source$ = from(
provider.streamImages( provider.streamImages(
{ {
@@ -639,11 +667,10 @@ export class CopilotController implements BeforeApplicationShutdown {
shared$.pipe( shared$.pipe(
reduce((acc, chunk) => acc.concat([chunk]), [] as string[]), reduce((acc, chunk) => acc.concat([chunk]), [] as string[]),
tap(attachments => { tap(attachments => {
onConnectionClosed(isAborted => {
session.push({ session.push({
role: 'assistant', role: 'assistant',
content: isAborted ? '> Request aborted' : '', content: endBeforePromiseResolve ? '> Request aborted' : '',
attachments: isAborted ? [] : attachments, attachments: endBeforePromiseResolve ? [] : attachments,
createdAt: new Date(), createdAt: new Date(),
}); });
void session void session
@@ -654,7 +681,6 @@ export class CopilotController implements BeforeApplicationShutdown {
err err
) )
); );
});
}), }),
ignoreElements() ignoreElements()
) )