@memoflowio/memoflow-openai
v0.1.2
Published
Durable budget and invocation adapter for the OpenAI Responses API
Readme
@memoflowio/memoflow-openai
Optional OpenAI Responses API adapter for server-backed memoflow activities. It reserves server-authoritative budget capacity before provider I/O, records the provider-neutral invocation ledger, normalizes terminal token usage, and settles actual usage by invocation identity.
The published package accepts an injected official OpenAI client and declares
no runtime or peer dependency on openai, so the core SDK remains
provider-independent. This repository pins the official SDK only as a
development dependency for compile-time compatibility and opt-in live
certification.
The adapter targets the current OpenAI Responses API and consumes the documented terminal streaming lifecycle events.
import OpenAI from 'openai';
import { createOpenAIResponsesAdapter } from '@memoflowio/memoflow-openai';
const openai = createOpenAIResponsesAdapter(new OpenAI());
const response = await openai.run({
request: { model: 'your-approved-model', input: 'Hello' },
budget: { totalTokens: 2_000, modelCalls: 1 },
recordedReplay: { key: 'primary-model-turn' },
});Function calls can be executed as versioned, digest-only durable tool effects:
const { response, toolResults } = await openai.runWithTools({
request: {
model: 'your-approved-model',
input: 'What is the weather?',
tools: [{ type: 'function', name: 'lookup_weather' }],
},
budget: { totalTokens: 2_000, modelCalls: 1, toolCalls: 1 },
// One conservative reservation for each allowed continuation request.
continuationBudgets: [
{ totalTokens: 2_000, modelCalls: 1, toolCalls: 0 },
],
recordedReplay: { key: 'primary-model-turn' },
}, {
lookup_weather: {
version: 'v1',
execute: async (args, call) => weather.lookup(args, {
idempotencyKey: call.idempotencyKey,
}),
},
});runWithTools executes Responses API function-call batches. It records
argument and result digests, never their plaintext, but returns tool results to
the activity. When continuationBudgets is present, it preserves every model
output item—including reasoning items—appends the corresponding
function_call_output values, and continues until the model stops requesting
functions or the admitted budgets are exhausted. Each provider request gets a
separate reservation and durable invocation; each budget must reserve exactly
one model call, and the full interaction is capped at 32 calls. Exhaustion
throws OpenAIToolRoundLimitError after the last admitted call has been
settled, never by silently issuing an unbudgeted request.
With an explicit stable recordedReplay.key, the adapter stores
the terminal response and tool results in the run's protected durable-output
store after the invocation and budget have settled. If the worker dies before
activity completion, a retry restores that result without reserving budget or
calling the provider/tool again. The envelope is bound to the complete initial
Responses request and the registered executor names/versions; reusing a key
for a changed model, prompt, parameters, tools, or executor version fails
before budget admission.
The key must be stable and unique among calls made by the same activity type
within a workflow run. Restoration uses WorkerService.ReadActivityOutput,
which derives namespace, workflow and run from the current activity task token.
It is authorized by worker.connect; the built-in least-privilege workload
role does not need—and must not receive—operator workflow.read permission.
runWithTools requires recordedReplay.key before it reserves budget or calls
the provider. Every executor receives an opaque idempotencyKey that is stable
across retries of the same logical activity and different across workflow runs.
It also binds the replay key, tool ordinal, name, executor version and argument
digest, so changed tool input cannot reuse the previous effect key. The
executor must forward this key to its external system (or implement an atomic
deduplication table) for the guarantee to cross the process boundary.
This closes the tool-effect uncertainty window only when the external tool honors the supplied key. The provider-call-to-ledger window remains explicit: the Responses API reference does not currently establish a provider idempotency guarantee, so this adapter does not synthesize an undocumented header. Hard budgets and reconciliation remain required.
Pass signal on the adapter input to cancel provider I/O. Cancellation that
is already visible before reservation consumes no budget. If it wins after an
invocation starts but before responses.create is entered, the invocation is
recorded as cancelled and the reservation is released with
PROVIDER_CALL_NOT_STARTED evidence. Once provider dispatch begins, an abort
is classified with finish reason cancelled, but the invocation and budget
remain OUTCOME_UNKNOWN/RECONCILE: aborting the client does not prove that
the provider performed no work.
Executor exceptions are conservatively recorded as OUTCOME_UNKNOWN. Throw
OpenAIToolKnownFailure only when no external side effect occurred (for
example, deterministic local validation) and the tool can safely be recorded
as FAILED.
If the provider call throws after responses.create() is entered, the adapter
marks both invocation and reservation OUTCOME_UNKNOWN/RECONCILE. It does
not assume the request was never sent. A failure before provider dispatch
releases the reservation with PROVIDER_CALL_NOT_STARTED evidence.
Live certification
The repository includes an opt-in certification against the official OpenAI SDK and Responses API. It is never part of pull-request or push CI and refuses to run unless the worktree is clean and all three inputs are explicit:
$env:MEMOFLOW_OPENAI_LIVE = "1"
$env:MEMOFLOW_OPENAI_MODEL = "your-exact-approved-model-id"
$env:OPENAI_API_KEY = "your-project-key"
pnpm check:openai-liveThe four certified boundaries are a terminal response, terminal streaming,
one function-call continuation, and post-dispatch cancellation. The last case
must remain OUTCOME_UNKNOWN / RECONCILE; client cancellation is never
treated as proof that the provider did no work. The runner makes five bounded
provider calls, disables SDK retries, and writes
target/openai-live/certification.json.
Downloaded evidence can be checked without provider access:
pnpm check:openai-live-evidenceEvidence contains the exact source commit, model and official SDK version,
case durations, usage counters, hashed provider response IDs, and normalized
ledger states. It contains no prompts, response text, tool arguments/results,
request IDs, API key, or raw provider object IDs. The GitHub workflow is
manual-only and targets the protected openai-live environment so repository
operators can require approval and scope the API key as an environment secret.
