Migrate from the OpenAI SDK
Moving from the official openai package to ORXA: LLM-SDK is mechanical. You replace
the client construction and the call site; your prompts, tools, and schemas stay the same.
Two paths:
- Rewrite the call sites (this guide) — gets you the unified interface, per-call cost, observability, and agent governance.
- Zero code changes — point your existing OpenAI client at ORXA’s
OpenAI-compatible server and change only the
baseURL.
Install
Section titled “Install”npm install @combycode/llm-sdkBasic completion
Section titled “Basic completion”Before:
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });const res = await client.chat.completions.create({ model: 'gpt-5.5', messages: [{ role: 'user', content: 'Hello' }],});console.log(res.choices[0].message.content);After:
import { complete } from '@combycode/llm-sdk';
const { text } = await complete({ model: 'openai/gpt-5.5', apiKey: process.env.OPENAI_API_KEY, prompt: 'Hello',});console.log(text);The model id becomes openai/<model>. The response is { text, parsed?, response } — no
more reaching into choices[0].message.content. Token counts live on response.usage
(response.usage.inputTokens, response.usage.outputTokens).
System prompt
Section titled “System prompt”Before:
const res = await client.chat.completions.create({ model: 'gpt-5.5', messages: [ { role: 'system', content: 'You are terse.' }, { role: 'user', content: 'Hello' }, ],});After:
const { text } = await complete({ model: 'openai/gpt-5.5', system: 'You are terse.', prompt: 'Hello',});Multi-turn
Section titled “Multi-turn”Pass a Message[] as the prompt:
const { text } = await complete({ model: 'openai/gpt-5.5', prompt: [ { role: 'user', content: 'Hi' }, { role: 'assistant', content: 'Hello!' }, { role: 'user', content: 'What did I just say?' }, ],});For managed conversations (server-state, layered context), see Multi-turn and Conversation state.
Streaming
Section titled “Streaming”Before:
const stream = await client.chat.completions.create({ model: 'gpt-5.5', messages: [{ role: 'user', content: 'Count to 5.' }], stream: true,});for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? '');}After:
import { createLLM } from '@combycode/llm-sdk';
const llm = createLLM({ model: 'openai/gpt-5.5', apiKey: process.env.OPENAI_API_KEY });for await (const ev of llm.stream('Count to 5.')) { if (ev.type === 'text') process.stdout.write(ev.text);}Stream events are a discriminated union (text, thinking, usage, tool_call_start,
done, …). See Streaming.
Function / tool calling
Section titled “Function / tool calling”Before — you define a JSON schema, then manually parse tool_calls, execute, and loop:
const res = await client.chat.completions.create({ model: 'gpt-5.5', messages: [{ role: 'user', content: 'Weather in Paris?' }], tools: [{ type: 'function', function: { name: 'get_weather', description: 'Get weather for a city', parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] }, }, }],});// ...read res.choices[0].message.tool_calls, run them, append results, call againAfter — defineTool carries the executor, and complete() runs the tool loop for you:
import { complete, defineTool } from '@combycode/llm-sdk';
const getWeather = defineTool({ name: 'get_weather', description: 'Get weather for a city', params: { city: 'string' }, execute: async ({ city }) => `Sunny in ${city}`,});
const { text } = await complete({ model: 'openai/gpt-5.5', prompt: 'Weather in Paris?', tools: [getWeather],});See Single tool call and Multi-step loop.
Structured / JSON output
Section titled “Structured / JSON output”Before:
const res = await client.chat.completions.create({ model: 'gpt-5.5', messages: [{ role: 'user', content: 'Extract name and age: John is 30' }], response_format: { type: 'json_schema', json_schema: { name: 'person', schema: { /* ... */ } } },});const data = JSON.parse(res.choices[0].message.content ?? '{}');After — pass a structured.schema; the parsed object comes back on parsed:
const { parsed } = await complete({ model: 'openai/gpt-5.5', prompt: 'Extract name and age: John is 30', structured: { schema: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' } }, required: ['name', 'age'], }, },});console.log(parsed); // { name: 'John', age: 30 }Note: if the model returns invalid JSON, complete() throws — wrap in try/catch to
retry. See Structured output.
Built-in tools (web search, code interpreter)
Section titled “Built-in tools (web search, code interpreter)”OpenAI’s built-ins pass through verbatim:
const { text } = await complete({ model: 'openai/gpt-5.5', prompt: 'What launched this week?', tools: [{ type: 'web_search' }],});See Web search and Server-side tools.
Zero-code-change path: the drop-in server
Section titled “Zero-code-change path: the drop-in server”If you don’t want to touch your call sites at all, run ORXA’s OpenAI-compatible server and repoint your existing OpenAI client:
const client = new OpenAI({ apiKey: 'your-server-key', baseURL: 'http://localhost:8787/v1', // ORXA server});// every existing chat.completions.create(...) call now flows through ORXAWhat changes, what stays the same
Section titled “What changes, what stays the same”| Official SDK | ORXA | |
|---|---|---|
| Model id | 'gpt-5.5' | 'openai/gpt-5.5' |
| Response text | choices[0].message.content | result.text |
| Token usage | usage.prompt_tokens | response.usage.inputTokens |
| Tool loop | manual | run by complete() |
| Switch provider | new SDK + new shapes | change the model string |
Next: Models & providers · Provider routing & fallback · Cost tracking.