mirror of
https://github.com/davidkaya/aryx.git
synced 2026-07-28 07:28:42 +02:00
fix: stabilize streamed message rendering
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -609,18 +609,24 @@ export class EryxAppService extends EventEmitter<AppServiceEvents> {
|
||||
sessionId: string,
|
||||
event: TurnDeltaEvent,
|
||||
): Promise<void> {
|
||||
if (event.content === undefined && event.contentDelta === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const session = this.requireSession(workspace, sessionId);
|
||||
const existing = session.messages.find((message) => message.id === event.messageId);
|
||||
|
||||
if (existing) {
|
||||
existing.content = mergeStreamingText(existing.content, event.contentDelta);
|
||||
existing.content =
|
||||
event.content ?? mergeStreamingText(existing.content, event.contentDelta);
|
||||
existing.pending = true;
|
||||
existing.authorName = event.authorName;
|
||||
} else {
|
||||
session.messages.push({
|
||||
id: event.messageId,
|
||||
role: 'assistant',
|
||||
authorName: event.authorName,
|
||||
content: event.contentDelta,
|
||||
content: event.content ?? event.contentDelta,
|
||||
createdAt: nowIso(),
|
||||
pending: true,
|
||||
});
|
||||
@@ -636,6 +642,7 @@ export class EryxAppService extends EventEmitter<AppServiceEvents> {
|
||||
messageId: event.messageId,
|
||||
authorName: event.authorName,
|
||||
contentDelta: event.contentDelta,
|
||||
content: event.content,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -697,6 +704,7 @@ export class EryxAppService extends EventEmitter<AppServiceEvents> {
|
||||
occurredAt: nowIso(),
|
||||
messageId: message.id,
|
||||
authorName: message.authorName,
|
||||
content: message.content,
|
||||
});
|
||||
|
||||
this.emitCompletedActivity(sessionId, pattern, message);
|
||||
|
||||
@@ -418,7 +418,13 @@ export function ChatPane({
|
||||
: `rounded-xl border px-4 py-3 text-[14px] leading-relaxed text-zinc-200 ${assistantContainerClass}`
|
||||
}
|
||||
>
|
||||
<MarkdownContent content={message.content} />
|
||||
{!isUser && message.pending ? (
|
||||
<div className="whitespace-pre-wrap break-words text-[14px] leading-relaxed text-zinc-200">
|
||||
{message.content}
|
||||
</div>
|
||||
) : (
|
||||
<MarkdownContent content={message.content} />
|
||||
)}
|
||||
{message.pending && message.content && (
|
||||
<span className="mt-1 inline-block h-4 w-[2px] animate-pulse rounded-sm bg-zinc-400" />
|
||||
)}
|
||||
|
||||
@@ -77,17 +77,18 @@ function applyErrorEvent(session: SessionRecord, event: SessionEventRecord): Ses
|
||||
}
|
||||
|
||||
function applyMessageDeltaEvent(session: SessionRecord, event: SessionEventRecord): SessionRecord {
|
||||
if (!event.messageId || event.contentDelta === undefined) {
|
||||
if (!event.messageId || (event.content === undefined && event.contentDelta === undefined)) {
|
||||
return session;
|
||||
}
|
||||
|
||||
const resolvedContent = event.content ?? event.contentDelta ?? '';
|
||||
const messageIndex = session.messages.findIndex((message) => message.id === event.messageId);
|
||||
if (messageIndex >= 0) {
|
||||
const existing = session.messages[messageIndex];
|
||||
const nextMessage: ChatMessageRecord = {
|
||||
...existing,
|
||||
authorName: event.authorName ?? existing.authorName,
|
||||
content: mergeStreamingText(existing.content, event.contentDelta),
|
||||
content: event.content ?? mergeStreamingText(existing.content, resolvedContent),
|
||||
pending: true,
|
||||
};
|
||||
|
||||
@@ -112,14 +113,14 @@ function applyMessageDeltaEvent(session: SessionRecord, event: SessionEventRecor
|
||||
...session,
|
||||
messages: [
|
||||
...session.messages,
|
||||
{
|
||||
id: event.messageId,
|
||||
role: 'assistant',
|
||||
authorName: event.authorName ?? 'assistant',
|
||||
content: event.contentDelta,
|
||||
createdAt: event.occurredAt,
|
||||
pending: true,
|
||||
},
|
||||
{
|
||||
id: event.messageId,
|
||||
role: 'assistant',
|
||||
authorName: event.authorName ?? 'assistant',
|
||||
content: resolvedContent,
|
||||
createdAt: event.occurredAt,
|
||||
pending: true,
|
||||
},
|
||||
],
|
||||
updatedAt: event.occurredAt,
|
||||
};
|
||||
@@ -139,10 +140,15 @@ function applyMessageCompleteEvent(session: SessionRecord, event: SessionEventRe
|
||||
const nextMessage: ChatMessageRecord = {
|
||||
...existing,
|
||||
authorName: event.authorName ?? existing.authorName,
|
||||
content: event.content ?? existing.content,
|
||||
pending: false,
|
||||
};
|
||||
|
||||
if (nextMessage.authorName === existing.authorName && !existing.pending) {
|
||||
if (
|
||||
nextMessage.authorName === existing.authorName
|
||||
&& nextMessage.content === existing.content
|
||||
&& !existing.pending
|
||||
) {
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ export interface TurnDeltaEvent {
|
||||
messageId: string;
|
||||
authorName: string;
|
||||
contentDelta: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
export interface TurnCompleteEvent {
|
||||
|
||||
@@ -15,6 +15,7 @@ export interface SessionEventRecord {
|
||||
messageId?: string;
|
||||
authorName?: string;
|
||||
contentDelta?: string;
|
||||
content?: string;
|
||||
activityType?: SessionActivityType;
|
||||
agentId?: string;
|
||||
agentName?: string;
|
||||
|
||||
Reference in New Issue
Block a user