Prompts
Define typed, composable prompts that run with any SDK adapter.
Prompt strings, input shapes, output schemas, and model settings tend to
scatter across call sites as an application grows. prompt() keeps that call
contract in one typed place.
A Prompt describes what the model should see, what input the call accepts, what output it should return, and which reusable capabilities it needs. It does not call a model by itself. Adapters execute the same definition through Vercel AI SDK, OpenAI, Google GenAI, or Anthropic.
Define Your First Prompt
Start with ordinary strings. Markdown-oriented md
templates are optional.
import { prompt } from "@use-crux/core";
import { z } from "zod";
export const answerSupport = prompt({
id: "answer-support",
input: z.object({
question: z.string(),
}),
system: "You are a concise product support assistant.",
prompt: ({ input }) => input.question,
settings: { temperature: 0.2 },
});Run that definition with the adapter for the SDK you already use:
import { generate } from "@use-crux/ai";
import { openai } from "@ai-sdk/openai";
const result = await generate(answerSupport, {
model: openai("gpt-4o-mini"),
input: {
question: "How do I rotate an API key?",
},
});
console.log(result.text);See Prompt execution for streaming and every supported adapter.
The Mental Model
A Prompt owns one model-call contract:
prompt({
id: "answer-support",
use: [brand, docs, memory],
input: InputSchema,
output: OutputSchema,
system: "Who the assistant is.",
prompt: ({ input }) => `What the user asked: ${input.question}`,
});| Field | What it owns |
|---|---|
id | Stable identity for logs, evals, cache keys, Catalog, and Devtools |
input | Runtime data the prompt accepts |
output | Structured output schema; omit it for text generation |
system | Role, policy, and high-level instructions |
prompt | The user or task message for a single-turn call |
messages | Full message history for chat, few-shot examples, or role-specific turns |
use | Reusable contexts and capabilities resolved into the call |
tools | Tools owned by this prompt |
settings | SDK-agnostic defaults such as temperature and maximum tokens |
tests | Inline Eval cases for CI and local regression checks |
The normal composition path is use. Put a capability in that array and Crux
resolves what it contributes: instructions, tools, constraints, metadata, or
lifecycle behavior.
Choose Text or Structured Output
Omit output when the model should return text:
const rewrite = prompt({
id: "rewrite",
input: z.object({
text: z.string(),
tone: z.enum(["clear", "friendly", "direct"]),
}),
system: "Rewrite without changing the meaning.",
prompt: ({ input }) => `Tone: ${input.tone}\n\n${input.text}`,
});Add a Zod output schema when downstream code needs validated data:
const classifyTicket = prompt({
id: "classify-ticket",
input: z.object({
subject: z.string(),
body: z.string(),
}),
output: z.object({
category: z.enum(["billing", "auth", "bug", "other"]),
urgency: z.enum(["low", "normal", "high"]),
summary: z.string(),
}),
system: "Classify support tickets for routing.",
prompt: ({ input }) => `${input.subject}\n\n${input.body}`,
});Crux compiles your authored schema to the provider's supported wire shape, then validates the response against the schema you wrote. See Structured Output for provider lowering, optional fields, and result types.
Compose Capabilities With use
Contexts, memory, retrieval, grounding, skills, and other contributors use the same composition surface:
import { context, history, prompt } from "@use-crux/core";
import { memory, facts } from "@use-crux/core/memory";
import { grounding } from "@use-crux/core/retrieval";
const policy = context({
id: "support-policy",
system: "Do not invent product behavior.",
});
const userMemory = memory({
id: "user-memory",
store,
namespace: ({ input }) => `user:${input.userId}`,
blocks: [facts({ id: "facts", embed: dense })],
});
const docsGrounding = grounding({
id: "docs-grounding",
retriever: docs,
inject: "both",
citations: true,
});
const support = prompt({
id: "support",
use: [history.recent({ messages: 8 }), policy, userMemory, docsGrounding],
input: z.object({
userId: z.string(),
question: z.string(),
}),
system: "Answer the support question.",
prompt: ({ input }) => input.question,
});The prompt names its dependencies. Each primitive owns what it contributes.
Package Reusable Wiring in a Context
If several prompts should share the same policy and capabilities, package that wiring in a Context:
const supportKnowledge = context({
id: "support-knowledge",
use: [docsGrounding, userMemory],
system: "Use product documentation before user memory.",
});
const reply = prompt({
id: "reply",
use: [supportKnowledge],
input: z.object({
userId: z.string(),
question: z.string(),
}),
system: "You are a support assistant.",
prompt: ({ input }) => input.question,
});Use direct prompt({ use: [...] }) for one-off wiring. Use
context({ use: [...] }) when the combination is a reusable product
capability.
Choose system + prompt or messages
Use system and prompt for the common single-turn shape. Use messages when
the message list itself is the source of truth: chat history, few-shot
examples, or role-specific turns.
const chatReply = prompt({
id: "chat-reply",
input: z.object({
userMessage: z.string(),
}),
messages: ({ input }) => [
{ role: "system", content: "Answer as a careful assistant." },
{ role: "user", content: "Answer in one sentence." },
{ role: "assistant", content: "I will keep responses concise." },
{ role: "user", content: input.userMessage },
],
});Do not combine messages with system or prompt.
Inspect Before You Run
Use .resolve() for provider-neutral prompt data or preview() for
whole-request fit and prospective adaptations. Neither calls a model.
const inspection = await preview(support, {
model,
input: {
userId: "user_123",
question: "How do I configure SSO?",
},
inputBudget: { max: 4000 },
});
console.log(inspection.status);
console.log(inspection.adaptations);
console.log(inspection.diagnostics);For Markdown-aware source previews and captured runtime evidence, see PromptText.
What to Reach For
| Need | Use |
|---|---|
| One model call with typed input/output | prompt() |
| Readable multiline prompt composition | md |
| Reusable instructions or input fields | context() in use |
| Runtime conditional instructions | context({ when }), when(), or match() |
| Durable user or session recall | memory() in use |
| Retrieved evidence and citations | retriever() or grounding() in use |
| Markdown instructions loaded on demand | skill() in use |
| Prompt regression testing | tests or evaluate() |
| Provider-specific prompt tweaks | adapt |
Pick a Topic
PromptText
Compose readable Markdown-oriented prompt text and inspect it from source to Run.
Structured Output
Return provider-compatible, Zod-validated objects.
Execution
Generate and stream with each supported SDK adapter.
Semantic Response Cache
Reuse safe prompt results for semantically similar inputs.
Contexts
Compose reusable instructions, runtime data, tools, and nested capabilities.
API Reference
Full prompt() signature, options, methods, and types.