ws.mjs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import * as WS from 'ws';
  2. import { OpenAI } from "../../index.mjs";
  3. import { OpenAIRealtimeEmitter, buildRealtimeURL, isAzure } from "./internal-base.mjs";
  4. export class OpenAIRealtimeWS extends OpenAIRealtimeEmitter {
  5. constructor(props, client) {
  6. super();
  7. client ?? (client = new OpenAI());
  8. const hasProvider = typeof client?._options?.apiKey === 'function';
  9. if (hasProvider && !props.__resolvedApiKey) {
  10. throw new Error([
  11. 'Cannot open Realtime WebSocket with a function-based apiKey.',
  12. 'Use the .create() method so that the key is resolved before connecting:',
  13. 'await OpenAIRealtimeWS.create(client, { model })',
  14. ].join('\n'));
  15. }
  16. this.url = buildRealtimeURL(client, props.model);
  17. this.socket = new WS.WebSocket(this.url, {
  18. ...props.options,
  19. headers: {
  20. ...props.options?.headers,
  21. ...(isAzure(client) && !props.__resolvedApiKey ? {} : { Authorization: `Bearer ${client.apiKey}` }),
  22. 'OpenAI-Beta': 'realtime=v1',
  23. },
  24. });
  25. this.socket.on('message', (wsEvent) => {
  26. const event = (() => {
  27. try {
  28. return JSON.parse(wsEvent.toString());
  29. }
  30. catch (err) {
  31. this._onError(null, 'could not parse websocket event', err);
  32. return null;
  33. }
  34. })();
  35. if (event) {
  36. this._emit('event', event);
  37. if (event.type === 'error') {
  38. this._onError(event);
  39. }
  40. else {
  41. // @ts-expect-error TS isn't smart enough to get the relationship right here
  42. this._emit(event.type, event);
  43. }
  44. }
  45. });
  46. this.socket.on('error', (err) => {
  47. this._onError(null, err.message, err);
  48. });
  49. }
  50. static async create(client, props) {
  51. return new OpenAIRealtimeWS({ ...props, __resolvedApiKey: await client._callApiKey() }, client);
  52. }
  53. static async azure(client, props = {}) {
  54. const isApiKeyProvider = await client._callApiKey();
  55. const deploymentName = props.deploymentName ?? client.deploymentName;
  56. if (!deploymentName) {
  57. throw new Error('No deployment name provided');
  58. }
  59. return new OpenAIRealtimeWS({
  60. model: deploymentName,
  61. options: {
  62. ...props.options,
  63. headers: {
  64. ...props.options?.headers,
  65. ...(isApiKeyProvider ? {} : { 'api-key': client.apiKey }),
  66. },
  67. },
  68. __resolvedApiKey: isApiKeyProvider,
  69. }, client);
  70. }
  71. send(event) {
  72. try {
  73. this.socket.send(JSON.stringify(event));
  74. }
  75. catch (err) {
  76. this._onError(null, 'could not send data', err);
  77. }
  78. }
  79. close(props) {
  80. try {
  81. this.socket.close(props?.code ?? 1000, props?.reason ?? 'OK');
  82. }
  83. catch (err) {
  84. this._onError(null, 'could not close the connection', err);
  85. }
  86. }
  87. }
  88. //# sourceMappingURL=ws.mjs.map