@use-crux/openai
OpenAI SDK adapter, factory pattern with native generation options plus dense embedding helpers.
Peer dependency: openai
import {
createOpenAI,
embedding,
openaiProviderRuntime,
openAITranscript,
toMessages,
fromMessages,
} from "@use-crux/openai";
import { createGenerateObjectFn, createGenerateTextFn } from "@use-crux/openai";For media workflows, start with Media and multimodal,
then use Streaming generated media for
streamImage() and streamSpeech(). For text usage and provider comparison,
see the Execution guide.
Request capacity and counting
The adapter exposes adapter.capacity(model) and the standalone
openAIModelCapacity(model) helper. Capacity lookup is synchronous and never
calls the provider.
import { openAIModelCapacity } from "@use-crux/openai";
const profile = openAIModelCapacity("gpt-4o-mini");| Known model-id prefix | Context window | Default output reserve |
|---|---|---|
gpt-5 | 400,000 | 128,000 |
gpt-4.1 | 1,047,576 | 32,768 |
gpt-4o | 128,000 | 16,384 |
gpt-4-turbo | 128,000 | 4,096 |
gpt-4 | 8,192 | 4,096 |
gpt-3.5-turbo | 16,385 | 4,096 |
o1, o3, o4 | 200,000 | 100,000 |
Prefix matching includes dated aliases. Unknown identifiers use a conservative
16,384-token window with a 4,096-token reserve. Known profiles report
countingConfidence: "estimated"; the fallback reports "conservative".
The adapter does not call a token-count endpoint. Crux estimates the complete
request and applies the margin reported by request inspection. Use
inputBudget when you need a smaller product limit or earlier optimization.
createOpenAI(client)
Create an adapter bound to an OpenAI client.
createOpenAI() is openaiProviderRuntime.create. The package-owned openAITranscript owns message conversion plus assistant text/tool-call extraction; response metadata normalization, stream deltas, and settings/schema mapping stay in this package too. Crux still owns prompt resolution, tool loops, safety, validation retry, memory capture, and observability.
OpenAI Chat content support maps image parts to image_url, wav/mp3 file parts with data sources to input_audio, and supported file data/provider-file sources to OpenAI file parts. Unsupported model/content combinations throw after prompt resolution and before the client or custom transport runs. Assistant/user media returned in provider content arrays decodes back into canonical ContentPart[]. Chat Completions tool messages remain text-only; when a tool returns media, Crux sends the correlated text tool result followed by native media in a user content part on the same next turn.
The package also exports native generateImage(), streamImage(),
transcribe(), generateSpeech(), and streamSpeech() operations.
Translation is selected through the
transcription task union; requested timing/detail must match the selected
endpoint. Returned image/audio assets are usable immediately and are never
persisted implicitly. Typed extra records expose endpoint-specific controls.
These operations also accept portable canonical Safety options: image and
speech expose guardrails/safety, while transcription additionally exposes
one-shot output-text constraints. Provider-native raw values remain
unguarded; see the content-primitive matrix.
| Field | Type | Description |
|---|---|---|
client | OpenAI | The OpenAI client instance |
Returns:
| Method | Description |
|---|---|
.generate(prompt, options) | Execute a prompt via chat.completions.parse (structured) or .create (text) |
.stream(prompt, options) | Stream a prompt execution |
.generateImage(options) | Generate or edit completed images through the native Images API |
.transcribe(options) | Transcribe or translate audio with requested native detail |
.generateSpeech(options) | Generate one completed native speech asset |
.streamImage(options) | Stream genuine Images API preview/final events |
.streamSpeech(options) | Stream genuine Speech API response-body bytes |
.prepare(prompt, options) | Prepare a sans-I/O call handle with OpenAI params |
.retrievalModel(config) | Bind OpenAI generation to core retrieval recipe steps |
.reranker(config) | Create a judge-backed core Reranker |
import { createOpenAI } from "@use-crux/openai";
import OpenAI from "openai";
const adapter = createOpenAI(
new OpenAI({ apiKey: process.env.OPENAI_API_KEY }),
);
const result = await adapter.generate(editDraft, {
model: "gpt-4o",
input: { instruction: "Fix the intro" },
});
result.text;
result.usage;
result.finalStep;Media uses the same call and message list:
import { prompt } from "@use-crux/core";
const describeChart = prompt({ id: "describe-chart" });
await adapter.generate(describeChart, {
model: "gpt-4o",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe this chart." },
{
type: "image",
source: new URL("https://example.com/chart.png"),
providerOptions: { openai: { detail: "high" } },
},
],
},
],
});adapter.generate(prompt, options)
| Field | Type | Description |
|---|---|---|
prompt | Prompt | The prompt to execute |
options.model | string | Model name |
options.input | object | Input values |
options.tools | Record<string, unknown>? | Additional Crux tools to merge at call time |
options.toolMiddleware | ToolMiddleware | readonly ToolMiddleware[]? | Tool execution hooks, including Crux resumable approvals through result.messages. |
options.toolApproval | ToolApprovalMap? | Call-site approval policy; exact tool names beat '*'. |
options.toolsContext | Record<string, unknown>? | Required for composed tools that declare contextSchema; values are Zod-validated before the tool loop starts. |
options.runtimeContext | unknown? | Shared per-run context visible to tool execute, middleware, and function-form toolApproval policies. |
options.transport | (params, info) => Promise<ChatCompletion> | BYO wire call. Crux still owns tools, approvals, validation retry, routing, and timeouts. |
options.extra.tools | ChatCompletionTool[]? | OpenAI-native tools that bypass Crux tool conversion |
options.extra.tool_choice | string? | OpenAI-native tool selection |
options.extra.parallel_tool_calls | boolean? | Allow parallel tool calls |
Returns: normalized Crux generate result with:
result.text: extracted assistant textresult.usage: accumulated usage when every provider-call step reported usageresult.cost: provider-reported cost when availableresult.finalStep: final provider-call text, usage, finish reason, response id, and actual model idresult.raw: raw OpenAI SDK responseresult._meta: retained trace metadata for observability plumbing
When the prompt has an output schema, the provider-parsed value remains available on result.raw.choices[0].message.parsed.
adapter.stream(prompt, options)
Stream a prompt execution.
| Field | Type | Description |
|---|---|---|
prompt | Prompt | The prompt to execute |
options.model | string | Model name |
options.input | object | Input values |
options.tools | Record<string, unknown>? | Additional Crux tools to merge at call time |
options.toolMiddleware | ToolMiddleware | readonly ToolMiddleware[]? | Tool execution hooks, including Crux resumable approvals through result.messages. |
options.toolApproval | ToolApprovalMap? | Call-site approval policy; exact tool names beat '*'. |
options.toolsContext | Record<string, unknown>? | Required for composed tools that declare contextSchema; values are Zod-validated before the tool loop starts. |
options.runtimeContext | unknown? | Shared per-run context visible to tool execute, middleware, and function-form toolApproval policies. |
options.extra.tools | ChatCompletionTool[]? | OpenAI-native tools that bypass Crux tool conversion |
options.extra.tool_choice | string? | OpenAI-native tool selection |
options.extra.parallel_tool_calls | boolean? | Allow parallel tool calls |
Returns: StreamResult<TOutput, TPartial>:
result.textStream: published, Safety-final text deltasresult.fullStream: every published logical event in canonical orderresult.partialOutputStream: canonical partialz.inputfor a structured promptresult.completion: promise resolving to the canonical completion envelope (text, optionalobject, optionalusage, optionalcost,steps,finalStep,messages, and pending approvals)result.cancel(reason?): abort the whole logical operation, including the active provider attempt
There is deliberately no raw on a managed stream: a provider stream resolves
before terminal Safety and describes only one attempt, so exposing it would
bypass guardrail holds, structured occurrence gating, commit gates, and
validation retry.
Media operations
Completed and streaming image, transcription, and speech behavior is documented
separately in OpenAI media operations.
That page owns native endpoints, typed extra controls, model gates, terminal
raw types, and stream framing.
toParams(resolved, options) / fromResponse(response)
toParams() converts a ResolvedPrompt plus { model, settings?, extra? }
into OpenAI chat-completion params. ResolvedPrompt does not include a model,
so options.model is required. fromResponse() normalizes an OpenAI response
into Crux AdapterResponse facts.
These codecs are translation-only. Use managed generate()/stream() when
Crux should run tools, approvals, validation retry, memory capture, safety, and
observability.
adapter.prepare(prompt, options)
Prepare a headless OpenAI call. Crux resolves the prompt and returns public
OpenAI params without making the SDK call. Feed a raw OpenAI response back with
finish(response), or use step(response) when tools, validation retry, or
approval suspension may need another provider turn.
generate(prompt, { ...options, transport }) is the BYO-wire mode where Crux
keeps owning the loop and invokes your callback for each OpenAI request.
stream() with transport is intentionally unsupported and rejects with
CruxTransportStreamUnsupportedError.
adapter.retrievalModel(config)
Create a core RetrievalModel from the adapter instance.
const retrievalModel = adapter.retrievalModel({ model: "gpt-4o-mini" });Use it as a recipe-level model or step-level model.
adapter.reranker(config)
Create a core Reranker for rerank({ engine }).
const engine = adapter.reranker({
model: "gpt-4o-mini",
topN: 12,
});OpenAI's direct SDK adapter does not expose a native rerank endpoint. adapter.reranker() uses Crux's judgeReranker() over the configured OpenAI model, so each rerank call spends generation tokens.
toMessages(sdkMessages)
Convert OpenAI ChatCompletionMessageParam[] to canonical Message[].
| Field | Type | Description |
|---|---|---|
sdkMessages | ChatCompletionMessageParam[] | OpenAI SDK messages |
Returns: Message[]
fromMessages(messages)
Convert canonical Message[] to OpenAI ChatCompletionMessageParam[].
| Field | Type | Description |
|---|---|---|
messages | Message[] | Canonical messages |
Returns: ChatCompletionMessageParam[]
Assistant tool calls become OpenAI tool_calls; tool results become tool role messages with tool_call_id. For rich tool output, that correlated tool message keeps the safe text projection and a following user message carries the native media parts.
openAITranscript is the lower-level NativeTranscriptCodec used by createOpenAI(). The public toMessages() and fromMessages() wrappers delegate to it.
createGenerateObjectFn(client)
Create a provider-native GenerateObjectFn bound only to a client. Every call
must supply a non-empty OpenAI model id.
| Field | Type | Description |
|---|---|---|
client | OpenAI | OpenAI client instance |
Returns: GenerateObjectFn
const generateObject = createGenerateObjectFn(client);
const { object } = await generateObject({
model: "gpt-4.1-mini",
messages,
schema,
});This helper uses the same native chat profile and media codec as
createOpenAI(). It accepts exactly one of prompt or canonical messages,
uses OpenAI's structured parse surface, and returns { object }. It does not
run Crux prompt resolution, validation retry, safety, tools, memory capture, or
instrumentation.
createGenerateTextFn(client, model)
Create a GenerateTextFn bound to a client and model.
| Field | Type | Description |
|---|---|---|
client | OpenAI | OpenAI client instance |
model | string | Model name |
Returns: GenerateTextFn
This helper is also generated from the native chat profile, so text helper calls and adapter calls share request construction and response extraction.
embedding(client, config)
Create a dense Crux embedding backed by client.embeddings.create().
import OpenAI from "openai";
import { embedding } from "@use-crux/openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const docsEmbedding = embedding(client, {
name: "docs-embedding",
model: "text-embedding-3-small",
});Known OpenAI embedding models infer their default dimensions automatically. For custom model IDs, pass dimensions explicitly.
The hosted OpenAI embedding surface is text-only. The helper declares
modalities: ['text']; media input throws EmbeddingModalityError before an
OpenAI client call.
| Field | Type | Description |
|---|---|---|
client | OpenAI | OpenAI client instance |
config.name | string | Stable embedding identifier |
config.model | string | OpenAI embedding model |
config.dimensions | number? | Explicit vector dimensionality. Required for unknown/custom model IDs. |
config.version | string? | Extra vector-semantic revision for cache invalidation. |
config.maxInputTokens | number? | Per-input token ceiling. Defaults to 8192. |
config.batch.maxSize | number? | Top-level Crux batch size. Defaults to 100. |
config.batch.concurrency | number? | Top-level Crux batch concurrency. Defaults to 1. |
config.user | string? | Optional OpenAI end-user identifier |
The helper fingerprint always includes the model, then appends version when
supplied; resolved dimensions participate through the core embedding
fingerprint. A user version never replaces model identity, so either change
invalidates cached vectors.
Types
import type {
OpenAIChatRequest,
OpenAIEmbeddingConfig,
OpenAIExtra,
} from "@use-crux/openai";Related
- Guide: Execution
- Guide: Embeddings
- Reference: Prompts
- Reference: @use-crux/ai (Vercel AI SDK)