fix(session-title): serialize provider request writes
This commit is contained in:
@@ -8,7 +8,12 @@ import z from 'schemastery'
|
||||
import type { Branded } from '@deepseek-ai/dsh-brand'
|
||||
import { deepFreeze, isAgentLoopRequest } from '@deepseek-ai/dsh-llm'
|
||||
import type { GenerateOptions } from '@deepseek-ai/dsh-llm'
|
||||
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import type {
|
||||
OutOfBandSessionEventType,
|
||||
Session,
|
||||
SessionEvent,
|
||||
SessionEventMap,
|
||||
} from '@deepseek-ai/dsh-session'
|
||||
import { fallbackSessionTitle, normalizeSessionTitle } from './normalize.ts'
|
||||
|
||||
export { fallbackSessionTitle, normalizeSessionTitle, truncateTitleUtf8 } from './normalize.ts'
|
||||
@@ -95,6 +100,46 @@ declare module '@deepseek-ai/dsh-session' {
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-session settlement tails for title-capability out-of-band writes. */
|
||||
const SESSION_TITLE_WRITE_TAILS = new WeakMap<Session, Promise<void>>()
|
||||
|
||||
/** Convert either write outcome into a fulfilled queue tail. */
|
||||
function settleSessionTitleWrite(): void {}
|
||||
|
||||
/**
|
||||
* Serialize one title-capability out-of-band event with its session peers.
|
||||
* Cancellation is checked when the write reaches the head of the queue; once
|
||||
* the core append starts, its durability contract runs to completion.
|
||||
* @param ctx - context exposing the live session store.
|
||||
* @param session - exact live session that owns the title-capability event.
|
||||
* @param type - plugin-declared log-only title event type.
|
||||
* @param data - typed JSON payload for the event.
|
||||
* @param signal - service or provider lifetime checked before publication starts.
|
||||
* @returns the durably accepted event.
|
||||
*/
|
||||
export async function appendSessionTitleOutOfBand<T extends OutOfBandSessionEventType>(
|
||||
ctx: Context,
|
||||
session: Session,
|
||||
type: T,
|
||||
data: SessionEventMap[T],
|
||||
signal: AbortSignal,
|
||||
): Promise<SessionEvent<T>> {
|
||||
const predecessor = SESSION_TITLE_WRITE_TAILS.get(session)
|
||||
const run = Promise.resolve(predecessor).then(() => {
|
||||
signal.throwIfAborted()
|
||||
return ctx.sessions.appendOutOfBand(session, type, data, { kind: 'session-title' })
|
||||
})
|
||||
const tail = run.then(settleSessionTitleWrite, settleSessionTitleWrite)
|
||||
SESSION_TITLE_WRITE_TAILS.set(session, tail)
|
||||
try {
|
||||
return await run
|
||||
} finally {
|
||||
if (SESSION_TITLE_WRITE_TAILS.get(session) === tail) {
|
||||
SESSION_TITLE_WRITE_TAILS.delete(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** One eligible human text message exposed to title providers. */
|
||||
export interface SessionTitleUserMessage {
|
||||
/** Source `user/message` event seq. */
|
||||
@@ -481,7 +526,7 @@ export class SessionTitleService extends Service {
|
||||
})
|
||||
this.assertCurrent(session, work)
|
||||
const accepted = this.validateResult(result, messages)
|
||||
await this.ctx.sessions.appendOutOfBand(session, 'session/title', {
|
||||
await appendSessionTitleOutOfBand(this.ctx, session, 'session/title', {
|
||||
title: accepted.title,
|
||||
messageSeqs: [...accepted.messageSeqs],
|
||||
source: {
|
||||
@@ -489,7 +534,7 @@ export class SessionTitleService extends Service {
|
||||
provider: work.registration.provider.id,
|
||||
...accepted.model === undefined ? {} : { model: accepted.model },
|
||||
},
|
||||
}, { kind: 'session-title' })
|
||||
}, work.signal)
|
||||
return this.get(session)
|
||||
} finally {
|
||||
const state = this.work.get(session)
|
||||
@@ -662,11 +707,11 @@ export class SessionTitleService extends Service {
|
||||
this.config.fallbackMaxBytes,
|
||||
)
|
||||
if (title.length === 0) return undefined
|
||||
await this.ctx.sessions.appendOutOfBand(session, 'session/title', {
|
||||
await appendSessionTitleOutOfBand(this.ctx, session, 'session/title', {
|
||||
title,
|
||||
messageSeqs: [first.seq],
|
||||
source: { kind: 'fallback' },
|
||||
}, { kind: 'session-title' })
|
||||
}, this.lifetime.signal)
|
||||
return this.get(session)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user