Execution
Run the same prompt with Vercel AI SDK, OpenAI, Google GenAI, Anthropic, or agent frameworks.
All adapters accept the same prompt() definitions — write once, run on any SDK.
import { generate, stream } from '@crux/ai'
import { openai } from '@ai-sdk/openai'
// Structured output
const result = await generate(editDraft, {
model: openai('gpt-4o'),
input: { instruction: 'Fix the intro' },
})
result.object // typed as z.infer<typeof OutputSchema>
// Text output
const text = await generate(greet, {
model: openai('gpt-4o'),
input: { name: 'Henri' },
})
text.text // string
// Streaming
const result = await stream(editDraft, {
model: openai('gpt-4o'),
input: { instruction: 'Fix the intro' },
})
for await (const partial of result.partialObjectStream) { ... }Also re-exports tool, stepCountIs, hasToolCall, and types like LanguageModel, ToolSet, ToolChoice.
import { createOpenAI } from '@crux/openai'
import OpenAI from 'openai'
const adapter = createOpenAI(new OpenAI({ apiKey: '...' }))
// Structured output → chat.completions.parse
const result = await adapter.generate(editDraft, {
model: 'gpt-4o',
input: { instruction: 'Fix the intro' },
})
result.choices[0].message.parsed // typed
// Text output → chat.completions.create
const text = await adapter.generate(greet, {
model: 'gpt-4o-mini',
input: { name: 'Henri' },
})
// Streaming
const stream = await adapter.stream(greet, {
model: 'gpt-4o',
input: { name: 'Henri' },
})Accepts OpenAI-native options: tools, tool_choice, parallel_tool_calls, and all OpenAISettings.
import { createGoogle } from '@crux/google'
import { GoogleGenAI } from '@google/genai'
const adapter = createGoogle(new GoogleGenAI({ apiKey: '...' }))
// Structured output → generateContent with JSON schema
const result = await adapter.generate(editDraft, {
model: 'gemini-2.5-flash',
input: { instruction: 'Fix the intro' },
})
result.object // parsed + validated with Zod
// Text output
const text = await adapter.generate(greet, {
model: 'gemini-2.0-flash',
input: { name: 'Henri' },
})
// Streaming
const stream = await adapter.stream(greet, {
model: 'gemini-2.5-flash',
input: { name: 'Henri' },
})Accepts Google-native options: tools, temperature, maxOutputTokens, topP, topK, plus extra.cache.skip and extra.cache.ttlSeconds for per-call CachedContent control.
The Google adapter validates structured output with Zod after generation. If the model returns invalid JSON, you'll get a Zod error, not a Google SDK error.
import { createAnthropic } from '@crux/anthropic'
import Anthropic from '@anthropic-ai/sdk'
const adapter = createAnthropic(new Anthropic({ apiKey: '...' }))
// Structured output → messages.parse
const result = await adapter.generate(editDraft, {
model: 'claude-sonnet-4-5-20250929',
input: { instruction: 'Fix the intro' },
})
result.parsed_output // typed
// Text output → messages.create
const text = await adapter.generate(greet, {
model: 'claude-haiku-4-5-20251001',
input: { name: 'Henri' },
})
// Streaming
const stream = await adapter.stream(greet, {
model: 'claude-sonnet-4-5-20250929',
input: { name: 'Henri' },
})Accepts Anthropic-native options: tools, tool_choice, thinking, metadata.
Anthropic requires max_tokens. If not set in your prompt's settings, the adapter defaults to 4096.
Agent frameworks
For AI SDK-based agent frameworks that manage their own model calls, @crux/ai/agent returns composed instructions and a wrapped model instead of executing directly:
import { resolve } from '@crux/ai/agent'
const { instructions, model } = await resolve(chatAgent, {
model: languageModel,
input: { mode },
})
// Pass to any AI SDK-compatible agent framework
const agent = createAgent({
model,
instructions,
tools,
})See Framework Guides for specific integration examples with Convex Agent and other frameworks.
Same prompt, multiple SDKs
The same prompt works across all adapters without modification:
import { generate } from '@crux/ai'
import { createOpenAI } from '@crux/openai'
import { createGoogle } from '@crux/google'
import { createAnthropic } from '@crux/anthropic'
// Vercel AI SDK
await generate(sentiment, { model: openai('gpt-4o'), input })
// OpenAI SDK
const oai = createOpenAI(new OpenAI())
await oai.generate(sentiment, { model: 'gpt-4o', input })
// Google GenAI
const google = createGoogle(new GoogleGenAI({ apiKey: '...' }))
await google.generate(sentiment, { model: 'gemini-2.5-flash', input })
// Anthropic SDK
const anthropic = createAnthropic(new Anthropic({ apiKey: '...' }))
await anthropic.generate(sentiment, { model: 'claude-sonnet-4-5-20250929', input })Message conversion
Each adapter provides toMessages() and fromMessages() to convert between SDK-specific message formats and Crux's canonical Message type. Use these when integrating with existing chat UIs or migrating from raw SDK calls.
import { toMessages, fromMessages } from '@crux/ai'
// SDK format → canonical
const messages = toMessages(coreMessages)
// Canonical → SDK format
const sdkMessages = fromMessages(messages)import { toMessages, fromMessages } from '@crux/openai'
// ChatCompletionMessageParam[] → canonical
const messages = toMessages(chatMessages)
// Canonical → OpenAI format
const sdkMessages = fromMessages(messages)import { toMessages, fromMessages } from '@crux/google'
// Google Content[] → canonical
const messages = toMessages(contents)
// Canonical → Google format
const sdkMessages = fromMessages(messages)import { toMessages, fromMessages } from '@crux/anthropic'
// Anthropic MessageParam[] → canonical
const messages = toMessages(anthropicMessages)
// Canonical → Anthropic format
const sdkMessages = fromMessages(messages)Framework-agnostic generate functions
Some @crux/core APIs (like llmJudge, extractKeyFacts, summarizeMessages) accept a generic GenerateObjectFn or GenerateTextFn instead of being tied to a specific SDK. Each adapter exports these wrappers:
import { generateObjectFn, generateTextFn } from '@crux/ai'
const judge = llmJudge({ generate: generateObjectFn, model /* ... */ })
const result = await summarizeMessages({ generate: generateTextFn, model, messages })import { createGenerateObjectFn, createGenerateTextFn } from '@crux/openai'
const generateObj = createGenerateObjectFn(client, 'gpt-4o')
const generateTxt = createGenerateTextFn(client, 'gpt-4o')import { createGenerateObjectFn, createGenerateTextFn } from '@crux/google'
const generateObj = createGenerateObjectFn(client, 'gemini-2.5-flash')
const generateTxt = createGenerateTextFn(client, 'gemini-2.5-flash')import { createGenerateObjectFn, createGenerateTextFn } from '@crux/anthropic'
const generateObj = createGenerateObjectFn(client, 'claude-sonnet-4-5-20250929')
const generateTxt = createGenerateTextFn(client, 'claude-haiku-4-5-20251001')These helpers are provider-native, lightweight wrappers. They pass the schema to the provider structured-output API
and return {object}, but they do not run Crux prompt resolution, validation retry, safety, cassettes, tools, memory
capture, or instrumentation. Use full adapter generate() for normal prompt execution.
When a core API needs a GenerateObjectFn but you also need the full adapter runtime, bridge an existing adapter generate() function:
import { createGenerateObjectFnFromGenerate } from '@crux/core/compaction'
import { generate } from '@crux/ai'
const generateObj = createGenerateObjectFnFromGenerate(generate, {
promptId: 'quality.judge',
})The Vercel AI SDK adapter exports pre-bound functions (generateObjectFn, generateTextFn) since the model is passed
per-call. Its generateObjectFn shares the same structured-attempt mechanics as prompt structured generation. The
OpenAI, Google, and Anthropic adapters use factory functions that bind the client and model upfront.
Next steps
Flows
Chain multiple generate() calls into structured pipelines with named steps.
Tools
Declare and compose tools across contexts and prompts.
Middleware & Hooks
Global middleware and per-prompt lifecycle hooks.
Examples
Full working examples combining execution with memory and agents.
API Reference
@crux/ai, @crux/openai, @crux/google, and @crux/anthropic adapter signatures.