> ## Documentation Index
> Fetch the complete documentation index at: https://docs.core-ai.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# streamObject()

> Stream structured objects validated against Zod schemas in real-time

## Overview

The `streamObject()` function streams structured objects from chat models in real-time, with progressive updates validated against a Zod schema. This is ideal for displaying partial results as they're being generated.

## Function signature

```typescript theme={null}
export async function streamObject<TSchema extends z.ZodType>(
    params: StreamObjectParams<TSchema>
): Promise<ObjectStream<TSchema>>

export type StreamObjectParams<TSchema extends z.ZodType> = 
    StreamObjectOptions<TSchema> & {
        model: ChatModel;
    };
```

## Parameters

<ParamField path="model" type="ChatModel" required>
  The chat model instance to use for streaming.
</ParamField>

<ParamField path="messages" type="Message[]" required>
  Array of messages in the conversation. Must not be empty.
</ParamField>

<ParamField path="schema" type="z.ZodType" required>
  Zod schema that defines the structure of the object to generate.
</ParamField>

<ParamField path="schemaName" type="string">
  Optional name for the schema.
</ParamField>

<ParamField path="schemaDescription" type="string">
  Optional description of the schema.
</ParamField>

<ParamField path="temperature" type="number">
  Sampling temperature (0-2).
</ParamField>

<ParamField path="maxTokens" type="number">
  Maximum number of tokens to generate.
</ParamField>

<ParamField path="topP" type="number">
  Nucleus sampling parameter (0-1).
</ParamField>

<ParamField path="reasoning" type="ReasoningConfig">
  Configuration for extended thinking/reasoning capabilities.
</ParamField>

<ParamField path="providerOptions" type="GenerateProviderOptions">
  Provider-specific options, namespaced by provider name.
</ParamField>

<ParamField path="signal" type="AbortSignal">
  AbortSignal for cancelling the stream.
</ParamField>

## Return value

Returns a `Promise<ObjectStream<TSchema>>`. `ObjectStream` is an async iterable of `ObjectStreamEvent` objects with two additional properties:

```typescript theme={null}
type ObjectStream<TSchema extends z.ZodType> = AsyncIterable<
    ObjectStreamEvent<TSchema>
> & {
    readonly result: Promise<GenerateObjectResult<TSchema>>;
    readonly events: Promise<readonly ObjectStreamEvent<TSchema>[]>;
};
```

<ResponseField name="result" type="Promise<GenerateObjectResult<TSchema>>">
  Resolves with the final validated object result when the stream completes. Rejects on abort or upstream failure.
</ResponseField>

<ResponseField name="events" type="Promise<readonly ObjectStreamEvent<TSchema>[]>">
  Resolves with all observed events, including abort and failure cases.
</ResponseField>

<Info>
  The HTTP request starts as soon as you create the stream. You do not need to iterate before the model begins responding.
</Info>

### ObjectStreamEvent types

<ResponseField name="object-delta" type="{ type: 'object-delta'; text: string }">
  Emitted for each chunk of the JSON object being generated.
</ResponseField>

<ResponseField name="object" type="{ type: 'object'; object: z.infer<TSchema> }">
  Emitted once, when the full object payload has been validated.
</ResponseField>

<ResponseField name="finish" type="{ type: 'finish'; finishReason: FinishReason; usage: ChatUsage }">
  Emitted when streaming completes with final metadata.
</ResponseField>

## Examples

### Basic streaming object

```typescript theme={null}
import { streamObject } from '@core-ai/core-ai';
import { createOpenAI } from '@core-ai/openai';
import { z } from 'zod';

const openai = createOpenAI();
const model = openai.chatModel('gpt-5-mini');

const schema = z.object({
  name: z.string(),
  age: z.number(),
  bio: z.string()
});

const objectStream = await streamObject({
  model,
  messages: [
    { role: 'user', content: 'Generate a character profile' }
  ],
  schema
});

for await (const event of objectStream) {
  if (event.type === 'object-delta') {
    process.stdout.write(event.text);
  } else if (event.type === 'object') {
    console.log('\n\nComplete object:', event.object);
  }
}
```

### Progressive UI updates

```typescript theme={null}
const schema = z.object({
  tasks: z.array(
    z.object({
      title: z.string(),
      description: z.string(),
      priority: z.enum(['low', 'medium', 'high'])
    })
  )
});

const objectStream = await streamObject({
  model,
  messages: [
    { role: 'user', content: 'Create a project task list' }
  ],
  schema
});

let partialJson = '';

for await (const event of objectStream) {
  switch (event.type) {
    case 'object-delta':
      partialJson += event.text;
      updatePreview(partialJson);
      break;
    
    case 'object':
      displayTasks(event.object.tasks);
      break;
    
    case 'finish':
      console.log('Tokens used:', event.usage.outputTokens);
      break;
  }
}
```

### Using .result

```typescript theme={null}
const objectStream = await streamObject({
  model,
  messages: [
    { role: 'user', content: 'Generate a user profile' }
  ],
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

const finalResult = await objectStream.result;
console.log(finalResult.object);
console.log('Usage:', finalResult.usage);
```

### Using .events

```typescript theme={null}
const objectStream = await streamObject({
  model,
  messages: [{ role: 'user', content: 'Generate a user profile' }],
  schema
});

const events = await objectStream.events;
console.log(events.map((event) => event.type));
```

<Note>
  `ObjectStream` follows the same lifecycle semantics as `ChatStream`: `.result` settles independently of iteration, `.events` keeps the observed history, and iteration is replayable.
</Note>

### Cancellation

```typescript theme={null}
const controller = new AbortController();

const objectStream = await streamObject({
  model,
  messages: [
    { role: 'user', content: 'Generate a large dataset' }
  ],
  schema: z.object({
    data: z.array(z.object({
      id: z.number(),
      value: z.string()
    }))
  }),
  signal: controller.signal
});

setTimeout(() => controller.abort(), 5000);

try {
  for await (const event of objectStream) {
    if (event.type === 'object-delta') {
      process.stdout.write(event.text);
    }
  }
} catch (error) {
  console.log('\nStream cancelled');
}
```

## Important notes

<Info>
  `ObjectStream` is replayable: iterating after events have already arrived replays the buffered event history.
</Info>

<Info>
  The `object` event is emitted once with the complete validated object. Use `object-delta` events to show progressive updates while the JSON payload is still streaming.
</Info>

## Error handling

Throws `ValidationError` if:

* Messages array is empty

May also throw:

* `CoreAIError` if the stream completes without generating an object
* `ProviderError` if the provider returns an error during streaming

May also throw structured output errors:

* `StructuredOutputValidationError`
* `StructuredOutputParseError`

```typescript theme={null}
import { 
  ValidationError,
  StructuredOutputValidationError 
} from '@core-ai/core-ai';

try {
  const objectStream = await streamObject({
    model,
    messages: [{ role: 'user', content: 'Generate data' }],
    schema: z.object({ name: z.string() })
  });
  
  for await (const event of objectStream) {
    // Process events
  }
} catch (error) {
  if (error instanceof StructuredOutputValidationError) {
    console.error('Validation failed:', error.issues);
  } else if (error instanceof ValidationError) {
    console.error('Stream failed:', error.message);
  }
}
```
