| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- /** Process-local assistant attempt framing and durable stream accumulation. */
- import {
- AssistantStreamAccumulator,
- BlockAssembler,
- LlmAttemptId,
- type AssistantStreamRecord,
- type ContentBlock,
- type FinishReason,
- type ReplayEnvelope,
- type StreamChunk,
- type TokenUsage,
- } from '@deepseek-ai/dsh-llm'
- import type { AssistantStreamFrame } from '@deepseek-ai/dsh-agent'
- import type { SessionEventMap, SessionId, SessionSeq } from '@deepseek-ai/dsh-session'
- /** Folds one model attempt into one compact stream plus ordered transient frames. */
- export class AssistantStreamAttempt {
- private readonly accumulator = new AssistantStreamAccumulator()
- private readonly assembler = new BlockAssembler()
- private index = 0
- private terminal = false
- /** Attempt identity unique within this Agent lifecycle. */
- readonly attemptId: LlmAttemptId
- /** Whether this started attempt has emitted its terminal frame. */
- get ended(): boolean { return this.terminal }
- /**
- * @param sessionId - identity embedded only in the Agent-lifecycle-local attempt id.
- * @param attempt - attached-Session-local attempt counter.
- * @param nextRevision - allocates the next emitted frame revision.
- * @param turn - durable turn owning the request.
- * @param step - durable step owning the request.
- * @param emit - agent-scoped notification publisher.
- */
- constructor(
- sessionId: SessionId,
- attempt: number,
- private readonly nextRevision: () => number,
- readonly turn: number,
- readonly step: number,
- private readonly emit: (frame: AssistantStreamFrame) => void,
- ) {
- this.attemptId = LlmAttemptId(`${sessionId}:${attempt}`)
- }
- /** Publish the opening marker before the first delivered chunk. */
- start(): void {
- this.emit({
- type: 'start',
- attemptId: this.attemptId,
- revision: this.nextRevision(),
- turn: this.turn,
- step: this.step,
- })
- }
- /** Snapshot one chunk once, then feed durable compaction, assembly, and live publication. */
- push(chunk: StreamChunk): void {
- const timed = this.accumulator.push({ time: Date.now(), chunk })
- this.assembler.push(timed.chunk)
- this.emit({
- type: 'chunk',
- attemptId: this.attemptId,
- revision: this.nextRevision(),
- index: this.index++,
- time: timed.time,
- chunk: timed.chunk,
- })
- }
- /**
- * Publish terminal settlement after the matching durable event commits.
- * @param eventType - durable settlement type.
- * @param append - synchronous durable append returning its committed seq.
- */
- settle(
- eventType: 'assistant/message' | 'assistant/attempt',
- append: () => SessionSeq,
- ): void {
- let seq: SessionSeq
- try {
- seq = append()
- } catch (error: unknown) {
- this.abandon()
- throw error
- }
- this.terminal = true
- this.emit({
- type: 'end',
- attemptId: this.attemptId,
- revision: this.nextRevision(),
- index: this.index,
- outcome: { kind: 'committed', eventType, seq },
- })
- }
- /** Publish abandonment when no durable attempt event can be committed. */
- abandon(): void {
- this.terminal = true
- this.emit({
- type: 'end',
- attemptId: this.attemptId,
- revision: this.nextRevision(),
- index: this.index,
- outcome: { kind: 'abandoned' },
- })
- }
- /** Exact compact stream for the final durable event. */
- get stream(): SessionEventMap['assistant/attempt']['stream'] {
- return [...this.accumulator.snapshot()] as AssistantStreamRecord[]
- }
- /** Canonical completed-message blocks from the same chunks. */
- blocks(): ContentBlock[] {
- return this.assembler.blocks()
- }
- /** Safe visible prefix when cancellation interrupts the attempt. */
- interruptedBlocks(): ContentBlock[] {
- return this.assembler.interruptedBlocks()
- }
- /** Latest adapter-reported usage in the stream. */
- get usage(): TokenUsage | undefined {
- return this.assembler.usage
- }
- /** Terminal reason, defaulting to stop when the stream omitted one. */
- get finish(): FinishReason {
- return this.assembler.finish
- }
- /** Replay state carried by the terminal finish record. */
- get replayState(): ReplayEnvelope | undefined {
- return this.assembler.replayState
- }
- }
|