Generation, streaming, and tools
Use the portable primitives for streaming text, validated objects, messages, and local tools.
The high-level SDK primitives share the same model contract. You can move from a single response to streaming, structured output, or tools without rewriting provider plumbing.
Stream a response
streamText() starts the request and exposes an async text stream. Call collect() when you also need the normalized final result.
import { streamText } from "@zhivex-ai/sdk";
import { createOpenAI } from "@zhivex-ai/openai";
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
const stream = streamText({
model: openai("gpt-6-astra"),
prompt: "Draft a short release note."
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
const final = await stream.collect();
console.log(final.finishReason, final.usage);
Always consume or collect a production stream so terminal state, usage, and persistence work can finish.
Return validated data
Use generateObject() when downstream code needs a typed object instead of prose.
import { generateObject } from "@zhivex-ai/sdk";
import { createGemini } from "@zhivex-ai/gemini";
import { z } from "zod";
const gemini = createGemini({ apiKey: process.env.GEMINI_API_KEY });
const result = await generateObject({
model: gemini("gemini-3.6-flash"),
prompt: "Create a release summary for developers.",
schema: z.object({
title: z.string(),
highlights: z.array(z.string()).min(2)
}),
mode: "native",
schemaName: "release_summary"
});
console.log(result.object);
Provider capabilities differ. Choose the model from the current provider matrix and handle UnsupportedFeatureError instead of assuming silent fallback.
Give the model a local tool
Tools are application-owned functions. Their schema validates model-generated input before execute runs.
import { generateText, tool } from "@zhivex-ai/sdk";
import { z } from "zod";
const result = await generateText({
model,
prompt: "Check order ord_123.",
maxSteps: 2,
tools: {
lookupOrder: tool({
name: "lookupOrder",
description: "Reads an order from the application database.",
schema: z.object({ orderId: z.string() }),
execute: async ({ orderId }) => ({
orderId,
status: "shipped"
})
})
}
});
console.log(result.text, result.toolResults);
Treat a tool like any other privileged backend operation: authorize the user, scope database access to the tenant, bound its runtime, and require approval before side effects.
Stop, prepare, and narrow a tool loop
TypeScript 1.35.0 adds optional loop controls. Existing calls are unchanged: when both maxSteps and stopWhen are omitted, generation still stops after one step. Setting stopWhen without maxSteps runs until a condition matches and also stops after 20 steps. prepareStep applies to that step only. activeTools limits the names the model may see.
output: Output.object({ schema }) can sit on the same generateText or streamText call as tools. Output.object reads the final step and requires a Zod schema. generateObject remains available. A tool can use Zod or jsonSchema().
import { generateText, jsonSchema, Output, stepCountIs, tool } from "@zhivex-ai/sdk";
import { z } from "zod";
const schema = z.object({
orderId: z.string(),
status: z.string()
});
const result = await generateText({
model,
prompt: "Check order ord_123 and return its status.",
stopWhen: stepCountIs(2),
activeTools: ["lookupOrder"],
prepareStep: ({ stepNumber }) => (
stepNumber === 0 ? { activeTools: ["lookupOrder"] } : { activeTools: [] }
),
output: Output.object({ schema }),
tools: {
lookupOrder: tool({
name: "lookupOrder",
description: "Reads an order from the application database.",
inputSchema: jsonSchema({
type: "object",
properties: { orderId: { type: "string" } },
required: ["orderId"]
}),
execute: async (input) => ({
orderId: String(input.orderId),
status: "shipped"
})
})
}
});
console.log(result.output, result.toolResults);
hasToolCall("lookupOrder") is another stopWhen condition. Shared topP, seed, and stopSequences are accepted without changing existing defaults. OpenAI family checks reject shared topP on models that already reject top_p, and warn-and-omit shared seed and stopSequences in both API modes.
Use explicit messages
Use the message helpers for multi-part or pre-existing conversations:
import { assistant, system, user } from "@zhivex-ai/sdk";
const messages = [
system("You are a support assistant."),
user("My order has not arrived."),
assistant("I can check it. What is the order id?"),
user("ord_123")
];
For user-facing multi-turn history, prefer a Runner and durable session service instead of reconstructing the transcript in browser state.
Recover from invalid tool calls
TypeScript 1.17 adds opt-in toolExecution.validationErrorMode: "tool-result". Invalid schema input becomes sanitized feedback for another model step; the invalid call is neither executed nor sent for approval. Strict validation remains the default.
TypeScript 1.18 adds ToolNotRegisteredError and opt-in unknownToolMode recovery for unknown tool names. Bound recovery with model-step and agent budgets, and preserve call/result correlation across mixed batches and approval resumes. See the release notes for migration details.