Skip to content

Streaming ​

Spectator supports two streaming modes for real-time story generation.

Overview ​

MethodGranularityReturnsUse Case
stream()Scene-levelAsyncGenerator<Scene, Story>Process each scene as a complete unit
streamText()Token-levelStoryStreamReal-time text display, typing effects

Scene-Level Streaming ​

stream() is an async generator that yields each completed Scene as it's generated:

typescript
const engine = new Engine({ provider: 'anthropic' })

for await (const scene of engine.stream(input)) {
  console.log(`--- ${scene.beat?.name ?? 'Scene'} ---`)
  console.log(scene.text)
  console.log()
}

The generator's return value is the complete Story, but in most cases you'll just consume the scenes.

Token-Level Streaming ​

streamText() returns a StoryStream that emits fine-grained events as tokens arrive:

typescript
const stream = engine.streamText({
  characters,
  plot,
  instructions: 'Set in modern-day Seoul',
})

for await (const event of stream) {
  switch (event.type) {
    case 'scene-start':
      console.log(`\n--- Scene ${event.sceneIndex + 1}: ${event.beat?.name ?? 'Untitled'} ---\n`)
      break
    case 'text-delta':
      process.stdout.write(event.text)
      break
    case 'scene-complete':
      console.log('\n')
      if (event.scene.summary) {
        console.log(`[Summary: ${event.scene.summary}]`)
      }
      break
  }
}

Stream Events ​

The StoryStream emits three event types:

EventFieldsWhen
scene-startsceneIndex, beat?A new scene begins generating
text-deltatext, sceneIndexA chunk of text arrives
scene-completescene, sceneIndexA scene is fully generated and analyzed
typescript
type StreamEvent =
  | { type: 'scene-start'; sceneIndex: number; beat?: BeatData }
  | { type: 'text-delta'; text: string; sceneIndex: number }
  | { type: 'scene-complete'; scene: Scene; sceneIndex: number }

Accessing the Completed Story ​

After the stream is fully consumed, access the completed Story:

typescript
// Option 1: access after iteration
const stream = engine.streamText(input)
for await (const event of stream) { /* handle events */ }
const story = stream.story

// Option 2: drain the stream and get the story directly
const stream = engine.streamText(input)
const story = await stream.toStory()

WARNING

Accessing stream.story before the stream is fully consumed throws an error. Either iterate through all events first, or use toStory().

Continuation Streaming ​

Both streaming modes work with story continuation:

typescript
// Scene-level continuation
for await (const scene of engine.continueStream(story, input)) {
  console.log(scene.text)
}

// Token-level continuation
const stream = engine.continueStreamText(story, input)
for await (const event of stream) {
  // handle events...
}

See Story Continuation for more details.

Released under the MIT License.