Rulvar API reference / @rulvar/openai
@rulvar/openai
First-class adapter for the OpenAI Responses API (reasoning items, strict json_schema outputs), plus openaiCompatible, the factory that points the same adapter at any OpenAI-compatible endpoint (Ollama, vLLM, gateways) with an explicit id and baseURL. Models are addressed as 'openai:<model>' in routing.
Part of Rulvar, an embeddable TypeScript engine for durable, budget-bounded multi-agent LLM workflows, where a completed LLM call is never paid for twice. Full documentation: docs.rulvar.com.
Install
bash
pnpm add @rulvar/core @rulvar/openaiThe umbrella package @rulvar/rulvar already bundles this adapter.
Documentation
License
Classes
| Class | Description |
|---|---|
| OpenAiIdMap | Bijective canonical-to-wire (call_*) id map. |
Interfaces
| Interface | Description |
|---|---|
| ComponentDelta | One (model, component) line of the reconciliation. |
| OpenAiAdapterOptions | - |
| OpenAiClientLike | The client sub-surface the adapter consumes; injectable for tests. |
| OpenAiCompatibleConfig | - |
| OpenAiModelInfo | - |
| ReconcileStatementOptions | - |
| StatementCategoryRow | One per-model per-component total: the Spend categories shape. |
| StatementColumnMap | Column mapping for statementFromRows: each field names the KEY in the caller's raw rows that carries the value. Provider export formats change without notice and differ per tenant surface (CSV headers, JSON field names, locale-shaped numbers), so this module deliberately ships NO per-provider schema knowledge: the caller states the mapping in one place and the normalizer applies one fail-closed validation to whatever the export actually contained, naming the row and the column of anything that cannot be evidence. |
| StatementCoverage | - |
| StatementReconciliation | - |
| StatementRequestRow | One normalized per-request row of a usage/billing export. usd is the row's billed dollars where the export carries amounts; componentsUsd its per-component split where it carries one; usage the provider-reported token counts where it carries those. A row must carry at least one of the three, and every row needs the provider's response id, the join key. |
| V1190CacheAudit | One journal's sidecar reconciliation; see auditV1190CacheJournal. |
Type Aliases
| Type Alias | Description |
|---|---|
| BillingComponent | The four billing components a provider statement itemizes. |
| OpenAiSdkOptions | Official SDK construction options forwarded verbatim to new OpenAI(...), minus maxRetries: Rulvar owns retries and wall-clock, so SDK autoretries stay disabled no matter what is passed here. This is the production surface for auth beyond a plain API key, workloadIdentity federation included, plus fetch, timeout, and defaultHeaders. The SDK's own rules still apply inside it, e.g. sdkOptions.apiKey and sdkOptions.workloadIdentity are mutually exclusive and rejected typed at construction. |
| ProviderStatement | A normalized provider export: never a headline total. |
| ResponsesStreamEvent | Raw Responses SSE events, structurally typed. |
Variables
| Variable | Description |
|---|---|
| CONSERVATIVE_COMPATIBLE_CAPS | Gateways cannot be introspected reliably: when caps are not supplied the factory assumes the most conservative capability set. Callers SHOULD supply caps for anything beyond it; the window and output floors here are deliberately small so an unprobed endpoint is never overcommitted. Absent pricing is legitimate for local models: they surface as unpriced in CostReport. |
| OPENAI_MODELS | Static seed table of the current model set. |
| OPENAI_PRICING | The seed pricing rows as a versioned price table, keyed by full ModelRef under the adapter's fixed id 'openai' (long-context tiers included; the 'gpt-5.6' alias carries the same row as its Sol target). Pass it to createEngine({ pricing }) so the run journals a concrete pricingVersion instead of 'unpriced': the versioned table wins over the caps fallback by rule, and a later table revision surfaces as explicit configuration drift on resume rather than a silent reinterpretation. |
Functions
| Function | Description |
|---|---|
| auditV1190CacheJournal | Folds a journal twice with the SAME price function: once as recorded and once with every affected OpenAI usage passed through undoV1190CacheDoubleCount, returning both totals and the affected entry count. An entry (or per-model slice) counts as affected when it was served by the openai adapter, carries cache writes, and has no usageSemantics stamp; stamped entries are already correct and fold identically in both totals. The journal itself is never touched. recordedUsd - correctedUsd is the exact overcharge IF the journal was recorded by v1.19.0; for a v1.20.0 journal the same shape folds to a smaller correctedUsd that does NOT correspond to any real charge, so version provenance stays the caller's responsibility. |
| buildChatCompletionsParams | The Chat Completions degraded path: delta-patched chunk assembly instead of typed SSE, nested function tools with explicit strict where supported, response_format instead of text.format, no reasoning item replay. Selected by caps (api: 'chat'), visible in events, never silent. |
| buildResponsesParams | Builds Responses API params. Manual item replay ONLY: store: false plus include reasoning.encrypted_content; previous_response_id and the Conversations API place state server-side, break replay identity, and are REJECTED as a typed ConfigError. Role 'system' messages project into top-level instructions on every request. |
| mapChatCompletionsStream | Delta-patched chunk assembly for the degraded path; yields each canonical event as its chunk is consumed (same live-streaming contract as mapResponsesStream). |
| mapOpenAiEffort | Canonical-to-wire effort: low through xhigh pass through. Canonical max passes through unchanged on models whose caps declare wire max support (the whole GPT-5.6 family, each sibling verified live 2026-07-18; v1.20.0 review P2-3); elsewhere it downmaps to xhigh (documented lossy; recorded in providerMetadata). Provider 'none' is reachable only via providerOptions.openai.reasoningEffort. |
| mapResponsesStream | Maps the typed Responses SSE stream to ChatEvents, yielding each canonical event AS the corresponding provider event is consumed: the consumer's pull drives the provider read (natural backpressure, no buffering, no detached work). Canonical parts come from the typed output array, never the output_text aggregate. Raw output items ride finish.providerMetadata.openai.outputItems so the runtime can retain reasoning items as provider-raw parts. |
| normalizeOpenAiUsage | Normalizes Responses usage into the canonical Usage invariant, where inputTokens is the FULL prompt. On the OpenAI wire input_tokens is ALREADY that full count: input_tokens_details.cached_tokens and input_tokens_details.cache_write_tokens (GPT-5.6 and later families) are priced SUBSETS of it, never additional tokens, so both pass through untouched and nothing is added. Verified on the live wire 2026-07-18: two identical long prompts report the SAME input_tokens while the details flip from write to read, and total_tokens equals input_tokens + output_tokens on both calls. Adding writes on top (the v1.19.0 reading of the field) double-billed every written token at 1x + 1.25x and inflated budget debits (v1.19.0 review P1-1). Contrast with the Anthropic adapter, whose wire genuinely EXCLUDES both cache counts from input_tokens, so that adapter adds them; the two wires differ, the canonical Usage invariant does not. |
| openai | @rulvar/openai: the first-class OpenAI Responses API adapter with the Chat Completions degraded path, plus the openaiCompatible factory for Ollama, vLLM, and gateways. |
| openaiCompatible | Creates a Chat Completions dialect adapter for a compatible endpoint. |
| openAiErrorToWire | Projects SDK/API errors into the retryable WireError vocabulary. |
| openAiModelInfo | - |
| reconcileStatement | Reconciles the invoice against a normalized provider export. Pure and journal-free; see the module doc for the contract. Throws a typed ConfigError on inputs that cannot be evidence: an empty statement (a headline total with no rows), a request row without a response id, a duplicate response id on either side (an ambiguous join, statement rows and local invoice rows alike, RV1804), a request export whose rows carry neither dollars, components, nor usage, any non-finite or negative dollar amount, any non-integer or negative token count, a non-finite or negative tolerance (RV903: a statement that cannot be summed must refuse loudly, never verdict 'match' on NaN totals), or a row whose usd and componentsUsd contradict each other beyond totalToleranceUsd (RV1005: an internally contradictory export is not evidence either). |
| statementFromRows | Normalizes raw keyed rows (a parsed CSV, a JSON export) into a ProviderStatement under one explicit StatementColumnMap (RV1703). Fail-closed at the cell: a mapped column whose value cannot be evidence (a non-numeric dollar figure, a fractional or negative token count, an empty response id, an unknown component name) refuses typed with the row index and column name instead of flowing a NaN or a guess into the reconciliation. Absent cells (missing key, null, empty string) mean "the export does not carry this figure" and simply omit the field; a requests row that ends up carrying no dollars, no component split, and no usage at all is refused, because a row without evidence cannot reconcile anything. |
| undoV1190CacheDoubleCount | The exact inverse of the v1.19.0 double count for one usage: subtracts cacheWriteTokens back out of inputTokens and leaves every other field untouched. A usage without cache writes is returned unchanged (v1.19.0 recorded those correctly). Throws a typed ConfigError when the arithmetic cannot be the v1.19.0 shape (the recorded input has no room for the subtraction), which means the usage was NOT recorded by the affected adapter; do not guess. |