OpenAI / Codex Adapter
Overview
Mechanism: HTTP POST to OpenAI REST API (/v1/chat/completions) with stream: true.
Why OpenAI as secondary cloud provider:
- Tenant choice: some tenants may have existing OpenAI contracts or preferences
- GPT-4o is a capable alternative to Claude Sonnet for most copywriting tasks
- Codex / o1 models are purpose-built for code generation — useful if agents need to write scripts or automation code
- Fallback when Claude API has outages
When to use:
- Any creative or analysis task as an alternative to Claude (tenant-configured)
- Code generation tasks (Codex/o1 models)
- Tenants who have opted for OpenAI-primary
Configuration
interface OpenAIAdapterConfig {
model: string; // e.g. 'gpt-4o', 'gpt-4o-mini', 'o1', 'codex'
baseUrl?: string; // default: https://api.openai.com/v1
env: { OPENAI_API_KEY: string };
timeoutMs: number;
}How Data Flows IN
POST https://api.openai.com/v1/chat/completions
Content-Type: application/json
{
"model": "gpt-4o",
"stream": true,
"messages": [
{ "role": "system", "content": "<system prompt + injected skills>" },
{ "role": "user", "content": "<task prompt>" },
// ... prior conversation turns (for session continuity)
{ "role": "assistant", "content": "<prior assistant reply>" },
{ "role": "user", "content": "<next task>" }
],
"max_tokens": 8000
}The full assembled system prompt (with tenant settings, RAG context, etc.) goes in messages[0]. Skills are prepended to the system message as plain Markdown. Prior conversation turns are appended as user/assistant pairs from sessions.messageHistory.
How Data Flows OUT — SSE Streaming
data: {"id":"chatcmpl-abc","choices":[{"delta":{"content":"Why "},"index":0}]}
data: {"id":"chatcmpl-abc","choices":[{"delta":{"content":"Your "},"index":0}]}
data: {"id":"chatcmpl-abc","choices":[{"delta":{},"finish_reason":"stop"},"index":0}],"usage":{"prompt_tokens":4200,"completion_tokens":1800}}
data: [DONE]The adapter reads the response body as a stream, splits on \n, filters for data: lines, parses each JSON chunk, and accumulates delta.content. Each chunk emits a text_delta event for live UI streaming. [DONE] signals completion.
Session Handling
OpenAI has no native session resumption. Conversation history is maintained manually:
- Stored as
messageHistory: Message[]in the PostgreSQLsessionstable - Each new task appends
{ role: 'user', content: taskPrompt }to the array - After completion, the assistant reply is appended as
{ role: 'assistant', content: result } - When approaching the context limit: older turns are summarised using a cheap model and replaced with the summary
Skills Injection
There is no --add-dir equivalent for OpenAI. Skills content is injected into the prompt directly:
- Skills Markdown content is retrieved from MongoDB
- Full text is concatenated and prepended to the system message:
skillsContent + '\n\n---\n\n' + systemPrompt - All skills content is sent upfront with every request (unlike Claude’s
--add-dirwhich is lazy)
Cost Source
Token counts come from the usage field in the final streaming chunk → MODEL_PRICING[model] × tokens.
Timeout
HTTP fetch with AbortController at timeoutMs. The activity run is marked failed with error: 'timeout' and BullMQ retry policy picks it up.