pipedog is an unofficial community port of Pipecat, not affiliated with Daily.
pipedog
LLM

OpenAI Responses

OpenAI's Responses API with OpenAIResponsesLLMService (WebSocket) and OpenAIResponsesHttpLLMService (HTTP), including reasoning models.

Overview

The Responses API is OpenAI's newer interface for text generation, tools and reasoning models. pipedog ports both of Pipecat's Responses services:

  • OpenAIResponsesLLMService: keeps a WebSocket open to wss://api.openai.com/v1/responses. It sends only the new input items with previous_response_id when it can, which lowers latency in long, tool-heavy conversations. This is the recommended variant for voice.
  • OpenAIResponsesHttpLLMService: the same service over HTTP streaming, through the OpenAI SDK. It sends the full context on every turn.

Both store nothing on OpenAI's side (store: false). For Chat Completions, see OpenAI.

Installation

bun add @pipedog/openai
import {
  OpenAIResponsesHttpLLMService,
  OpenAIResponsesLLMService,
  OpenAIResponsesReasoningConfig,
} from "@pipedog/openai/responses/llm";

Prerequisites

  • An OpenAI account and an API key from the OpenAI dashboard.
  • Pass the key as apiKey. If you leave it out, both services read OPENAI_API_KEY. The examples read it from OPENAI_API_KEY.

OpenAIResponsesLLMService

Configuration

Prop

Type

Settings

Runtime-updatable settings, passed as settings: new OpenAIResponsesLLMService.Settings({...}) (OpenAIResponsesLLMSettings). Both services share them. Any setting you leave out keeps the default below. Fields marked "not sent" are left out of the request, so OpenAI's own default applies.

SettingTypeDefaultDescription
modelstring"gpt-4.1"Model.
systemInstructionstring | nullnullSent as instructions.
temperaturenumbernot sentSampling temperature.
topPnumbernot sentNucleus sampling.
maxCompletionTokensnumbernot sentSent as max_output_tokens.
reasoningOpenAIResponsesReasoningConfig | nullnullReasoning config, for reasoning models. See below.
frequencyPenalty, presencePenalty, seed, topK, maxTokensnumber | nullnullNot part of the Responses request.
filterIncompleteUserTurnsbooleanfalseFilter incomplete user turns.
userTurnCompletionConfigUserTurnCompletionConfig | nullnullTurn completion config used with filterIncompleteUserTurns.
extraRecord<string, unknown>{}Extra request fields, merged in after the fields above.

OpenAIResponsesReasoningConfig (also OpenAIResponsesLLMService.ReasoningConfig) takes:

FieldTypeDefaultDescription
effort"none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | string | nullnullReasoning effort. null leaves it to the model.
summary"auto" | "concise" | "detailed" | string | nullnullReasoning summary to return. null asks for none.
mode"standard" | "pro" | string | nullnullReasoning mode, for models that have one.

Usage

responses.ts
import {
  OpenAIResponsesHttpLLMService,
  OpenAIResponsesLLMService,
  OpenAIResponsesReasoningConfig,
} from "@pipedog/openai/responses/llm";

const llm = new OpenAIResponsesLLMService({
  apiKey: process.env.OPENAI_API_KEY ?? "",
  settings: new OpenAIResponsesLLMService.Settings({
    model: "gpt-4.1",
    systemInstruction: "You are a helpful assistant in a voice conversation.",
  }),
});

A reasoning model, with a reasoning summary:

const reasoning = new OpenAIResponsesLLMService({
  apiKey: process.env.OPENAI_API_KEY ?? "",
  settings: new OpenAIResponsesLLMService.Settings({
    model: "gpt-5.4-mini",
    reasoning: new OpenAIResponsesReasoningConfig({ effort: "low", summary: "auto" }),
  }),
});

Event handlers

EventDescription
on_completion_timeoutA completion timed out.
on_function_calls_startedThe model asked for one or more function calls.
on_function_calls_cancelledFunction calls were cancelled.
on_connection_errorThe WebSocket failed (WebSocket variant only).

These come from the LLMService and WebsocketLLMService base classes.

Notes

  • Runs a response for each LLMContextFrame, between an LLMFullResponseStartFrame and an LLMFullResponseEndFrame, and streams the reply as LLMTextFrames.
  • wsUrl sets the WebSocket endpoint. baseUrl only affects the HTTP client the service uses for runInference.
  • When the context still starts with what was sent last time, only the new items go out, with previous_response_id. After a reconnect, or when the server no longer has the previous response, the service sends the full context again. It also reconnects and retries when it hits OpenAI's 60 minute connection limit.
  • With reasoning left null, the service sends the lowest effort OpenAI documents for a known reasoning model (for example "none" for gpt-5.4 and gpt-6.1-terra, "low" for o3), for the fastest replies, and logs what it sent. Set useProviderThinkingDefault to keep OpenAI's default. gpt-4.1, the default model, does not reason.
  • Reasoning summaries come out as LLMThoughtStartFrame, LLMThoughtTextFrame and LLMThoughtEndFrame (the assistant aggregator's on_assistant_thought event). The encrypted reasoning is kept in the context and sent back on later turns.
  • serviceTier accepts "auto", "default", "flex", "scale", "fast", "priority" and "ultrafast".

OpenAIResponsesHttpLLMService

Configuration

Prop

Type

Settings

Same as OpenAIResponsesLLMService above.

Usage

responses-http.ts
const http = new OpenAIResponsesHttpLLMService({
  apiKey: process.env.OPENAI_API_KEY ?? "",
  settings: new OpenAIResponsesHttpLLMService.Settings({
    model: "gpt-4.1",
    systemInstruction: "You are a helpful assistant.",
  }),
});

Notes

  • Each turn sends the whole context. Over HTTP, previous_response_id needs store: true, which turns on OpenAI-side conversation storage, so this variant doesn't use it.
  • retryOnTimeout and retryTimeoutSecs work as in OpenAI: one re-issue when the first attempt produces nothing in time.

Runtime notes

RuntimeWorksWhat it needs
Bun✅ yesNothing.
Node.js✅ yesNothing.
Deno✅ yesNothing.
Cloudflare Workers✅ yesNothing.

Generated from pipecat.runtimes and pipecat.compat in packages/openai/package.json: ✅ means the compat smoke test passes on that runtime.

  • OpenAIResponsesHttpLLMService uses the openai JS SDK over fetch, which works on Bun, Node, Deno and Cloudflare Workers.
  • OpenAIResponsesLLMService sends the key as an Authorization header on the WebSocket handshake. Bun, Node and Deno send it from the global WebSocket; on Cloudflare Workers core opens the socket through fetch.
  • Upstream's mixin base _BaseOpenAIResponsesLLMService is a class-factory mixin here (BaseOpenAIResponsesLLMServiceMixin). instanceof _BaseOpenAIResponsesLLMService holds for both services.

pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.

Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).

On this page