OpenAI JS SDK
The official openai package for Node, Bun and Deno. It works with plugboard on both Chat Completions and the Responses API.
Connect
import OpenAI from "openai"
const client = new OpenAI({ baseURL: "http://127.0.0.1:4010/v1", apiKey: process.env.PLUGBOARD_KEY })model is an alias from your aliases option, such as "fast", or any allowed provider/model[#variant].
Chat
const response = await client.chat.completions.create({ model, messages: [{ role: "user", content: "Reply with exactly: pong" }], max_tokens: 1024 })Tool calls
Plugboard returns the call. Your code runs the tool and sends the result back. client.chat.completions.runTools also works.
const tool: OpenAI.ChatCompletionTool = {
type: "function",
function: {
name: "get_weather",
description: "Current weather for a city",
parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
},
}
const messages: OpenAI.ChatCompletionMessageParam[] = [{ role: "user", content: "What is the weather in Paris? Use the tool." }]
const first = await client.chat.completions.create({ model, messages, tools: [tool], max_tokens: 1024 })
const message = first.choices[0]!.message
const call = message.tool_calls![0]!
// Send the assistant turn back as the SDK typed it, then the result under the same call id.
messages.push(message, { role: "tool", tool_call_id: call.id, content: JSON.stringify({ temp_c: 18, sky: "cloudy" }) })
const second = await client.chat.completions.create({ model, messages, tools: [tool], max_tokens: 1024 })Structured output
zodResponseFormat(Meal, "meal") with .parse() or .stream() fills message.parsed:
const Meal = z.object({ calories: z.number(), protein_g: z.number() })
const messages: OpenAI.ChatCompletionMessageParam[] = [{ role: "user", content: "Estimate: 2 boiled eggs and a slice of toast." }]
const parsed = await client.chat.completions.parse({ model, messages, response_format: zodResponseFormat(Meal, "meal_estimate"), max_tokens: 1024 })
const meal = parsed.choices[0]?.message.parsed // a typed Meal, or undefined when the content did not parseResponses API
Text:
const response = await client.responses.create({ model, input: "Reply with exactly: pong", max_output_tokens: 1024 })Tool calls. responsesTool is the tool in the Responses shape (name at the top level). With store: false you send the whole history in input; with the default you can pass previous_response_id instead.
const first = await client.responses.create({ model, input: PROMPT, tools: [responsesTool], store: false, max_output_tokens: 1024 })
const call = first.output.find((item): item is OpenAI.Responses.ResponseFunctionToolCall => item.type === "function_call")
if (!call) throw new Error("no function_call in the answer")
// Store is off, so the whole conversation goes back in input: the answer's items as the SDK typed them, then the result.
// The SDK types output items apart from input items; the wire takes the same objects, so they are cast here.
const input = [
{ role: "user", content: PROMPT },
...(first.output as OpenAI.Responses.ResponseInputItem[]),
{ type: "function_call_output", call_id: call.call_id, output: TOOL_RESULT },
] satisfies OpenAI.Responses.ResponseInputItem[]
const second = await client.responses.create({ model, input, tools: [responsesTool], store: false, max_output_tokens: 1024 })Streaming, from response.output_text.delta events:
const stream = client.responses.stream({ model, input: "Reply with exactly: pong", max_output_tokens: 1024 })
let text = ""
for await (const event of stream) {
if (event.type === "response.output_text.delta") text += event.delta
}
const final = await stream.finalResponse()Images, as a data URL:
const red = readFileSync(new URL("../_shared/red32.dataurl.txt", import.meta.url), "utf8").trim()
const response = await client.responses.create({
model,
input: [
{
role: "user",
content: [
{ type: "input_text", text: "What color is this image? One word." },
{ type: "input_image", image_url: red, detail: "auto" },
],
},
],
max_output_tokens: 1024,
})Structured output with responses.parse and zodTextFormat:
const Meal = z.object({ calories: z.number(), protein_g: z.number() })
const parsed = await client.responses.parse({
model,
input: "Estimate: 2 boiled eggs and a slice of toast.",
text: { format: zodTextFormat(Meal, "meal_estimate") },
max_output_tokens: 1024,
})
const meal = parsed.output_parsed // a typed Meal, or null when the text did not parseclient.responses.retrieve, inputItems.list and delete work on stored responses. See Responses API.
Files
client.files.create({ file: fs.createReadStream("doc.pdf"), purpose: "user_data" }) returns an id you can use in a file part (Chat) or an input_file part (Responses). See Files.
Limits
- Streaming usage needs
stream_options: { include_usage: true }. - The schema in structured output is not enforced by the provider. Validate the answer.
- A wrong key throws
AuthenticationError(401). An unknown model throwsNotFoundError(404).