authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-26 20:27:56-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 20:43:22-08:00
log9d5c5156805a24c01de1bc7147c80b9bcd43827f
tree2102c4de13d0da6fa2719a10bc6bd734d5c58637
parent0e9677965cca02d925244e7814a245a194bd42de

Always flush buffered assistant text as final delta

- Remove spilled-state cache tracking for buffered assistant messages - Emit any remaining buffered text as a finalize delta on completion - Keep assistant completion payload text empty in buffered mode

1 files changed, 7 insertions(+), 35 deletions(-)

apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts+7-35
...@@ -28,8 +28,6 @@ const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 10_000;...@@ -28,8 +28,6 @@ const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 10_000;
28const TURN_MESSAGE_IDS_BY_TURN_TTL = Duration.minutes(120);28const TURN_MESSAGE_IDS_BY_TURN_TTL = Duration.minutes(120);
29const BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_CACHE_CAPACITY = 20_000;29const BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_CACHE_CAPACITY = 20_000;
30const BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL = Duration.minutes(120);30const BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL = Duration.minutes(120);
31const BUFFERED_ASSISTANT_MESSAGE_SPILLED_CACHE_CAPACITY = 20_000;
32const BUFFERED_ASSISTANT_MESSAGE_SPILLED_TTL = Duration.minutes(120);
33const MAX_BUFFERED_ASSISTANT_CHARS = 24_000;31const MAX_BUFFERED_ASSISTANT_CHARS = 24_000;
3432
35type TurnStartRequestedDomainEvent = Extract<33type TurnStartRequestedDomainEvent = Extract<
...@@ -172,11 +170,6 @@ const make = Effect.gen(function* () {...@@ -172,11 +170,6 @@ const make = Effect.gen(function* () {
172 timeToLive: BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL,170 timeToLive: BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL,
173 lookup: () => Effect.succeed(""),171 lookup: () => Effect.succeed(""),
174 });172 });
175 const bufferedAssistantMessageSpilledByMessageId = yield* Cache.make<MessageId, true>({
176 capacity: BUFFERED_ASSISTANT_MESSAGE_SPILLED_CACHE_CAPACITY,
177 timeToLive: BUFFERED_ASSISTANT_MESSAGE_SPILLED_TTL,
178 lookup: () => Effect.succeed(true),
179 });
180173
181 const rememberAssistantMessageId = (174 const rememberAssistantMessageId = (
182 sessionId: ProviderSessionId,175 sessionId: ProviderSessionId,
...@@ -263,26 +256,7 @@ const make = Effect.gen(function* () {...@@ -263,26 +256,7 @@ const make = Effect.gen(function* () {
263 const clearBufferedAssistantText = (messageId: MessageId) =>256 const clearBufferedAssistantText = (messageId: MessageId) =>
264 Cache.invalidate(bufferedAssistantTextByMessageId, messageId);257 Cache.invalidate(bufferedAssistantTextByMessageId, messageId);
265258
266 const markBufferedAssistantMessageSpilled = (messageId: MessageId) =>259 const clearAssistantMessageState = (messageId: MessageId) => clearBufferedAssistantText(messageId);
267 Cache.set(bufferedAssistantMessageSpilledByMessageId, messageId, true);
268
269 const takeBufferedAssistantMessageSpilled = (messageId: MessageId) =>
270 Cache.getOption(bufferedAssistantMessageSpilledByMessageId, messageId).pipe(
271 Effect.flatMap((spilled) =>
272 Cache.invalidate(bufferedAssistantMessageSpilledByMessageId, messageId).pipe(
273 Effect.as(Option.isSome(spilled)),
274 ),
275 ),
276 );
277
278 const clearBufferedAssistantMessageSpilled = (messageId: MessageId) =>
279 Cache.invalidate(bufferedAssistantMessageSpilledByMessageId, messageId);
280
281 const clearAssistantMessageState = (messageId: MessageId) =>
282 Effect.all(
283 [clearBufferedAssistantText(messageId), clearBufferedAssistantMessageSpilled(messageId)],
284 { concurrency: 1 },
285 ).pipe(Effect.asVoid);
286260
287 const finalizeAssistantMessage = (input: {261 const finalizeAssistantMessage = (input: {
288 event: ProviderRuntimeEvent;262 event: ProviderRuntimeEvent;
...@@ -291,16 +265,15 @@ const make = Effect.gen(function* () {...@@ -291,16 +265,15 @@ const make = Effect.gen(function* () {
291 turnId?: TurnId;265 turnId?: TurnId;
292 createdAt: string;266 createdAt: string;
293 commandTag: string;267 commandTag: string;
294 spillRemainderCommandTag: string;268 finalDeltaCommandTag: string;
295 }) =>269 }) =>
296 Effect.gen(function* () {270 Effect.gen(function* () {
297 const text = yield* takeBufferedAssistantText(input.messageId);271 const text = yield* takeBufferedAssistantText(input.messageId);
298 const spilled = yield* takeBufferedAssistantMessageSpilled(input.messageId);
299272
300 if (spilled && text.length > 0) {273 if (text.length > 0) {
301 yield* orchestrationEngine.dispatch({274 yield* orchestrationEngine.dispatch({
302 type: "thread.message.assistant.delta",275 type: "thread.message.assistant.delta",
303 commandId: providerCommandId(input.event, input.spillRemainderCommandTag),276 commandId: providerCommandId(input.event, input.finalDeltaCommandTag),
304 threadId: input.threadId,277 threadId: input.threadId,
305 messageId: input.messageId,278 messageId: input.messageId,
306 delta: text,279 delta: text,
...@@ -315,7 +288,7 @@ const make = Effect.gen(function* () {...@@ -315,7 +288,7 @@ const make = Effect.gen(function* () {
315 threadId: input.threadId,288 threadId: input.threadId,
316 messageId: input.messageId,289 messageId: input.messageId,
317 ...(input.turnId ? { turnId: input.turnId } : {}),290 ...(input.turnId ? { turnId: input.turnId } : {}),
318 text: spilled ? "" : text,291 text: "",
319 createdAt: input.createdAt,292 createdAt: input.createdAt,
320 });293 });
321 yield* clearAssistantMessageState(input.messageId);294 yield* clearAssistantMessageState(input.messageId);
...@@ -418,7 +391,6 @@ const make = Effect.gen(function* () {...@@ -418,7 +391,6 @@ const make = Effect.gen(function* () {
418 if (assistantDeliveryMode === "buffered") {391 if (assistantDeliveryMode === "buffered") {
419 const spillChunk = yield* appendBufferedAssistantText(assistantMessageId, event.delta);392 const spillChunk = yield* appendBufferedAssistantText(assistantMessageId, event.delta);
420 if (spillChunk.length > 0) {393 if (spillChunk.length > 0) {
421 yield* markBufferedAssistantMessageSpilled(assistantMessageId);
422 yield* orchestrationEngine.dispatch({394 yield* orchestrationEngine.dispatch({
423 type: "thread.message.assistant.delta",395 type: "thread.message.assistant.delta",
424 commandId: providerCommandId(event, "assistant-delta-buffer-spill"),396 commandId: providerCommandId(event, "assistant-delta-buffer-spill"),
...@@ -456,7 +428,7 @@ const make = Effect.gen(function* () {...@@ -456,7 +428,7 @@ const make = Effect.gen(function* () {
456 ...(turnId ? { turnId } : {}),428 ...(turnId ? { turnId } : {}),
457 createdAt: now,429 createdAt: now,
458 commandTag: "assistant-complete",430 commandTag: "assistant-complete",
459 spillRemainderCommandTag: "assistant-delta-buffer-remainder",431 finalDeltaCommandTag: "assistant-delta-finalize",
460 });432 });
461433
462 if (turnId) {434 if (turnId) {
...@@ -479,7 +451,7 @@ const make = Effect.gen(function* () {...@@ -479,7 +451,7 @@ const make = Effect.gen(function* () {
479 turnId,451 turnId,
480 createdAt: now,452 createdAt: now,
481 commandTag: "assistant-complete-finalize",453 commandTag: "assistant-complete-finalize",
482 spillRemainderCommandTag: "assistant-delta-buffer-remainder-finalize",454 finalDeltaCommandTag: "assistant-delta-finalize-fallback",
483 });455 });
484 }),456 }),
485 { concurrency: 1 },457 { concurrency: 1 },