ai-functions
Version:
Core AI primitives for building intelligent applications
246 lines (234 loc) • 6.69 kB
text/typescript
/**
* AI Generation functions with automatic model resolution and routing
*
* Wraps AI SDK generateObject and generateText with smart model routing:
* - Simple aliases: 'opus', 'sonnet', 'gpt-4o'
* - Full IDs: 'anthropic/claude-sonnet-4.5'
* - Auto-routes to native SDKs for openai/anthropic/google
*
* @packageDocumentation
*/
import {
generateText as sdkGenerateText,
streamText as sdkStreamText,
Output,
type GenerateTextResult,
type StreamTextResult,
type LanguageModel,
} from 'ai'
import { schema as convertSchema, type SimpleSchema } from './schema.js'
import { isZodSchema } from './type-guards.js'
import type { ZodTypeAny } from 'zod'
type ModelArg = string | LanguageModel
type SchemaArg = ZodTypeAny | SimpleSchema
interface GenerateObjectOptions<T> {
model: ModelArg
schema: T
prompt?: string
messages?: Array<{ role: 'user' | 'assistant' | 'system'; content: string }>
system?: string
mode?: 'auto' | 'json' | 'tool'
maxTokens?: number
temperature?: number
topP?: number
topK?: number
presencePenalty?: number
frequencyPenalty?: number
seed?: number
maxRetries?: number
abortSignal?: AbortSignal
headers?: Record<string, string>
experimental_telemetry?: {
isEnabled?: boolean
functionId?: string
metadata?: Record<string, string>
}
}
interface GenerateTextOptions {
model: ModelArg
prompt?: string
messages?: Array<{ role: 'user' | 'assistant' | 'system'; content: string }>
system?: string
maxTokens?: number
temperature?: number
topP?: number
topK?: number
presencePenalty?: number
frequencyPenalty?: number
seed?: number
maxRetries?: number
abortSignal?: AbortSignal
headers?: Record<string, string>
tools?: Record<string, unknown>
toolChoice?: 'auto' | 'none' | 'required' | { type: 'tool'; toolName: string }
maxSteps?: number
experimental_telemetry?: {
isEnabled?: boolean
functionId?: string
metadata?: Record<string, string>
}
}
/**
* Resolve model string to LanguageModel instance
* Uses ai-providers for model routing with Cloudflare AI Gateway support
*/
async function resolveModel(modelArg: ModelArg): Promise<LanguageModel> {
// Already a LanguageModel instance
if (typeof modelArg !== 'string') {
return modelArg
}
// Use ai-providers for model resolution
const { model } = await import('ai-providers')
return model(modelArg)
}
/**
* Convert schema to Zod if needed
*/
function resolveSchema(schemaArg: SchemaArg): ZodTypeAny {
if (isZodSchema(schemaArg)) {
return schemaArg
}
return convertSchema(schemaArg as SimpleSchema)
}
/**
* Generate a typed object from a prompt using AI
*
* Automatically resolves model aliases and routes to the best provider.
* Supports both Zod schemas and simplified schema syntax.
*
* @example
* ```ts
* import { generateObject } from 'ai-functions'
*
* // Simplified schema syntax
* const { object } = await generateObject({
* model: 'sonnet',
* schema: {
* recipe: {
* name: 'What is the recipe name?',
* type: 'food | drink | dessert',
* ingredients: ['List all ingredients'],
* steps: ['List all cooking steps'],
* },
* },
* prompt: 'Generate a lasagna recipe.',
* })
*
* // Zod schema also works
* import { z } from 'zod'
* const { object } = await generateObject({
* model: 'sonnet',
* schema: z.object({
* name: z.string(),
* ingredients: z.array(z.string()),
* }),
* prompt: 'Generate a lasagna recipe.',
* })
* ```
*/
export async function generateObject<T>(
options: GenerateObjectOptions<T>
): Promise<{ object: T; usage?: unknown; warnings?: unknown[] | undefined }> {
const model = await resolveModel(options.model)
const schema = resolveSchema(options.schema as SchemaArg)
const { schema: _schema, mode: _mode, ...rest } = options
// Using unknown cast for SDK compatibility - the AI SDK has complex type unions
const result = await sdkGenerateText({
...rest,
model,
output: Output.object({ schema }),
} as unknown as Parameters<typeof sdkGenerateText>[0])
return { object: result.output as T, usage: result.usage, warnings: result.warnings }
}
/**
* Generate text from a prompt using AI
*
* Automatically resolves model aliases and routes to the best provider.
*
* @example
* ```ts
* import { generateText } from 'ai-functions'
*
* const { text } = await generateText({
* model: 'opus', // → anthropic/claude-opus-4.5
* prompt: 'Write a haiku about programming.',
* })
*
* // With tools
* const { text, toolResults } = await generateText({
* model: 'gpt-4o', // → openai/gpt-4o
* prompt: 'What is the weather in San Francisco?',
* tools: { ... },
* maxSteps: 5,
* })
* ```
*/
export async function generateText(
options: GenerateTextOptions
): Promise<Awaited<ReturnType<typeof sdkGenerateText>>> {
const model = await resolveModel(options.model)
return sdkGenerateText({
...options,
model,
} as Parameters<typeof sdkGenerateText>[0])
}
/**
* Stream a typed object from a prompt using AI
*
* @example
* ```ts
* import { streamObject } from 'ai-functions'
*
* const { partialObjectStream } = streamObject({
* model: 'sonnet',
* schema: { story: 'Write a creative story' },
* prompt: 'Write a short story.',
* })
*
* for await (const partial of partialObjectStream) {
* console.log(partial.story)
* }
* ```
*/
export async function streamObject<T>(
options: GenerateObjectOptions<T>
): Promise<{ partialObjectStream: AsyncIterable<T> }> {
const model = await resolveModel(options.model)
const schema = resolveSchema(options.schema as SchemaArg)
const { schema: _schema, mode: _mode, ...rest } = options
// Using unknown cast for SDK compatibility - the AI SDK has complex type unions
const result = await sdkStreamText({
...rest,
model,
output: Output.object({ schema }),
} as unknown as Parameters<typeof sdkStreamText>[0])
return {
partialObjectStream: result.partialOutputStream as AsyncIterable<T>,
}
}
/**
* Stream text from a prompt using AI
*
* @example
* ```ts
* import { streamText } from 'ai-functions'
*
* const { textStream } = streamText({
* model: 'gemini', // → google/gemini-2.5-flash
* prompt: 'Explain quantum computing.',
* })
*
* for await (const chunk of textStream) {
* process.stdout.write(chunk)
* }
* ```
*/
export async function streamText(
options: GenerateTextOptions
): Promise<ReturnType<typeof sdkStreamText>> {
const model = await resolveModel(options.model)
return sdkStreamText({
...options,
model,
} as Parameters<typeof sdkStreamText>[0])
}