Skip to content

Commit f3c6ba8

Browse files
Merge pull request #1506 from openai/release-please--branches--master--changes--next--components--openai
release: 4.103.0
2 parents 1bd29ac + 00b63f4 commit f3c6ba8

File tree

16 files changed

+196
-166
lines changed

16 files changed

+196
-166
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "4.102.0"
2+
".": "4.103.0"
33
}

.stats.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 111
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6af14840a810139bf407013167ce1c8fb21b6ef8eb0cc3db58b51af7d52c4b5a.yml
3-
openapi_spec_hash: 3241bde6b273cfec0035e522bd07985d
4-
config_hash: 7367b68a4e7db36885c1a886f57b17f6
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fc64d7c2c8f51f750813375356c3f3fdfc7fc1b1b34f19c20a5410279d445d37.yml
3+
openapi_spec_hash: 618285fc70199ee32b9ebe4bf72f7e4c
4+
config_hash: c497f6b750cc89c0bf2eefc0bc839c70

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 4.103.0 (2025-05-22)
4+
5+
Full Changelog: [v4.102.0...v4.103.0](https://github.com/openai/openai-node/compare/v4.102.0...v4.103.0)
6+
7+
### Features
8+
9+
* **api:** new streaming helpers for background responses ([1ddd6ff](https://github.com/openai/openai-node/commit/1ddd6ff182b09d696954fda4bde50fb82f1d6585))
10+
311
## 4.102.0 (2025-05-21)
412

513
Full Changelog: [v4.101.0...v4.102.0](https://github.com/openai/openai-node/compare/v4.101.0...v4.102.0)

api.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,6 @@ Types:
692692
- <code><a href="./src/resources/responses/responses.ts">ResponseRefusalDoneEvent</a></code>
693693
- <code><a href="./src/resources/responses/responses.ts">ResponseStatus</a></code>
694694
- <code><a href="./src/resources/responses/responses.ts">ResponseStreamEvent</a></code>
695-
- <code><a href="./src/resources/responses/responses.ts">ResponseTextAnnotationDeltaEvent</a></code>
696695
- <code><a href="./src/resources/responses/responses.ts">ResponseTextConfig</a></code>
697696
- <code><a href="./src/resources/responses/responses.ts">ResponseTextDeltaEvent</a></code>
698697
- <code><a href="./src/resources/responses/responses.ts">ResponseTextDoneEvent</a></code>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env -S npm run tsn -T
2+
3+
import OpenAI from 'openai';
4+
5+
const openai = new OpenAI();
6+
7+
async function main() {
8+
const runner = openai.responses.stream({
9+
model: 'gpt-4o-2024-08-06',
10+
input: 'solve 8x + 31 = 2',
11+
background: true,
12+
});
13+
14+
let id: string | null = null;
15+
16+
for await (const event of runner) {
17+
if (event.type == 'response.created') {
18+
id = event.response.id;
19+
}
20+
21+
console.log('event', event);
22+
if (event.sequence_number == 10) {
23+
break;
24+
}
25+
}
26+
27+
console.log('Interrupted. Continuing...');
28+
29+
const runner2 = openai.responses.stream({
30+
response_id: id!,
31+
starting_after: 10,
32+
});
33+
34+
for await (const event of runner2) {
35+
console.log('event', event);
36+
}
37+
38+
const result = await runner2.finalResponse();
39+
console.log(result);
40+
}
41+
42+
main();

jsr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openai/openai",
3-
"version": "4.102.0",
3+
"version": "4.103.0",
44
"exports": {
55
".": "./index.ts",
66
"./helpers/zod": "./helpers/zod.ts",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openai",
3-
"version": "4.102.0",
3+
"version": "4.103.0",
44
"description": "The official TypeScript library for the OpenAI API",
55
"author": "OpenAI <[email protected]>",
66
"types": "dist/index.d.ts",

src/lib/ResponsesParser.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { OpenAIError } from '../error';
22
import type { ChatCompletionTool } from '../resources/chat/completions';
33
import {
4+
ResponseTextConfig,
45
type FunctionTool,
56
type ParsedContent,
67
type ParsedResponse,
@@ -20,7 +21,9 @@ export type ResponseCreateParamsWithTools = ResponseCreateParamsBase & {
2021
tools?: ParseableToolsParams;
2122
};
2223

23-
export type ExtractParsedContentFromParams<Params extends ResponseCreateParamsWithTools> =
24+
type TextConfigParams = { text?: ResponseTextConfig };
25+
26+
export type ExtractParsedContentFromParams<Params extends TextConfigParams> =
2427
NonNullable<Params['text']>['format'] extends AutoParseableTextFormat<infer P> ? P : null;
2528

2629
export function maybeParseResponse<

src/lib/responses/EventTypes.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {
2424
ResponseOutputItemDoneEvent,
2525
ResponseRefusalDeltaEvent,
2626
ResponseRefusalDoneEvent,
27-
ResponseTextAnnotationDeltaEvent,
2827
ResponseTextDeltaEvent as RawResponseTextDeltaEvent,
2928
ResponseTextDoneEvent,
3029
ResponseIncompleteEvent,
@@ -68,7 +67,6 @@ export type ParsedResponseStreamEvent =
6867
| ResponseOutputItemDoneEvent
6968
| ResponseRefusalDeltaEvent
7069
| ResponseRefusalDoneEvent
71-
| ResponseTextAnnotationDeltaEvent
7270
| ResponseTextDeltaEvent
7371
| ResponseTextDoneEvent
7472
| ResponseWebSearchCallCompletedEvent

src/lib/responses/ResponseStream.ts

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
ResponseTextConfig,
23
type ParsedResponse,
34
type Response,
45
type ResponseCreateParamsBase,
@@ -10,12 +11,40 @@ import { APIUserAbortError, OpenAIError } from '../../error';
1011
import OpenAI from '../../index';
1112
import { type BaseEvents, EventStream } from '../EventStream';
1213
import { type ResponseFunctionCallArgumentsDeltaEvent, type ResponseTextDeltaEvent } from './EventTypes';
13-
import { maybeParseResponse } from '../ResponsesParser';
14+
import { maybeParseResponse, ParseableToolsParams } from '../ResponsesParser';
15+
import { Stream } from 'openai/streaming';
1416

15-
export type ResponseStreamParams = Omit<ResponseCreateParamsBase, 'stream'> & {
17+
export type ResponseStreamParams = ResponseCreateAndStreamParams | ResponseStreamByIdParams;
18+
19+
export type ResponseCreateAndStreamParams = Omit<ResponseCreateParamsBase, 'stream'> & {
1620
stream?: true;
1721
};
1822

23+
export type ResponseStreamByIdParams = {
24+
/**
25+
* The ID of the response to stream.
26+
*/
27+
response_id: string;
28+
/**
29+
* If provided, the stream will start after the event with the given sequence number.
30+
*/
31+
starting_after?: number;
32+
/**
33+
* Configuration options for a text response from the model. Can be plain text or
34+
* structured JSON data. Learn more:
35+
*
36+
* - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
37+
* - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
38+
*/
39+
text?: ResponseTextConfig;
40+
41+
/**
42+
* An array of tools the model may call while generating a response. When continuing a stream, provide
43+
* the same tools as the original request.
44+
*/
45+
tools?: ParseableToolsParams;
46+
};
47+
1948
type ResponseEvents = BaseEvents &
2049
Omit<
2150
{
@@ -52,7 +81,7 @@ export class ResponseStream<ParsedT = null>
5281
): ResponseStream<ParsedT> {
5382
const runner = new ResponseStream<ParsedT>(params as ResponseCreateParamsStreaming);
5483
runner._run(() =>
55-
runner._createResponse(client, params, {
84+
runner._createOrRetrieveResponse(client, params, {
5685
...options,
5786
headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' },
5887
}),
@@ -65,11 +94,17 @@ export class ResponseStream<ParsedT = null>
6594
this.#currentResponseSnapshot = undefined;
6695
}
6796

68-
#addEvent(this: ResponseStream<ParsedT>, event: ResponseStreamEvent) {
97+
#addEvent(this: ResponseStream<ParsedT>, event: ResponseStreamEvent, starting_after: number | null) {
6998
if (this.ended) return;
7099

100+
const maybeEmit = (name: string, event: ResponseStreamEvent & { snapshot?: string }) => {
101+
if (starting_after == null || event.sequence_number > starting_after) {
102+
this._emit(name as any, event);
103+
}
104+
};
105+
71106
const response = this.#accumulateResponse(event);
72-
this._emit('event', event);
107+
maybeEmit('event', event);
73108

74109
switch (event.type) {
75110
case 'response.output_text.delta': {
@@ -86,7 +121,7 @@ export class ResponseStream<ParsedT = null>
86121
throw new OpenAIError(`expected content to be 'output_text', got ${content.type}`);
87122
}
88123

89-
this._emit('response.output_text.delta', {
124+
maybeEmit('response.output_text.delta', {
90125
...event,
91126
snapshot: content.text,
92127
});
@@ -99,16 +134,15 @@ export class ResponseStream<ParsedT = null>
99134
throw new OpenAIError(`missing output at index ${event.output_index}`);
100135
}
101136
if (output.type === 'function_call') {
102-
this._emit('response.function_call_arguments.delta', {
137+
maybeEmit('response.function_call_arguments.delta', {
103138
...event,
104139
snapshot: output.arguments,
105140
});
106141
}
107142
break;
108143
}
109144
default:
110-
// @ts-ignore
111-
this._emit(event.type, event);
145+
maybeEmit(event.type, event);
112146
break;
113147
}
114148
}
@@ -128,9 +162,9 @@ export class ResponseStream<ParsedT = null>
128162
return parsedResponse;
129163
}
130164

131-
protected async _createResponse(
165+
protected async _createOrRetrieveResponse(
132166
client: OpenAI,
133-
params: ResponseStreamingParams,
167+
params: ResponseStreamParams,
134168
options?: Core.RequestOptions,
135169
): Promise<ParsedResponse<ParsedT>> {
136170
const signal = options?.signal;
@@ -140,13 +174,25 @@ export class ResponseStream<ParsedT = null>
140174
}
141175
this.#beginRequest();
142176

143-
const stream = await client.responses.create(
144-
{ ...params, stream: true },
145-
{ ...options, signal: this.controller.signal },
146-
);
177+
let stream: Stream<ResponseStreamEvent> | undefined;
178+
let starting_after: number | null = null;
179+
if ('response_id' in params) {
180+
stream = await client.responses.retrieve(
181+
params.response_id,
182+
{ stream: true },
183+
{ ...options, signal: this.controller.signal, stream: true },
184+
);
185+
starting_after = params.starting_after ?? null;
186+
} else {
187+
stream = await client.responses.create(
188+
{ ...params, stream: true },
189+
{ ...options, signal: this.controller.signal },
190+
);
191+
}
192+
147193
this._connected();
148194
for await (const event of stream) {
149-
this.#addEvent(event);
195+
this.#addEvent(event, starting_after);
150196
}
151197
if (stream.controller.signal?.aborted) {
152198
throw new APIUserAbortError();

0 commit comments

Comments
 (0)