Signal · guide

A stream

The same call, read while the model is still writing: the text a few characters at a time, then the whole result.

Install it
pnpm add @niscorp/signal zod openai
Step 1 of 3

The client

createSignal names a provider and gives you a client. The key is read from GROQ_API_KEY when a call is made, and the model is the provider’s default unless you name one with .model().

The client
import { createSignal } from '@niscorp/signal';

const signal = createSignal('groq');
Step 2 of 3

A way to stop

A stream takes an AbortSignal. Aborting it ends the request to the provider, and the loop that reads the stream then throws.

A way to stop
const stop = new AbortController();
const timer = setTimeout(() => stop.abort(), 30_000);
Step 3 of 3

The events

stream is the same run as complete, as events. text carries what the model wrote since the last one. done carries what complete would have returned: the response, the history and the usage.

With a schema, the text is the JSON as it is written, and the typed object arrives with done. An answer that never passes the schema arrives as an error event. A provider’s failure, a missing key and an abort throw, so the loop stands in a try.

The events
try {
  for await (const event of signal.stream('Describe The Tempest in two sentences.', { signal: stop.signal })) {
    if (event.type === 'text') process.stdout.write(event.text);
    else if (event.type === 'done') console.log('\n' + event.meta.usage.totalTokens, 'tokens');
    else if (event.type === 'error') console.log(event.error.message);
  }
} catch (error) {
  console.log('stopped:', error instanceof Error ? error.message : error);
} finally {
  clearTimeout(timer);
}
Then

Run it.

This file asks a model, so it is not run on this site. It prints the two sentences as they arrive, then a line with the number of tokens.

Run it
GROQ_API_KEY=your-key npx tsx stream.mts