@openai/agents-openai 0.4.14 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/defaults.d.ts +4 -0
  2. package/dist/defaults.js +15 -1
  3. package/dist/defaults.js.map +1 -1
  4. package/dist/defaults.mjs +11 -0
  5. package/dist/defaults.mjs.map +1 -1
  6. package/dist/index.d.ts +3 -2
  7. package/dist/index.js +5 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +3 -2
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/metadata.js +2 -2
  12. package/dist/metadata.js.map +1 -1
  13. package/dist/metadata.mjs +2 -2
  14. package/dist/metadata.mjs.map +1 -1
  15. package/dist/openaiProvider.d.ts +7 -0
  16. package/dist/openaiProvider.js +84 -2
  17. package/dist/openaiProvider.js.map +1 -1
  18. package/dist/openaiProvider.mjs +86 -4
  19. package/dist/openaiProvider.mjs.map +1 -1
  20. package/dist/openaiResponsesModel.d.ts +28 -1
  21. package/dist/openaiResponsesModel.js +435 -33
  22. package/dist/openaiResponsesModel.js.map +1 -1
  23. package/dist/openaiResponsesModel.mjs +433 -32
  24. package/dist/openaiResponsesModel.mjs.map +1 -1
  25. package/dist/responsesTransportUtils.d.ts +29 -0
  26. package/dist/responsesTransportUtils.js +208 -0
  27. package/dist/responsesTransportUtils.js.map +1 -0
  28. package/dist/responsesTransportUtils.mjs +198 -0
  29. package/dist/responsesTransportUtils.mjs.map +1 -0
  30. package/dist/responsesWebSocketConnection.d.ts +22 -0
  31. package/dist/responsesWebSocketConnection.js +273 -0
  32. package/dist/responsesWebSocketConnection.js.map +1 -0
  33. package/dist/responsesWebSocketConnection.mjs +259 -0
  34. package/dist/responsesWebSocketConnection.mjs.map +1 -0
  35. package/dist/responsesWebSocketSession.d.ts +22 -0
  36. package/dist/responsesWebSocketSession.js +56 -0
  37. package/dist/responsesWebSocketSession.js.map +1 -0
  38. package/dist/responsesWebSocketSession.mjs +53 -0
  39. package/dist/responsesWebSocketSession.mjs.map +1 -0
  40. package/package.json +2 -2
@@ -3,15 +3,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.OpenAIResponsesModel = void 0;
6
+ exports.OpenAIResponsesWSModel = exports.OpenAIResponsesModel = void 0;
7
7
  exports.getToolChoice = getToolChoice;
8
8
  exports.converTool = converTool;
9
9
  exports.getInputItems = getInputItems;
10
10
  exports.convertToOutputItem = convertToOutputItem;
11
11
  const agents_core_1 = require("@openai/agents-core");
12
+ const openai_1 = __importDefault(require("openai"));
12
13
  const logger_1 = __importDefault(require("./logger.js"));
13
14
  const zod_1 = require("zod");
14
15
  const defaults_1 = require("./defaults.js");
16
+ const responsesWebSocketConnection_1 = require("./responsesWebSocketConnection.js");
17
+ const responsesTransportUtils_1 = require("./responsesTransportUtils.js");
15
18
  const tools_1 = require("./tools.js");
16
19
  const providerData_1 = require("./utils/providerData.js");
17
20
  const utils_1 = require("@openai/agents-core/utils");
@@ -1399,23 +1402,51 @@ function convertToOutputItem(items) {
1399
1402
  };
1400
1403
  });
1401
1404
  }
1405
+ const TERMINAL_RESPONSES_STREAM_EVENT_TYPES = new Set([
1406
+ 'response.completed',
1407
+ 'response.failed',
1408
+ 'response.incomplete',
1409
+ 'response.error',
1410
+ ]);
1411
+ function isTerminalResponsesStreamEventType(eventType) {
1412
+ return (typeof eventType === 'string' &&
1413
+ TERMINAL_RESPONSES_STREAM_EVENT_TYPES.has(eventType));
1414
+ }
1402
1415
  /**
1403
1416
  * Model implementation that uses OpenAI's Responses API to generate responses.
1404
1417
  */
1405
1418
  class OpenAIResponsesModel {
1406
- #client;
1407
- #model;
1419
+ _client;
1420
+ _model;
1408
1421
  constructor(client, model) {
1409
- this.#client = client;
1410
- this.#model = model;
1422
+ this._client = client;
1423
+ this._model = model;
1411
1424
  }
1412
- async #fetchResponse(request, stream) {
1425
+ async _fetchResponse(request, stream) {
1426
+ const builtRequest = this._buildResponsesCreateRequest(request, stream);
1427
+ const response = await this._client.responses.create(builtRequest.requestData, {
1428
+ headers: builtRequest.sdkRequestHeaders,
1429
+ signal: builtRequest.signal,
1430
+ ...(builtRequest.transportExtraQuery
1431
+ ? { query: builtRequest.transportExtraQuery }
1432
+ : {}),
1433
+ });
1434
+ if (logger_1.default.dontLogModelData) {
1435
+ logger_1.default.debug('Response received');
1436
+ }
1437
+ else {
1438
+ logger_1.default.debug(`Response received: ${JSON.stringify(response, null, 2)}`);
1439
+ }
1440
+ return response;
1441
+ }
1442
+ _buildResponsesCreateRequest(request, stream) {
1413
1443
  const input = getInputItems(request.input);
1414
1444
  const { tools, include } = getTools(request.tools, request.handoffs);
1415
1445
  const toolChoice = getToolChoice(request.modelSettings.toolChoice);
1416
- const { text, ...restOfProviderData } = request.modelSettings.providerData ?? {};
1446
+ const { providerData: providerDataWithoutTransport, overrides: transportOverrides, } = (0, responsesTransportUtils_1.splitResponsesTransportOverrides)(request.modelSettings.providerData);
1447
+ const { text, ...restOfProviderData } = providerDataWithoutTransport;
1417
1448
  if (request.modelSettings.reasoning) {
1418
- // Merge top-level reasoning settings with provider data
1449
+ // Merge top-level reasoning settings with provider data.
1419
1450
  restOfProviderData.reasoning = {
1420
1451
  ...request.modelSettings.reasoning,
1421
1452
  ...restOfProviderData.reasoning,
@@ -1423,7 +1454,7 @@ class OpenAIResponsesModel {
1423
1454
  }
1424
1455
  let mergedText = text;
1425
1456
  if (request.modelSettings.text) {
1426
- // Merge top-level text settings with provider data
1457
+ // Merge top-level text settings with provider data.
1427
1458
  mergedText = { ...request.modelSettings.text, ...text };
1428
1459
  }
1429
1460
  const responseFormat = getResponseFormat(request.outputType, mergedText);
@@ -1444,8 +1475,8 @@ class OpenAIResponsesModel {
1444
1475
  const shouldOmitToolChoice = Boolean(request.prompt) &&
1445
1476
  !shouldSendTools &&
1446
1477
  typeof toolChoice === 'object';
1447
- const requestData = {
1448
- ...(shouldSendModel ? { model: this.#model } : {}),
1478
+ let requestData = {
1479
+ ...(shouldSendModel ? { model: this._model } : {}),
1449
1480
  instructions: normalizeInstructions(request.systemInstructions),
1450
1481
  input,
1451
1482
  include,
@@ -1471,23 +1502,34 @@ class OpenAIResponsesModel {
1471
1502
  prompt_cache_retention: request.modelSettings.promptCacheRetention,
1472
1503
  ...restOfProviderData,
1473
1504
  };
1474
- if (logger_1.default.dontLogModelData) {
1475
- logger_1.default.debug('Calling LLM');
1476
- }
1477
- else {
1478
- logger_1.default.debug(`Calling LLM. Request data: ${JSON.stringify(requestData, null, 2)}`);
1505
+ if (transportOverrides.extraBody) {
1506
+ requestData = {
1507
+ ...requestData,
1508
+ ...transportOverrides.extraBody,
1509
+ };
1479
1510
  }
1480
- const response = await this.#client.responses.create(requestData, {
1481
- headers: defaults_1.HEADERS,
1482
- signal: request.signal,
1511
+ // Keep the transport mode aligned with the calling path even if extra_body includes stream.
1512
+ requestData.stream = stream;
1513
+ const requestHeaderAccumulator = (0, responsesTransportUtils_1.createHeaderAccumulator)();
1514
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(requestHeaderAccumulator, defaults_1.HEADERS);
1515
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(requestHeaderAccumulator, transportOverrides.extraHeaders, {
1516
+ allowBlockedOverride: true,
1483
1517
  });
1518
+ const sdkRequestHeaders = (0, responsesTransportUtils_1.headerAccumulatorToSDKHeaders)(requestHeaderAccumulator);
1519
+ const builtRequest = {
1520
+ requestData,
1521
+ sdkRequestHeaders,
1522
+ signal: request.signal,
1523
+ transportExtraHeaders: transportOverrides.extraHeaders,
1524
+ transportExtraQuery: transportOverrides.extraQuery,
1525
+ };
1484
1526
  if (logger_1.default.dontLogModelData) {
1485
- logger_1.default.debug('Response received');
1527
+ logger_1.default.debug('Calling LLM');
1486
1528
  }
1487
1529
  else {
1488
- logger_1.default.debug(`Response received: ${JSON.stringify(response, null, 2)}`);
1530
+ logger_1.default.debug(`Calling LLM. Request data: ${JSON.stringify(builtRequest.requestData, null, 2)}`);
1489
1531
  }
1490
- return response;
1532
+ return builtRequest;
1491
1533
  }
1492
1534
  /**
1493
1535
  * Get a response from the OpenAI model using the Responses API.
@@ -1496,7 +1538,7 @@ class OpenAIResponsesModel {
1496
1538
  */
1497
1539
  async getResponse(request) {
1498
1540
  const response = await (0, agents_core_1.withResponseSpan)(async (span) => {
1499
- const response = await this.#fetchResponse(request, false);
1541
+ const response = await this._fetchResponse(request, false);
1500
1542
  if (request.tracing) {
1501
1543
  span.spanData.response_id = response.id;
1502
1544
  span.spanData._input = request.input;
@@ -1536,10 +1578,11 @@ class OpenAIResponsesModel {
1536
1578
  span.spanData._input = request.input;
1537
1579
  }
1538
1580
  }
1539
- const response = await this.#fetchResponse(request, true);
1581
+ const response = await this._fetchResponse(request, true);
1540
1582
  let finalResponse;
1541
1583
  for await (const event of response) {
1542
- if (event.type === 'response.created') {
1584
+ const eventType = event.type;
1585
+ if (eventType === 'response.created') {
1543
1586
  yield {
1544
1587
  type: 'response_started',
1545
1588
  providerData: {
@@ -1547,9 +1590,10 @@ class OpenAIResponsesModel {
1547
1590
  },
1548
1591
  };
1549
1592
  }
1550
- else if (event.type === 'response.completed') {
1551
- finalResponse = event.response;
1552
- const { response, ...remainingEvent } = event;
1593
+ else if (isTerminalResponsesStreamEventType(eventType)) {
1594
+ const terminalEvent = event;
1595
+ finalResponse = terminalEvent.response;
1596
+ const { response, ...remainingEvent } = terminalEvent;
1553
1597
  const { output, usage, id, ...remainingResponse } = response;
1554
1598
  yield {
1555
1599
  type: 'response_done',
@@ -1574,12 +1618,14 @@ class OpenAIResponsesModel {
1574
1618
  },
1575
1619
  providerData: remainingEvent,
1576
1620
  };
1577
- yield {
1578
- type: 'model',
1579
- event: event,
1580
- };
1621
+ if (eventType === 'response.completed') {
1622
+ yield {
1623
+ type: 'model',
1624
+ event: event,
1625
+ };
1626
+ }
1581
1627
  }
1582
- else if (event.type === 'response.output_text.delta') {
1628
+ else if (eventType === 'response.output_text.delta') {
1583
1629
  const { delta, ...remainingEvent } = event;
1584
1630
  yield {
1585
1631
  type: 'output_text_delta',
@@ -1621,6 +1667,344 @@ class OpenAIResponsesModel {
1621
1667
  }
1622
1668
  }
1623
1669
  exports.OpenAIResponsesModel = OpenAIResponsesModel;
1670
+ /**
1671
+ * Model implementation that uses the OpenAI Responses API over a websocket transport.
1672
+ *
1673
+ * @see {@link https://developers.openai.com/api/docs/guides/websocket-mode}
1674
+ */
1675
+ class OpenAIResponsesWSModel extends OpenAIResponsesModel {
1676
+ #websocketBaseURL;
1677
+ #reuseConnection;
1678
+ #wsConnection;
1679
+ #wsConnectionIdentity;
1680
+ #wsRequestLock = Promise.resolve();
1681
+ constructor(client, model, options = {}) {
1682
+ super(client, model);
1683
+ this.#websocketBaseURL = options.websocketBaseURL;
1684
+ this.#reuseConnection = options.reuseConnection ?? true;
1685
+ }
1686
+ async _fetchResponse(request, stream) {
1687
+ // The websocket transport always uses streamed Responses events, then callers either
1688
+ // consume the stream directly or collapse it into the final terminal response.
1689
+ const builtRequest = this._buildResponsesCreateRequest(request, true);
1690
+ if (stream) {
1691
+ return this.#iterWebSocketResponseEvents(builtRequest);
1692
+ }
1693
+ let finalResponse;
1694
+ for await (const event of this.#iterWebSocketResponseEvents(builtRequest)) {
1695
+ const eventType = event.type;
1696
+ if (isTerminalResponsesStreamEventType(eventType)) {
1697
+ finalResponse = event
1698
+ .response;
1699
+ }
1700
+ }
1701
+ if (!finalResponse) {
1702
+ throw new Error('Responses websocket stream ended without a terminal response event.');
1703
+ }
1704
+ return finalResponse;
1705
+ }
1706
+ async close() {
1707
+ await this.#dropWebSocketConnection();
1708
+ }
1709
+ async *#iterWebSocketResponseEvents(builtRequest) {
1710
+ const requestTimeoutDeadline = this.#createWebSocketRequestTimeoutDeadline();
1711
+ const releaseLock = await this.#acquireWebSocketRequestLock(builtRequest.signal, requestTimeoutDeadline);
1712
+ let receivedAnyEvent = false;
1713
+ let sawTerminalResponseEvent = false;
1714
+ try {
1715
+ (0, responsesWebSocketConnection_1.throwIfAborted)(builtRequest.signal);
1716
+ const { frame, wsURL, headers } = await this.#prepareWebSocketRequest(builtRequest, requestTimeoutDeadline);
1717
+ (0, responsesWebSocketConnection_1.throwIfAborted)(builtRequest.signal);
1718
+ let connection = await this.#ensureWebSocketConnection(wsURL, headers, builtRequest.signal, requestTimeoutDeadline);
1719
+ let reusedConnectionForCurrentAttempt = connection.reused;
1720
+ let activeConnection = connection.connection;
1721
+ const setActiveConnection = (nextConnection) => {
1722
+ connection = nextConnection;
1723
+ activeConnection = nextConnection.connection;
1724
+ reusedConnectionForCurrentAttempt = nextConnection.reused;
1725
+ };
1726
+ (0, responsesWebSocketConnection_1.throwIfAborted)(builtRequest.signal);
1727
+ const serializedFrame = JSON.stringify(frame);
1728
+ const sendSerializedFrame = async () => {
1729
+ try {
1730
+ await activeConnection.send(serializedFrame);
1731
+ }
1732
+ catch (error) {
1733
+ if (!(0, responsesWebSocketConnection_1.isWebSocketNotOpenError)(error)) {
1734
+ throw error;
1735
+ }
1736
+ setActiveConnection(await this.#reconnectWebSocketConnection(wsURL, headers, builtRequest.signal, requestTimeoutDeadline));
1737
+ await activeConnection.send(serializedFrame);
1738
+ }
1739
+ };
1740
+ await sendSerializedFrame();
1741
+ while (true) {
1742
+ const rawFrame = await this.#nextWebSocketFrame(activeConnection, builtRequest.signal, requestTimeoutDeadline);
1743
+ if (rawFrame === null) {
1744
+ if (!receivedAnyEvent && reusedConnectionForCurrentAttempt) {
1745
+ // The request frame was already sent on a reused socket. If the
1746
+ // socket closes before the first response event arrives, the server
1747
+ // may still be processing the request, so replaying `response.create`
1748
+ // can duplicate model work and tool side effects.
1749
+ receivedAnyEvent = true;
1750
+ throw new Error('Responses websocket connection closed after sending a request on a reused connection before any response events were received. The request may have been accepted, so the SDK will not automatically retry this websocket request.');
1751
+ }
1752
+ throw new responsesWebSocketConnection_1.ResponsesWebSocketInternalError('connection_closed_before_terminal_response_event', 'Responses websocket connection closed before a terminal response event.');
1753
+ }
1754
+ const payloadText = await (0, responsesWebSocketConnection_1.webSocketFrameToText)(rawFrame);
1755
+ const payload = JSON.parse(payloadText);
1756
+ const eventType = isRecord(payload) && typeof payload.type === 'string'
1757
+ ? payload.type
1758
+ : undefined;
1759
+ if (eventType === 'error') {
1760
+ receivedAnyEvent = true;
1761
+ throw new Error(`Responses websocket error: ${JSON.stringify(payload)}`);
1762
+ }
1763
+ const event = payload;
1764
+ const isTerminalResponseEvent = isTerminalResponsesStreamEventType(eventType);
1765
+ receivedAnyEvent = true;
1766
+ if (isTerminalResponseEvent) {
1767
+ sawTerminalResponseEvent = true;
1768
+ }
1769
+ yield event;
1770
+ if (isTerminalResponseEvent) {
1771
+ return;
1772
+ }
1773
+ }
1774
+ }
1775
+ catch (error) {
1776
+ if (!receivedAnyEvent &&
1777
+ !(error instanceof openai_1.default.APIUserAbortError) &&
1778
+ (0, responsesWebSocketConnection_1.shouldWrapNoEventWebSocketError)(error)) {
1779
+ const wrappedError = new Error('Responses websocket connection closed before any response events were received. The feature may not be enabled for this account or model yet.');
1780
+ if (error instanceof Error) {
1781
+ wrappedError.cause = error;
1782
+ }
1783
+ throw wrappedError;
1784
+ }
1785
+ throw error;
1786
+ }
1787
+ finally {
1788
+ const shouldDropConnection = !sawTerminalResponseEvent || !this.#reuseConnection;
1789
+ const dropConnectionPromise = shouldDropConnection
1790
+ ? this.#dropWebSocketConnection()
1791
+ : undefined;
1792
+ releaseLock();
1793
+ await dropConnectionPromise;
1794
+ }
1795
+ }
1796
+ async #prepareWebSocketRequest(builtRequest, requestTimeoutDeadline) {
1797
+ const wsURL = this.#prepareWebSocketURL(builtRequest.transportExtraQuery);
1798
+ const headers = await this.#mergeWebSocketHeaders(wsURL, builtRequest.transportExtraHeaders, builtRequest.signal, requestTimeoutDeadline);
1799
+ const frame = {
1800
+ ...builtRequest.requestData,
1801
+ type: 'response.create',
1802
+ stream: true,
1803
+ };
1804
+ return { frame, wsURL, headers };
1805
+ }
1806
+ async #mergeWebSocketHeaders(wsURL, extraHeaders, signal, requestTimeoutDeadline) {
1807
+ await this.#awaitWebSocketRequestTimedOperation(this.#refreshClientApiKey(), signal, requestTimeoutDeadline, (configuredTimeoutMs) => `Responses websocket auth header preparation timed out after ${configuredTimeoutMs}ms.`);
1808
+ const headerAccumulator = (0, responsesTransportUtils_1.createHeaderAccumulator)();
1809
+ const clientWithInternals = this._client;
1810
+ const handshakeURL = new URL(wsURL);
1811
+ const handshakeQuery = searchParamsToAuthHeaderQuery(handshakeURL.searchParams);
1812
+ const authHeaders = typeof clientWithInternals.authHeaders === 'function'
1813
+ ? await this.#awaitWebSocketRequestTimedOperation(clientWithInternals.authHeaders({
1814
+ method: 'get',
1815
+ path: handshakeURL.pathname,
1816
+ ...(handshakeQuery ? { query: handshakeQuery } : {}),
1817
+ }), signal, requestTimeoutDeadline, (configuredTimeoutMs) => `Responses websocket auth header preparation timed out after ${configuredTimeoutMs}ms.`)
1818
+ : undefined;
1819
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(headerAccumulator, authHeaders);
1820
+ if (typeof clientWithInternals.authHeaders !== 'function' &&
1821
+ typeof this._client.apiKey === 'string' &&
1822
+ this._client.apiKey.length > 0 &&
1823
+ this._client.apiKey !== 'Missing Key') {
1824
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(headerAccumulator, {
1825
+ Authorization: `Bearer ${this._client.apiKey}`,
1826
+ });
1827
+ }
1828
+ if (this._client.organization) {
1829
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(headerAccumulator, {
1830
+ 'OpenAI-Organization': this._client.organization,
1831
+ });
1832
+ }
1833
+ if (this._client.project) {
1834
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(headerAccumulator, {
1835
+ 'OpenAI-Project': this._client.project,
1836
+ });
1837
+ }
1838
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(headerAccumulator, clientWithInternals._options?.defaultHeaders);
1839
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(headerAccumulator, defaults_1.HEADERS);
1840
+ (0, responsesTransportUtils_1.applyHeadersToAccumulator)(headerAccumulator, extraHeaders, {
1841
+ allowBlockedOverride: true,
1842
+ });
1843
+ return (0, responsesTransportUtils_1.headerAccumulatorToRecord)(headerAccumulator);
1844
+ }
1845
+ #prepareWebSocketURL(extraQuery) {
1846
+ const baseURL = new URL(this.#websocketBaseURL ?? this._client.baseURL);
1847
+ const explicitBaseQuery = typeof this.#websocketBaseURL === 'string'
1848
+ ? new URLSearchParams(baseURL.search)
1849
+ : undefined;
1850
+ const clientWithInternals = this._client;
1851
+ if (baseURL.protocol === 'https:') {
1852
+ baseURL.protocol = 'wss:';
1853
+ }
1854
+ else if (baseURL.protocol === 'http:') {
1855
+ baseURL.protocol = 'ws:';
1856
+ }
1857
+ else if (baseURL.protocol !== 'ws:' && baseURL.protocol !== 'wss:') {
1858
+ throw new agents_core_1.UserError(`Unsupported websocket base URL protocol: ${baseURL.protocol}`);
1859
+ }
1860
+ baseURL.pathname = (0, responsesTransportUtils_1.ensureResponsesWebSocketPath)(baseURL.pathname);
1861
+ (0, responsesTransportUtils_1.mergeQueryParamsIntoURL)(baseURL, clientWithInternals._options?.defaultQuery);
1862
+ if (explicitBaseQuery && Array.from(explicitBaseQuery.keys()).length > 0) {
1863
+ const explicitTopLevelKeys = new Set();
1864
+ for (const key of explicitBaseQuery.keys()) {
1865
+ const bracketIndex = key.indexOf('[');
1866
+ explicitTopLevelKeys.add(bracketIndex >= 0 ? key.slice(0, bracketIndex) : key);
1867
+ }
1868
+ for (const topLevelKey of explicitTopLevelKeys) {
1869
+ for (const existingKey of Array.from(baseURL.searchParams.keys())) {
1870
+ if (existingKey === topLevelKey ||
1871
+ existingKey.startsWith(`${topLevelKey}[`)) {
1872
+ baseURL.searchParams.delete(existingKey);
1873
+ }
1874
+ }
1875
+ }
1876
+ for (const [key, value] of explicitBaseQuery.entries()) {
1877
+ baseURL.searchParams.append(key, value);
1878
+ }
1879
+ }
1880
+ (0, responsesTransportUtils_1.mergeQueryParamsIntoURL)(baseURL, extraQuery);
1881
+ return baseURL.toString();
1882
+ }
1883
+ async #ensureWebSocketConnection(wsURL, headers, signal, requestTimeoutDeadline) {
1884
+ const identity = this.#getConnectionIdentity(wsURL, headers);
1885
+ if (this.#wsConnection &&
1886
+ this.#wsConnectionIdentity &&
1887
+ this.#wsConnectionIdentity === identity &&
1888
+ this.#wsConnection.isReusable()) {
1889
+ return { connection: this.#wsConnection, reused: true };
1890
+ }
1891
+ await this.#dropWebSocketConnection();
1892
+ const connectTimeout = this.#resolveWebSocketRequestTimeout(requestTimeoutDeadline, (configuredTimeoutMs) => `Responses websocket connection timed out before opening after ${configuredTimeoutMs}ms.`);
1893
+ this.#wsConnection = await responsesWebSocketConnection_1.ResponsesWebSocketConnection.connect(wsURL, headers, signal, connectTimeout.timeoutMs, connectTimeout.errorMessage);
1894
+ this.#wsConnectionIdentity = identity;
1895
+ return { connection: this.#wsConnection, reused: false };
1896
+ }
1897
+ async #reconnectWebSocketConnection(wsURL, headers, signal, requestTimeoutDeadline) {
1898
+ await this.#dropWebSocketConnection();
1899
+ (0, responsesWebSocketConnection_1.throwIfAborted)(signal);
1900
+ const connection = await this.#ensureWebSocketConnection(wsURL, headers, signal, requestTimeoutDeadline);
1901
+ (0, responsesWebSocketConnection_1.throwIfAborted)(signal);
1902
+ return connection;
1903
+ }
1904
+ #getConnectionIdentity(wsURL, headers) {
1905
+ const normalizedHeaders = Object.entries(headers)
1906
+ .map(([key, value]) => [key.toLowerCase(), value])
1907
+ .sort(([leftKey, leftValue], [rightKey, rightValue]) => `${leftKey}:${leftValue}`.localeCompare(`${rightKey}:${rightValue}`));
1908
+ return JSON.stringify([wsURL, normalizedHeaders]);
1909
+ }
1910
+ async #dropWebSocketConnection() {
1911
+ const connectionToClose = this.#wsConnection;
1912
+ if (!connectionToClose) {
1913
+ this.#wsConnectionIdentity = undefined;
1914
+ return;
1915
+ }
1916
+ // Detach cached state before awaiting close so queued requests can proceed
1917
+ // without racing against this teardown path.
1918
+ this.#wsConnection = undefined;
1919
+ this.#wsConnectionIdentity = undefined;
1920
+ try {
1921
+ await connectionToClose.close();
1922
+ }
1923
+ catch {
1924
+ // Ignore close errors and reset the cached connection.
1925
+ }
1926
+ }
1927
+ async #acquireWebSocketRequestLock(signal, requestTimeoutDeadline) {
1928
+ (0, responsesWebSocketConnection_1.throwIfAborted)(signal);
1929
+ const queueWaitTimeout = this.#resolveWebSocketRequestTimeout(requestTimeoutDeadline, (configuredTimeoutMs) => `Responses websocket request queue wait timed out after ${configuredTimeoutMs}ms.`);
1930
+ const previousLock = this.#wsRequestLock;
1931
+ let released = false;
1932
+ let resolveOwnLock;
1933
+ const ownLock = new Promise((resolve) => {
1934
+ resolveOwnLock = resolve;
1935
+ });
1936
+ const releaseLock = () => {
1937
+ if (released) {
1938
+ return;
1939
+ }
1940
+ released = true;
1941
+ resolveOwnLock();
1942
+ };
1943
+ this.#wsRequestLock = previousLock.then(() => ownLock);
1944
+ try {
1945
+ await (0, responsesWebSocketConnection_1.withAbortSignal)((0, responsesWebSocketConnection_1.withTimeout)(previousLock, queueWaitTimeout.timeoutMs, queueWaitTimeout.errorMessage), signal);
1946
+ (0, responsesWebSocketConnection_1.throwIfAborted)(signal);
1947
+ return releaseLock;
1948
+ }
1949
+ catch (error) {
1950
+ releaseLock();
1951
+ throw error;
1952
+ }
1953
+ }
1954
+ async #refreshClientApiKey() {
1955
+ const clientWithInternals = this._client;
1956
+ if (typeof clientWithInternals._callApiKey === 'function') {
1957
+ await clientWithInternals._callApiKey();
1958
+ }
1959
+ }
1960
+ #getWebSocketFrameReadTimeoutMs() {
1961
+ const clientWithTimeout = this._client;
1962
+ const timeoutCandidate = typeof clientWithTimeout.timeout === 'number'
1963
+ ? clientWithTimeout.timeout
1964
+ : clientWithTimeout._options?.timeout;
1965
+ if (typeof timeoutCandidate === 'number') {
1966
+ return timeoutCandidate;
1967
+ }
1968
+ return openai_1.default.DEFAULT_TIMEOUT;
1969
+ }
1970
+ #createWebSocketRequestTimeoutDeadline() {
1971
+ const timeoutMs = this.#getWebSocketFrameReadTimeoutMs();
1972
+ if (typeof timeoutMs !== 'number' ||
1973
+ !Number.isFinite(timeoutMs) ||
1974
+ timeoutMs <= 0) {
1975
+ return undefined;
1976
+ }
1977
+ return {
1978
+ configuredTimeoutMs: timeoutMs,
1979
+ deadlineAtMs: Date.now() + timeoutMs,
1980
+ };
1981
+ }
1982
+ #resolveWebSocketRequestTimeout(requestTimeoutDeadline, errorMessageForConfiguredTimeout) {
1983
+ const configuredTimeoutMs = requestTimeoutDeadline?.configuredTimeoutMs ??
1984
+ this.#getWebSocketFrameReadTimeoutMs();
1985
+ const safeConfiguredTimeoutMs = typeof configuredTimeoutMs === 'number'
1986
+ ? configuredTimeoutMs
1987
+ : openai_1.default.DEFAULT_TIMEOUT;
1988
+ const errorMessage = errorMessageForConfiguredTimeout(safeConfiguredTimeoutMs);
1989
+ if (!requestTimeoutDeadline) {
1990
+ return { timeoutMs: configuredTimeoutMs, errorMessage };
1991
+ }
1992
+ const remainingTimeoutMs = Math.ceil(requestTimeoutDeadline.deadlineAtMs - Date.now());
1993
+ if (remainingTimeoutMs <= 0) {
1994
+ throw new Error(errorMessage);
1995
+ }
1996
+ return { timeoutMs: remainingTimeoutMs, errorMessage };
1997
+ }
1998
+ async #awaitWebSocketRequestTimedOperation(promise, signal, requestTimeoutDeadline, errorMessageForConfiguredTimeout) {
1999
+ const timeout = this.#resolveWebSocketRequestTimeout(requestTimeoutDeadline, errorMessageForConfiguredTimeout);
2000
+ return await (0, responsesWebSocketConnection_1.withAbortSignal)((0, responsesWebSocketConnection_1.withTimeout)(promise, timeout.timeoutMs, timeout.errorMessage), signal);
2001
+ }
2002
+ async #nextWebSocketFrame(connection, signal, requestTimeoutDeadline) {
2003
+ const frameReadTimeout = this.#resolveWebSocketRequestTimeout(requestTimeoutDeadline, (configuredTimeoutMs) => `Responses websocket frame read timed out after ${configuredTimeoutMs}ms.`);
2004
+ return await (0, responsesWebSocketConnection_1.withTimeout)(connection.nextFrame(signal), frameReadTimeout.timeoutMs, frameReadTimeout.errorMessage);
2005
+ }
2006
+ }
2007
+ exports.OpenAIResponsesWSModel = OpenAIResponsesWSModel;
1624
2008
  /**
1625
2009
  * Sending an empty string for instructions can override the prompt parameter.
1626
2010
  * Thus, this method checks if the instructions is an empty string and returns undefined if it is.
@@ -1636,6 +2020,24 @@ function normalizeInstructions(instructions) {
1636
2020
  }
1637
2021
  return undefined;
1638
2022
  }
2023
+ function searchParamsToAuthHeaderQuery(searchParams) {
2024
+ const query = {};
2025
+ let hasEntries = false;
2026
+ for (const [key, value] of searchParams.entries()) {
2027
+ hasEntries = true;
2028
+ const existingValue = query[key];
2029
+ if (typeof existingValue === 'undefined') {
2030
+ query[key] = value;
2031
+ continue;
2032
+ }
2033
+ if (Array.isArray(existingValue)) {
2034
+ existingValue.push(value);
2035
+ continue;
2036
+ }
2037
+ query[key] = [existingValue, value];
2038
+ }
2039
+ return hasEntries ? query : undefined;
2040
+ }
1639
2041
  function toRequestUsageEntry(usage, endpoint) {
1640
2042
  return new agents_core_1.RequestUsage({
1641
2043
  inputTokens: usage?.input_tokens ?? 0,