4.6 KiB
Recipes
Use this reference when the user wants code, when you are editing xsAI code, or when a prompt needs a canonical minimal example.
General rules
- Prefer granular
@xsai/*imports in new examples. - Switch to the umbrella
xsaipackage only when the repo already uses it or the user explicitly wants one dependency. - Keep
baseURLandmodelexplicit. - Include
apiKeyfor hosted providers. In Node.js examples, preferprocess.env. In browser examples, preferlocalStorage. For local or proxy endpoints, only include it if the target actually requires one. Do not hardcode secrets. - Preserve the repo's existing schema library instead of swapping between Zod, Valibot, ArkType, or Effect without a reason.
Minimal text generation
import { env } from 'node:process'
import { generateText } from '@xsai/generate-text'
const { text } = await generateText({
apiKey: env.OPENAI_API_KEY!,
baseURL: 'https://api.openai.com/v1/',
messages: [
{
content: 'You are a helpful assistant.',
role: 'system',
},
{
content: 'Write one sentence about the moon.',
role: 'user',
},
],
model: 'gpt-4o',
})
Use this as the default starting point for simple scripts, tests, and one-shot helpers.
These examples use node:process because they target Node.js. For browser examples, prefer reading the API key from localStorage instead of hardcoding it.
Streaming text with tools
import { env } from 'node:process'
import { streamText } from '@xsai/stream-text'
import { stepCountAtLeast } from '@xsai/stream-text/shared-chat'
import { tool } from '@xsai/tool'
import * as v from 'valibot'
const add = await tool({
description: 'Adds two numbers',
execute: ({ a, b }) => (Number.parseInt(a) + Number.parseInt(b)).toString(),
name: 'add',
parameters: v.object({
a: v.pipe(v.string(), v.description('First number')),
b: v.pipe(v.string(), v.description('Second number')),
}),
})
const { fullStream } = streamText({
apiKey: env.OPENAI_API_KEY!,
baseURL: 'https://api.openai.com/v1/',
messages: [
{
content: 'You are a helpful assistant.',
role: 'system',
},
{
content: 'What is 12 plus 30? Use the add tool.',
role: 'user',
},
],
model: 'gpt-4o',
stopWhen: stepCountAtLeast(2),
toolChoice: 'required',
tools: [add],
})
const text: string[] = []
for await (const event of fullStream) {
if (event.type === 'text-delta') {
text.push(event.text)
}
if (event.type === 'tool-call' || event.type === 'tool-result') {
console.log(event)
}
}
console.log(text.join(''))
Use textStream for plain live text. Use fullStream when the caller needs tool events, reasoning deltas, or finish metadata.
Validated structured output
Valibot examples require @valibot/to-json-schema in the project. If the repo already uses a different supported schema library, keep that choice.
import { env } from 'node:process'
import { generateObject } from '@xsai/generate-object'
import * as v from 'valibot'
const { object } = await generateObject({
apiKey: env.OPENAI_API_KEY!,
baseURL: 'https://api.openai.com/v1/',
messages: [
{
content: 'Extract the event information.',
role: 'system',
},
{
content: 'Alice and Bob are going to a science fair on Friday.',
role: 'user',
},
],
model: 'gpt-4o',
schema: v.object({
date: v.string(),
name: v.string(),
participants: v.array(v.string()),
}),
})
Prefer this over asking the model for free-form JSON.
Streaming structured output
import { env } from 'node:process'
import { streamObject } from '@xsai/stream-object'
import * as v from 'valibot'
const { partialObjectStream } = await streamObject({
apiKey: env.OPENAI_API_KEY!,
baseURL: 'https://api.openai.com/v1/',
messages: [
{
content: 'Extract the event information.',
role: 'system',
},
{
content: 'Alice and Bob are going to a science fair on Friday.',
role: 'user',
},
],
model: 'gpt-4o',
schema: v.object({
date: v.string(),
name: v.string(),
participants: v.array(v.string()),
}),
})
for await (const partialObject of partialObjectStream) {
console.log(partialObject)
}
Use object mode for partial updates and output: 'array' with elementStream when the caller needs item-by-item results.
Embeddings
import { embed } from '@xsai/embed'
const { embedding, usage } = await embed({
baseURL: 'http://localhost:11434/v1/',
input: 'sunny day at the beach',
model: 'all-minilm',
})
Use embedMany for batch inputs with the same provider and model.