Skip to main content

Multiple Streams

Real applications often need to stream multiple pieces of content simultaneously. A code reviewer might display issues, suggestions, and improved code in separate panels. A research assistant might summarize multiple sources before synthesizing them. Idyllic supports these patterns through multiple stream<T> fields, each operating independently.

Defining Multiple Stream Fields

Declare multiple stream<T> fields. Each maintains its own value, status, and update history:
export default class CodeReviewer extends AgenticSystem {
  @field issues = stream<string>('');
  @field suggestions = stream<string>('');
  @field improvedCode = stream<string>('');
  @field status: 'idle' | 'reviewing' = 'idle';

  @action()
  async review(code: string) {
    this.status = 'reviewing';
    await this.analyzeCode(code);
    this.status = 'idle';
  }
}
Every append() call broadcasts to all connected clients. The framework handles multiplexing automatically.

Sequential Streaming

Complete one stream before starting the next. Works when later streams depend on earlier content:
@action()
async review(code: string) {
  this.status = 'reviewing';

  // First: stream issues
  for await (const chunk of this.findIssues(code)) {
    this.issues.append(chunk);
  }
  this.issues.complete();

  // Second: stream suggestions based on issues
  for await (const chunk of ai.stream(`Suggest fixes for:\n${this.issues.current}`)) {
    this.suggestions.append(chunk);
  }
  this.suggestions.complete();

  // Third: stream improved code
  for await (const chunk of ai.stream(`Improve:\n${code}`)) {
    this.improvedCode.append(chunk);
  }
  this.improvedCode.complete();

  this.status = 'idle';
}
Each section finishes before the next begins, creating predictable reading flow.

Parallel Streaming

When streams are independent, update them simultaneously with Promise.all:
export default class ResearchSynthesizer extends AgenticSystem {
  @field summaries: Array<{ source: string; content: StreamableValue<string> }> = [];
  @field synthesis = stream<string>('');
  @field status: 'idle' | 'summarizing' | 'synthesizing' = 'idle';

  @action()
  async synthesize(sources: string[]) {
    this.status = 'summarizing';

    // Create streams for each source
    this.summaries = sources.map(source => ({
      source,
      content: stream<string>(''),
    }));

    // Stream all in parallel
    await Promise.all(
      sources.map(async (source, i) => {
        for await (const chunk of ai.stream(`Summarize: ${source}`)) {
          this.summaries[i].content.append(chunk);
        }
        this.summaries[i].content.complete();
      })
    );

    // Then synthesize
    this.status = 'synthesizing';
    const allContent = this.summaries.map(s => s.content.current).join('\n\n');

    for await (const chunk of ai.stream(`Synthesize:\n${allContent}`)) {
      this.synthesis.append(chunk);
    }
    this.synthesis.complete();

    this.status = 'idle';
  }
}
Clients see multiple cards streaming simultaneously. Fast sources don’t block slow ones.

Client-Side Rendering

Iterate over streams, showing status for each:
function ResearchView() {
  const { summaries, synthesis, status } = useSystem<ResearchSynthesizer>();

  return (
    <div>
      <StatusBadge status={status} />

      <div className="summaries">
        {summaries.map((summary, i) => (
          <SummaryCard key={i} source={summary.source}>
            {summary.content.current}
            {summary.content.status === 'streaming' && <Cursor />}
          </SummaryCard>
        ))}
      </div>

      {synthesis.current && (
        <div className="synthesis">
          {synthesis.current}
          {synthesis.status === 'streaming' && <Cursor />}
        </div>
      )}
    </div>
  );
}

FAQ

How many streams can run in parallel?

No hard limit. Practical constraints are LLM rate limits and client rendering performance. Ten simultaneous streams work fine.

What if one parallel stream fails?

With Promise.all, one failure rejects the group. Use Promise.allSettled for independent error handling:
await Promise.allSettled(
  sources.map(async (source, i) => {
    try {
      await this.streamSource(source, i);
    } catch (e) {
      this.summaries[i].content.fail(e);
    }
  })
);

How do I show overall progress?

Track completion count in state:
@field completedCount = 0;

// After each stream completes
this.summaries[i].content.complete();
this.completedCount++;
Client can show “3 of 5 sources analyzed.”