Created
July 10, 2025 11:10
-
-
Save jonastemplestein/6e562374ac5689f6bedb8e1831fb0fd6 to your computer and use it in GitHub Desktop.
Cheat sheet for openai responses API typescript types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************************************************************** | |
* OPENAI RESPONSES API – PRACTICAL OVERVIEW (concise engineering primer) | |
* | |
* NOTE: This is a playground file for exploring OpenAI's Responses API types. | |
* You'll need to install: `pnpm add openai ts-essentials` | |
* The imports are intentionally left for hover exploration. | |
* | |
* ── TL;DR ──────────────────────────────────────────────────────────────────────────────────── | |
* 1. You send *input items* ➜ `POST /v1/responses` (or `.stream()` variant). | |
* 2. The server returns *output items* (complete array ↔ non-streaming; chunks ↔ streaming). | |
* 3. Every output item can be echoed back later as an input item – that is how you maintain | |
* conversation state across turns. | |
* 4. A "message" is a special (deeper) structure: it's an input/output item whose `content` | |
* is itself an array of *content items* (text, image, file, …). Think slack thread. | |
* | |
* Key docs: | |
* • REST reference: https://platform.openai.com/docs/api-reference/responses | |
* • Streaming format: https://platform.openai.com/docs/api-reference/responses/streaming | |
**************************************************************************************************/ | |
/* ------------------------------------------------------------------------------------------------- | |
IMPORT THE ACTUAL TYPES – you already have them in node_modules when using the official SDK. | |
(Path may differ if package versions change.) | |
------------------------------------------------------------------------------------------------- */ | |
import type { | |
ResponseCreateParams, // payload you POST | |
Response, // full result (non-streaming) | |
ResponseStreamEvent, // SSE events | |
ResponseOutputItem, // indiv. output element | |
ResponseInputItem // indiv. input element | |
} from "openai/resources/responses/responses.mjs"; | |
import { OpenAI } from "openai"; | |
import type { Prettify } from "ts-essentials"; | |
// Create prettified type aliases for better hover experience | |
export type ResponseCreateParamsPretty = Prettify<ResponseCreateParams>; | |
export type ResponsePretty = Prettify<Response>; | |
export type ResponseStreamEventPretty = Prettify<ResponseStreamEvent>; | |
export type ResponseOutputItemPretty = Prettify<ResponseOutputItem>; | |
export type ResponseInputItemPretty = Prettify<ResponseInputItem>; | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
1. STREAMING RESPONSES | |
------------------------------------------------------------------------------------------------ | |
• Call `client.responses.create({ ..., stream: true })` | |
• Server emits a **Server-Sent Events** stream (`Content-Type: text/event-stream`) | |
• Each event is typed as `ResponseStreamEvent` (union of ~60 variants, see SDK types) | |
• Events of interest while stitching the final answer: | |
- `response.output_item.added` | |
- `response.output_item.done` | |
- `response.completed` / `response.failed` / … | |
• The *union* contains both low-level deltas (token chunks) and high-level milestones. | |
• When `response.completed` arrives you have seen every output item at least once in a | |
`.done`, and the stream closes. | |
------------------------------------------------------------------------------------------------- */ | |
/* Example: minimal pseudo-consumer */ | |
async function _consumeStream() { | |
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); | |
const stream = await client.responses.create({ input: "hello", stream: true }); | |
const outputItems: ResponseOutputItem[] = []; | |
for await (const ev of stream) { | |
if (ev.type === "response.output_item.done") { | |
outputItems[ev.output_index] = ev.item; // collect finalized item | |
} | |
if (ev.type === "response.completed") { | |
console.log("All items final:", outputItems); | |
} | |
} | |
} | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
2. OUTPUT ITEMS | |
------------------------------------------------------------------------------------------------ | |
Definition: `ResponseOutputItem` (union) | |
• assistant message (`type:"message"`) | |
• tool calls (`function_call`, `file_search_call`, `mcp_call`, …) | |
• code interpreter calls, computer use, image generation, … (built-ins) | |
Important rules: | |
• Array order is model-chosen. | |
• An output item can *immediately* be turned into an input item for the next request. | |
(That's what you do to preserve history.) | |
------------------------------------------------------------------------------------------------- */ | |
/* Quick sample */ | |
const _sampleOutput: ResponseOutputItem = { | |
type: "message", | |
id: "msg_123", | |
role: "assistant", | |
status: "completed", | |
content: [{ type: "output_text", text: "Hello!", annotations: [] }] | |
}; | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
3. INPUT ITEMS | |
------------------------------------------------------------------------------------------------ | |
Definition: `ResponseInputItem` (union) – superset that ALSO includes the items we create. | |
• `message` authored by user|system|developer | |
• function_call_output, computer_call_output, … | |
Life cycle: | |
• On turn N you POST an array of input items ➜ OpenAI ➜ outputs turn N. | |
• You merge your own new context + all *relevant* output items from turn N | |
to build input for turn N+1. | |
------------------------------------------------------------------------------------------------- */ | |
/* Turn N+1 built from previous assistant response + new user message */ | |
const _nextTurnInput: ResponseInputItem[] = [ | |
// 1) echo previous assistant message (as ResponseOutputMessage) | |
{ | |
type: "message", | |
id: "msg_prev", | |
role: "assistant", | |
status: "completed", | |
content: [{ type: "output_text", text: "Hello!", annotations: [] }] | |
}, | |
// 2) new user message | |
{ type: "message", role: "user", content: [{ type: "input_text", text: "How are you?" }] } | |
]; | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
4. "MESSAGE" ITEM STRUCTURE | |
------------------------------------------------------------------------------------------------ | |
A message is itself a container: | |
Message | |
↳ content: ContentItem[] | |
ContentItem (aka `ResponseInputContent` / subset of `ResponseOutputText`) can be: | |
• input_text / output_text – plain text | |
• input_image – image with {image_url|file_id, detail} | |
• input_file – arbitrary file blob or file_id | |
So hierarchy = Input|Output Item → Message → Content Items. | |
------------------------------------------------------------------------------------------------- */ | |
/* Example with mixed content */ | |
const _richUserMessage: ResponseInputItem = { | |
type: "message", | |
role: "user", | |
content: [ | |
{ type: "input_text", text: "Summarise this PDF please:" }, | |
{ type: "input_file", file_id: "file_abc123" }, | |
{ type: "input_image", image_url: "https://example.com/chart.png", detail: "auto" } | |
] | |
}; | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
ADVANCED EXAMPLES: Mixed Content Types | |
------------------------------------------------------------------------------------------------ | |
Messages can contain rich combinations of text, images, and files. Here are practical examples: | |
------------------------------------------------------------------------------------------------- */ | |
/* Example 1: Analyzing multiple documents with context */ | |
const _documentAnalysisRequest: ResponseInputItem = { | |
type: "message", | |
role: "user", | |
content: [ | |
{ type: "input_text", text: "Compare these financial reports and highlight key differences:" }, | |
{ type: "input_file", file_id: "file_q1_report_2024" }, | |
{ type: "input_file", file_id: "file_q1_report_2023" }, | |
{ type: "input_text", text: "Pay special attention to revenue growth and operational costs." }, | |
{ type: "input_image", image_url: "data:image/png;base64,iVBORw0KG...", detail: "high" } // Revenue chart | |
] | |
}; | |
/* Example 2: Assistant response with multiple content types and annotations */ | |
const _assistantAnalysisResponse: ResponseOutputItem = { | |
type: "message", | |
id: "msg_analysis_123", | |
role: "assistant", | |
status: "completed", | |
content: [ | |
{ | |
type: "output_text", | |
text: "Based on my analysis of the financial reports:", | |
annotations: [] | |
}, | |
{ | |
type: "output_text", | |
text: "Revenue increased by 23% year-over-year, primarily driven by...", | |
annotations: [ | |
{ | |
type: "file_citation", | |
file_id: "file_q1_report_2024", | |
filename: "Q1_2024_Financial_Report.pdf", | |
index: 0 | |
} | |
] | |
} | |
] | |
}; | |
/* Example 3: Complex user request with inline data and references */ | |
const _dataProcessingRequest: ResponseInputItem = { | |
type: "message", | |
role: "user", | |
content: [ | |
{ type: "input_text", text: "Process this CSV data and create visualizations:" }, | |
{ | |
type: "input_file", | |
file_data: "name,age,score\nAlice,25,92\nBob,30,87\nCarol,28,95", // Inline CSV | |
filename: "scores.csv" | |
}, | |
{ type: "input_text", text: "Use this color scheme for the charts:" }, | |
{ | |
type: "input_image", | |
image_url: "https://example.com/brand-colors.png", | |
detail: "low" // Low detail sufficient for color reference | |
}, | |
{ type: "input_text", text: "Output should match the style of this template:" }, | |
{ type: "input_file", file_id: "file_template_xyz" } | |
] | |
}; | |
/* Example 4: Developer instructions with user content (system context mixing) */ | |
const _contextualRequest: ResponseInputItem[] = [ | |
{ | |
type: "message", | |
role: "developer", | |
content: [ | |
{ | |
type: "input_text", | |
text: "The user will provide images of receipts. Extract amounts and dates." | |
} | |
] | |
}, | |
{ | |
type: "message", | |
role: "user", | |
content: [ | |
{ type: "input_text", text: "Here are my receipts from last month:" }, | |
{ type: "input_image", file_id: "file_receipt_001", detail: "high" }, | |
{ type: "input_image", file_id: "file_receipt_002", detail: "high" }, | |
{ type: "input_image", file_id: "file_receipt_003", detail: "high" }, | |
{ type: "input_text", text: "Please organize by date and calculate total." } | |
] | |
} | |
]; | |
/* Example 5: Conversation with image generation and file outputs */ | |
const _creativeWorkflow: ResponseInputItem[] = [ | |
// User request | |
{ | |
type: "message", | |
role: "user", | |
content: [ | |
{ type: "input_text", text: "Create a logo based on this sketch:" }, | |
{ type: "input_image", image_url: "data:image/jpeg;base64,...", detail: "high" }, | |
{ type: "input_text", text: "Use these brand guidelines:" }, | |
{ type: "input_file", file_id: "file_brand_guide" } | |
] | |
}, | |
// Assistant uses image generation tool (output item) | |
{ | |
type: "image_generation_call", | |
id: "img_gen_001", | |
status: "completed", | |
result: "data:image/png;base64,iVBORw0KG..." // Generated logo | |
}, | |
// Assistant response with the result | |
{ | |
type: "message", | |
role: "assistant", | |
status: "completed", | |
id: "msg_logo_result", | |
content: [ | |
{ | |
type: "output_text", | |
text: "I've created a logo based on your sketch. Here's the result:", | |
annotations: [] | |
}, | |
{ | |
type: "output_text", | |
text: "The logo incorporates the blue (#0066CC) from your brand guidelines...", | |
annotations: [ | |
{ | |
type: "file_citation", | |
file_id: "file_brand_guide", | |
filename: "brand_guidelines.pdf", | |
index: 1 | |
} | |
] | |
} | |
] | |
} | |
]; | |
/* Example 6: Error handling with mixed content */ | |
const _errorScenario: ResponseOutputItem = { | |
type: "message", | |
id: "msg_error", | |
role: "assistant", | |
status: "completed", | |
content: [ | |
{ | |
type: "output_text", | |
text: "I was able to process the first document successfully:", | |
annotations: [] | |
}, | |
{ | |
type: "refusal", | |
refusal: | |
"I cannot process the second file as it appears to contain sensitive personal information that I'm not authorized to handle." | |
}, | |
{ | |
type: "output_text", | |
text: "Would you like me to proceed with just the first document's analysis?", | |
annotations: [] | |
} | |
] | |
}; | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
5. FILES | |
------------------------------------------------------------------------------------------------ | |
• Upload separately: `POST /v1/files` ⇒ response `{ id: "file_..." }` | |
• Use the file by reference: supply `file_id` inside `input_file`, or embed in | |
a vector store and let `file_search` tool retrieve chunks. | |
• Vector Store: file ➜ embeddings ➜ vector store id ➜ search via built-in tool. | |
------------------------------------------------------------------------------------------------- */ | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
6. IMAGES | |
------------------------------------------------------------------------------------------------ | |
• Similar pattern: | |
- Provide `input_image` with `image_url` (fully qualified URL or data-URI) **or** | |
an uploaded `file_id`. | |
• `detail`: "auto" | "low" | "high" influences Vision models' behaviour / cost. | |
------------------------------------------------------------------------------------------------- */ | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
7. TOOL CALLING | |
------------------------------------------------------------------------------------------------ | |
• When the model decides to invoke a tool it emits an OUTPUT item (e.g. `function_call`). | |
• For most built-in tools (web_search, file_search, image_generation, …) OpenAI | |
runs the tool internally and immediately follows up with a finalised item containing | |
the result. | |
• For **function tools** (your local code) YOU must: | |
1) detect the output item (arguments are JSON string). | |
2) execute the function locally. | |
3) POST another *input item* of type `function_call_output` with `call_id` + | |
JSON-stringified `output`. | |
• Tool registration happens per-request via the `tools` array in `ResponseCreateParams`. | |
------------------------------------------------------------------------------------------------- */ | |
/* Example round-trip for a function tool */ | |
const _functionCallOutput: ResponseInputItem.FunctionCallOutput = { | |
type: "function_call_output", | |
call_id: "call_xyz", | |
output: JSON.stringify({ result: 42 }) | |
}; | |
/* ──────────────────────────────────────────────────────────────────────────────────────────────── | |
8. QUICK START SNIPPET | |
------------------------------------------------------------------------------------------------ | |
const res = await client.responses.create({ | |
model: "gpt-4o", | |
input: [ | |
{ role: "user", type: "message", content: [{ type:"input_text", text:"Hi!" }] } | |
], | |
tools: [{ type: "function", name: "myCalculator", parameters:{ type: "object" } }] | |
}); | |
console.log(res.output); // If not streaming | |
------------------------------------------------------------------------------------------------- */ | |
/* | |
### How to explore further | |
1. `open openai/resources/responses/responses.d.mts` in your editor – all referenced types are there. | |
2. Hover the imported names in the cheat-sheet to inspect definitions. | |
3. Replace the sample payloads with your own and rely on TypeScript to guide you. | |
--- | |
#### Reference tree (visual aid) | |
``` | |
Response (REST) / Stream | |
└─ output: ResponseOutputItem[] | |
├─ Message (assistant) ← contains ContentItem[] | |
│ └─ ContentItem (text|img|file) | |
├─ FunctionToolCall ← you run locally ➜ FunctionCallOutput (input) | |
├─ FileSearchToolCall ← built-in | |
├─ WebSearchToolCall ← built-in | |
└─ …other tool-related items… | |
Input (next turn) mirrors this: | |
└─ ResponseInputItem[] | |
├─ Message (user|system|dev) | |
├─ FunctionCallOutput | |
├─ …etc | |
``` | |
That's all you need to keep the moving pieces straight – happy hacking! | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment