Packages
@loop-engine/adapter-openai
Overview
@loop-engine/adapter-openai wraps the OpenAI API as a Loop Engine AI actor using response_format: { type: "json_object" } for structured output. Same governance model as adapter-anthropic — identical guard enforcement, same audit trail, drop-in pattern for multi-model loops.
Installation
1npm install @loop-engine/adapter-openai openaiPeer dependencies
1openai ^4.0.0Basic usage
1"cmt">// @no-typecheck2import OpenAI from 'openai'3import { createOpenAIActorAdapter } from '@loop-engine/adapter-openai'4import { createLoopSystem } from '@loop-engine/sdk'5 6const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })7 8const adapter = createOpenAIActorAdapter(openai, {9 modelId: 'gpt-4o',10 confidenceThreshold: 0.75,11})12 13const loopSystem = await createLoopSystem({ loops: [] })14const aggregateId = 'procurement-1'15 16"cmt">// Get the AI actor's decision for the current loop state17const { actor, decision } = await adapter.createSubmission({18 loopId: 'procurement',19 loopName: 'SCM Procurement',20 currentState: 'pending_analysis',21 availableSignals: [22 {23 signalId: 'submit_recommendation',24 name: 'Submit Recommendation',25 description: 'Submit a purchase order recommendation',26 allowedActors: ['ai-agent', 'automation'],27 },28 ],29 instruction: 'Analyze the demand data and recommend whether to issue a purchase order.',30 evidence: { demandForecast: 0.89, currentStock: 42, reorderPoint: 50 },31})32 33"cmt">// Submit to the runtime — guards evaluate after this point34const result = await loopSystem.engine.transition({35 aggregateId,36 transitionId: decision.transitionId,37 actor,38 evidence: decision,39})Configuration reference
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| modelId | string | gpt-4o | OpenAI model to use |
| maxTokens | number | 1024 | Max tokens in response |
| systemPrompt | string | — | Optional system prompt prepended to loop context |
| confidenceThreshold | number | 0.7 | Minimum confidence required (0–1) |
What the adapter produces
The adapter returns an AIAgentActor with:
type: "ai-agent"provider: "openai"modelId— the model usedconfidence— extracted from the model responsepromptHash— SHA-256 of the prompt sent (for audit trail)
Guard enforcement note
The confidence guard runs at the runtime level — not inside the adapter. If you configure a confidence-threshold guard on the transition, the runtime will block the transition if the model's confidence falls below the threshold regardless of what the adapter returns.
1"cmt">// In your loop definition — this guard is structural, not prompt-based2{3 guardId: 'confidence-threshold',4 severity: 'hard',5 evaluatedBy: 'runtime',6 parameters: { threshold: 0.75 },7}Multi-model loops
Both adapters return the same AIAgentActor shape. You can use Claude for one transition and GPT-4o for another in the same loop — the runtime treats them identically. The provider and modelId fields on the actor record distinguish them in the audit trail.
1"cmt">// @no-typecheck2"cmt">// Claude handles analysis, GPT-4o handles summarization — same loop3const claudeActor = await claudeAdapter.createSubmission({ ... })4const openaiActor = await openaiAdapter.createSubmission({ ... })Error handling
The adapter throws ActorDecisionError with one of these codes:
INVALID_SIGNAL— model returned a signalId not inavailableSignalsINVALID_CONFIDENCE— confidence value outside 0–1 rangePARSE_FAILED— model response could not be parsed as JSONAPI_ERROR— OpenAI API returned an error