Skip to content

Commit 53c3bea

Browse files
authored
fix(providers): name the header phase on streaming OpenAI requests (#6288)
1 parent 6cc25f6 commit 53c3bea

2 files changed

Lines changed: 40 additions & 18 deletions

File tree

apps/sim/providers/openai/core.transport-phase.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,20 @@ describe('OpenAI transport phase annotation', () => {
198198
expect(error.message.match(/phase=/g)).toHaveLength(1)
199199
})
200200

201+
/**
202+
* Streaming calls the request helper directly and never reaches `postResponses`, so a
203+
* header stall on a chat or SSE run used to surface as the bare runtime string with no
204+
* phase, elapsed time, or request id.
205+
*/
206+
it('names the header phase on a streaming request too', async () => {
207+
const error = await run(vi.fn().mockRejectedValue(timeoutError()), {
208+
stream: true,
209+
}).catch((e) => e)
210+
211+
expect(error.message).toContain('phase=awaiting-response-headers')
212+
expect(error.message).toMatch(/elapsedMs=\d+/)
213+
})
214+
201215
it('leaves a healthy response entirely unaffected', async () => {
202216
const fetchMock = vi.fn().mockResolvedValue({
203217
ok: true,

apps/sim/providers/openai/core.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,29 @@ export async function executeResponsesProviderRequest(
410410

411411
let reasoningSummariesUnavailable = false
412412

413+
/**
414+
* The single point every Responses request leaves through, so a stall waiting for
415+
* headers is named on the streaming paths too — they call
416+
* {@link fetchResponsesWithSummaryFallback} directly and never reach `postResponses`,
417+
* which is where the annotation used to live.
418+
*/
419+
const postOnce = async (
420+
payload: Record<string, unknown>,
421+
abortSignal: AbortSignal | undefined,
422+
startedAt: number
423+
): Promise<Response> => {
424+
try {
425+
return await fetchImpl(config.endpoint, {
426+
method: 'POST',
427+
headers: config.headers,
428+
body: JSON.stringify(payload),
429+
signal: abortSignal,
430+
})
431+
} catch (error) {
432+
throw annotateTransportFailure(error, 'awaiting-response-headers', startedAt)
433+
}
434+
}
435+
413436
const fetchResponsesWithSummaryFallback = async (
414437
requestedBody: Record<string, unknown>,
415438
startedAt: number,
@@ -418,12 +441,7 @@ export async function executeResponsesProviderRequest(
418441
const body = reasoningSummariesUnavailable
419442
? (stripReasoningSummary(requestedBody) ?? requestedBody)
420443
: requestedBody
421-
const response = await fetchImpl(config.endpoint, {
422-
method: 'POST',
423-
headers: config.headers,
424-
body: JSON.stringify(body),
425-
signal: abortSignal,
426-
})
444+
const response = await postOnce(body, abortSignal, startedAt)
427445
if (response.ok) return response
428446

429447
const message = await parseErrorResponse(response, startedAt)
@@ -439,12 +457,7 @@ export async function executeResponsesProviderRequest(
439457
`${config.providerLabel} rejected reasoning summaries (organization not verified); retrying without summary`,
440458
{ model: config.modelName }
441459
)
442-
const retryResponse = await fetchImpl(config.endpoint, {
443-
method: 'POST',
444-
headers: config.headers,
445-
body: JSON.stringify(strippedBody),
446-
signal: abortSignal,
447-
})
460+
const retryResponse = await postOnce(strippedBody, abortSignal, startedAt)
448461
if (!retryResponse.ok) {
449462
const retryMessage = await parseErrorResponse(retryResponse, startedAt)
450463
throw new Error(
@@ -459,12 +472,7 @@ export async function executeResponsesProviderRequest(
459472
): Promise<OpenAI.Responses.Response> => {
460473
const startedAt = Date.now()
461474

462-
let response: Response
463-
try {
464-
response = await fetchResponsesWithSummaryFallback(body, startedAt)
465-
} catch (error) {
466-
throw annotateTransportFailure(error, 'awaiting-response-headers', startedAt)
467-
}
475+
const response = await fetchResponsesWithSummaryFallback(body, startedAt)
468476

469477
const responseMeta = { ...describeResponse(response), ttfbMs: Date.now() - startedAt }
470478

0 commit comments

Comments
 (0)