Skip to main content

Method Signature

async *answerStreamGenerator(
  request: AnswerRequest
): AsyncGenerator<string | { metadata: AnswerResponse }, void, unknown>
Generate a streaming AI response with brand enrichment using async generators. This provides an alternative to answerStream() with a more modern async/await syntax using for await...of loops.

Parameters

request
AnswerRequest
required
The request payload containing the user’s message and optional configuration

Return Value

Returns an AsyncGenerator that yields two types of values:
string
string
Accumulated response text from the beginning. Each yield contains the full text so far, not just the latest chunk.
{ metadata: AnswerResponse }
object
Final object containing the complete response and metadata. This is yielded once at the end of the stream.

Examples

Basic Usage with for await

const stream = client.answerStreamGenerator({
  message: 'What are the best noise-cancelling headphones?'
});

for await (const chunk of stream) {
  if (typeof chunk === 'string') {
    // Text chunk - accumulated text so far
    console.log('Text:', chunk);
  } else {
    // Metadata object
    console.log('Brand:', chunk.metadata.metadata.brandUsed?.name);
    console.log('Link:', chunk.metadata.metadata.link);
  }
}

Update DOM in Real-Time

const responseElement = document.getElementById('response');
const brandElement = document.getElementById('brand');

const stream = client.answerStreamGenerator({
  message: 'Best laptops for developers?'
});

for await (const chunk of stream) {
  if (typeof chunk === 'string') {
    // Update UI with accumulated text
    responseElement.textContent = chunk;
  } else {
    // Display brand information
    const brand = chunk.metadata.metadata.brandUsed;
    if (brand) {
      brandElement.innerHTML = `
        <img src="${brand.image}" alt="${brand.name}">
        <a href="${chunk.metadata.metadata.link}">${brand.name}</a>
      `;
    }
  }
}

With Loading States

const loadingElement = document.getElementById('loading');
const responseElement = document.getElementById('response');

loadingElement.style.display = 'block';

try {
  const stream = client.answerStreamGenerator({
    message: 'Explain quantum computing',
    model: 'gpt-4-turbo'
  });

  for await (const chunk of stream) {
    if (typeof chunk === 'string') {
      responseElement.textContent = chunk;
    } else {
      console.log('Streaming complete');
    }
  }
} finally {
  loadingElement.style.display = 'none';
}

Extract Text and Metadata Separately

const stream = client.answerStreamGenerator({
  message: 'Best running shoes for marathon training?'
});

let finalText = '';
let metadata;

for await (const chunk of stream) {
  if (typeof chunk === 'string') {
    finalText = chunk;
    console.log(`Progress: ${chunk.length} characters`);
  } else {
    metadata = chunk.metadata;
  }
}

console.log('Final text:', finalText);
console.log('Metadata:', metadata);

With Type Guards

function isMetadata(chunk: string | { metadata: AnswerResponse }): chunk is { metadata: AnswerResponse } {
  return typeof chunk !== 'string';
}

const stream = client.answerStreamGenerator({
  message: 'What are the best cameras for photography?'
});

for await (const chunk of stream) {
  if (isMetadata(chunk)) {
    console.log('Received metadata:', chunk.metadata);
  } else {
    console.log('Received text:', chunk);
  }
}

With Conversation Context

const stream = client.answerStreamGenerator({
  message: 'What about battery life?',
  conversationId: 'conv_789',
  previousMessages: [
    {
      role: 'user',
      content: 'Best wireless headphones?'
    },
    {
      role: 'assistant',
      content: 'For wireless headphones, I recommend...'
    }
  ]
});

for await (const chunk of stream) {
  if (typeof chunk === 'string') {
    console.log(chunk);
  }
}

Character Count Tracking

let previousLength = 0;

const stream = client.answerStreamGenerator({
  message: 'Tell me about electric vehicles'
});

for await (const chunk of stream) {
  if (typeof chunk === 'string') {
    const newChars = chunk.length - previousLength;
    console.log(`Received ${newChars} new characters`);
    previousLength = chunk.length;
  }
}

Early Termination

const stream = client.answerStreamGenerator({
  message: 'Write a long article about AI'
});

let characterCount = 0;
const maxChars = 500;

for await (const chunk of stream) {
  if (typeof chunk === 'string') {
    characterCount = chunk.length;
    
    // Stop streaming after reaching character limit
    if (characterCount >= maxChars) {
      console.log('Reached character limit, stopping...');
      break;
    }
    
    console.log(chunk);
  }
}

Error Handling

try {
  const stream = client.answerStreamGenerator({
    message: '  ' // Empty message
  });

  for await (const chunk of stream) {
    console.log(chunk);
  }
} catch (error) {
  console.error('Error:', error.message);
  // Output: "Message is required"
}

Network and Timeout Errors

import { NetworkError, TimeoutError } from '@thred/sdk';

try {
  const stream = client.answerStreamGenerator({
    message: 'What are the best smartphones?'
  });

  for await (const chunk of stream) {
    if (typeof chunk === 'string') {
      console.log(chunk);
    }
  }
} catch (error) {
  if (error instanceof TimeoutError) {
    console.error('Request timed out');
  } else if (error instanceof NetworkError) {
    console.error('Network error:', error.message);
  } else {
    console.error('Streaming error:', error);
  }
}

Streaming Format

The generator yields values in this sequence:
  1. Multiple string yields: Each contains the accumulated text from the start
  2. One metadata yield: Final object with complete response and metadata
// Yield 1: "Hello"
// Yield 2: "Hello, I"
// Yield 3: "Hello, I can"
// Yield 4: "Hello, I can help"
// ...
// Final yield: { metadata: { response: "Hello, I can help...", metadata: {...} } }

How It Works

  1. Validates the message is not empty
  2. Sends a POST request to /v1/answer/stream
  3. Processes the response stream using ReadableStream API
  4. Yields accumulated text strings as data arrives
  5. Parses metadata from the end of the stream
  6. Yields the final metadata object
  7. Generator completes

Use Cases

When to Use answerStreamGenerator()

  • You prefer modern async/await syntax over callbacks
  • You need fine-grained control over stream processing
  • You want to easily break out of the streaming loop
  • You’re building with TypeScript and want strong type inference

When to Use answerStream() Instead

  • You prefer callback-based APIs
  • You need automatic DOM updates via targets parameter
  • You want automatic impression tracking
  • You need simpler code for basic streaming

When to Use answer() Instead

  • You don’t need streaming
  • You want the complete response at once
  • You’re building a non-interactive feature

Comparison with answerStream()

FeatureanswerStreamGenerator()answerStream()
Syntaxfor await...ofCallback function
DOM targetsNot supportedSupported
Impression trackingManualAutomatic
Control flowCan break/continueMust complete
Type safetyExcellentGood
ComplexityMore flexibleSimpler

Build docs developers (and LLMs) love