phonic 0.15.0 → 0.16.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.
package/README.md CHANGED
@@ -96,7 +96,7 @@ if (error === null) {
96
96
  To start a conversation, open a WebSocket connection:
97
97
 
98
98
  ```ts
99
- const { data, error } = await phonic.sts.websocket({
99
+ const phonicWebSocket = phonic.sts.websocket({
100
100
  input_format: "mulaw_8000",
101
101
 
102
102
  // Optional fields
@@ -106,12 +106,6 @@ const { data, error } = await phonic.sts.websocket({
106
106
  voice_id: "meredith",
107
107
  output_format: "mulaw_8000"
108
108
  });
109
-
110
- if (error !== null) {
111
- throw new Error(`Failed to start conversation: ${error.message}`);
112
- }
113
-
114
- const { phonicWebSocket } = data;
115
109
  ```
116
110
 
117
111
  Stream input (user) audio chunks:
package/dist/index.d.mts CHANGED
@@ -96,6 +96,9 @@ type PhonicSTSWebSocketResponseMessage = {
96
96
  } | {
97
97
  type: "is_user_speaking";
98
98
  isUserSpeaking: boolean;
99
+ } | {
100
+ type: "interrupted_response";
101
+ interruptedResponse: string;
99
102
  } | {
100
103
  type: "error";
101
104
  error: {
@@ -116,10 +119,13 @@ type OnErrorCallback = (event: WebSocket.ErrorEvent) => void;
116
119
 
117
120
  declare class PhonicSTSWebSocket {
118
121
  private readonly ws;
122
+ private readonly config;
119
123
  private onMessageCallback;
120
124
  private onCloseCallback;
121
125
  private onErrorCallback;
122
- constructor(ws: WebSocket);
126
+ private buffer;
127
+ private isOpen;
128
+ constructor(ws: WebSocket, config: PhonicSTSConfig);
123
129
  onMessage(callback: OnMessageCallback): void;
124
130
  onClose(callback: OnCloseCallback): void;
125
131
  onError(callback: OnErrorCallback): void;
@@ -138,10 +144,7 @@ declare class PhonicSTSWebSocket {
138
144
  declare class SpeechToSpeech {
139
145
  private readonly phonic;
140
146
  constructor(phonic: Phonic);
141
- private connectToPhonicAPI;
142
- websocket(config: PhonicSTSConfig): DataOrError<{
143
- phonicWebSocket: PhonicSTSWebSocket;
144
- }>;
147
+ websocket(config: PhonicSTSConfig): PhonicSTSWebSocket;
145
148
  }
146
149
 
147
150
  type Voice = {
package/dist/index.d.ts CHANGED
@@ -96,6 +96,9 @@ type PhonicSTSWebSocketResponseMessage = {
96
96
  } | {
97
97
  type: "is_user_speaking";
98
98
  isUserSpeaking: boolean;
99
+ } | {
100
+ type: "interrupted_response";
101
+ interruptedResponse: string;
99
102
  } | {
100
103
  type: "error";
101
104
  error: {
@@ -116,10 +119,13 @@ type OnErrorCallback = (event: WebSocket.ErrorEvent) => void;
116
119
 
117
120
  declare class PhonicSTSWebSocket {
118
121
  private readonly ws;
122
+ private readonly config;
119
123
  private onMessageCallback;
120
124
  private onCloseCallback;
121
125
  private onErrorCallback;
122
- constructor(ws: WebSocket);
126
+ private buffer;
127
+ private isOpen;
128
+ constructor(ws: WebSocket, config: PhonicSTSConfig);
123
129
  onMessage(callback: OnMessageCallback): void;
124
130
  onClose(callback: OnCloseCallback): void;
125
131
  onError(callback: OnErrorCallback): void;
@@ -138,10 +144,7 @@ declare class PhonicSTSWebSocket {
138
144
  declare class SpeechToSpeech {
139
145
  private readonly phonic;
140
146
  constructor(phonic: Phonic);
141
- private connectToPhonicAPI;
142
- websocket(config: PhonicSTSConfig): DataOrError<{
143
- phonicWebSocket: PhonicSTSWebSocket;
144
- }>;
147
+ websocket(config: PhonicSTSConfig): PhonicSTSWebSocket;
145
148
  }
146
149
 
147
150
  type Voice = {
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // package.json
38
- var version = "0.15.0";
38
+ var version = "0.16.0";
39
39
 
40
40
  // src/conversations/index.ts
41
41
  var Conversations = class {
@@ -87,8 +87,21 @@ var import_ws = __toESM(require("ws"));
87
87
 
88
88
  // src/sts/websocket.ts
89
89
  var PhonicSTSWebSocket = class {
90
- constructor(ws) {
90
+ constructor(ws, config) {
91
91
  this.ws = ws;
92
+ this.config = config;
93
+ this.buffer.push(
94
+ JSON.stringify({
95
+ type: "config",
96
+ ...this.config
97
+ })
98
+ );
99
+ this.ws.onopen = () => {
100
+ for (const message of this.buffer) {
101
+ this.ws.send(message);
102
+ }
103
+ this.isOpen = true;
104
+ };
92
105
  this.ws.onmessage = (event) => {
93
106
  if (this.onMessageCallback === null) {
94
107
  return;
@@ -122,6 +135,8 @@ var PhonicSTSWebSocket = class {
122
135
  onMessageCallback = null;
123
136
  onCloseCallback = null;
124
137
  onErrorCallback = null;
138
+ buffer = [];
139
+ isOpen = false;
125
140
  onMessage(callback) {
126
141
  this.onMessageCallback = callback;
127
142
  }
@@ -132,28 +147,37 @@ var PhonicSTSWebSocket = class {
132
147
  this.onErrorCallback = callback;
133
148
  }
134
149
  audioChunk({ audio }) {
135
- this.ws.send(
136
- JSON.stringify({
137
- type: "audio_chunk",
138
- audio
139
- })
140
- );
150
+ const audiochunkMessage = JSON.stringify({
151
+ type: "audio_chunk",
152
+ audio
153
+ });
154
+ if (this.isOpen) {
155
+ this.ws.send(audiochunkMessage);
156
+ } else {
157
+ this.buffer.push(audiochunkMessage);
158
+ }
141
159
  }
142
160
  updateSystemPrompt({ systemPrompt }) {
143
- this.ws.send(
144
- JSON.stringify({
145
- type: "update_system_prompt",
146
- system_prompt: systemPrompt
147
- })
148
- );
161
+ const updateSystemPromptMessage = JSON.stringify({
162
+ type: "update_system_prompt",
163
+ system_prompt: systemPrompt
164
+ });
165
+ if (this.isOpen) {
166
+ this.ws.send(updateSystemPromptMessage);
167
+ } else {
168
+ this.buffer.push(updateSystemPromptMessage);
169
+ }
149
170
  }
150
171
  setExternalId({ externalId }) {
151
- this.ws.send(
152
- JSON.stringify({
153
- type: "set_external_id",
154
- external_id: externalId
155
- })
156
- );
172
+ const setExternalIdMessage = JSON.stringify({
173
+ type: "set_external_id",
174
+ external_id: externalId
175
+ });
176
+ if (this.isOpen) {
177
+ this.ws.send(setExternalIdMessage);
178
+ } else {
179
+ this.buffer.push(setExternalIdMessage);
180
+ }
157
181
  }
158
182
  close(code) {
159
183
  this.ws.close(code ?? 1e3);
@@ -161,64 +185,11 @@ var PhonicSTSWebSocket = class {
161
185
  };
162
186
 
163
187
  // src/sts/index.ts
164
- var phonicApiCloseCodes = {
165
- insuffucientCapacityAvailable: 4004
166
- };
167
188
  var SpeechToSpeech = class {
168
189
  constructor(phonic) {
169
190
  this.phonic = phonic;
170
191
  }
171
- async connectToPhonicAPI(phonicApiWsUrl, config) {
172
- return new Promise((resolve) => {
173
- const ws = new import_ws.default(phonicApiWsUrl, {
174
- headers: this.phonic.headers
175
- });
176
- ws.onopen = () => {
177
- ws.send(
178
- JSON.stringify({
179
- type: "config",
180
- ...config
181
- })
182
- );
183
- };
184
- ws.onmessage = (event) => {
185
- if (typeof event.data !== "string") {
186
- throw new Error("Received non-string message");
187
- }
188
- const dataObj = JSON.parse(
189
- event.data
190
- );
191
- if (dataObj.type === "ready_to_start_conversation") {
192
- resolve({
193
- data: {
194
- phonicWebSocket: new PhonicSTSWebSocket(ws)
195
- },
196
- error: null
197
- });
198
- }
199
- };
200
- ws.onerror = (error) => {
201
- resolve({
202
- data: null,
203
- error: {
204
- message: error.message
205
- }
206
- });
207
- };
208
- ws.onclose = (event) => {
209
- if (event.code === phonicApiCloseCodes.insuffucientCapacityAvailable) {
210
- resolve({
211
- data: null,
212
- error: {
213
- message: event.reason,
214
- code: "insuffucient_capacity_available"
215
- }
216
- });
217
- }
218
- };
219
- });
220
- }
221
- async websocket(config) {
192
+ websocket(config) {
222
193
  const wsBaseUrl = this.phonic.baseUrl.replace(/^http/, "ws");
223
194
  const queryString = new URLSearchParams({
224
195
  ...this.phonic.__downstreamWebSocketUrl !== null && {
@@ -226,31 +197,10 @@ var SpeechToSpeech = class {
226
197
  }
227
198
  }).toString();
228
199
  const phonicApiWsUrl = `${wsBaseUrl}/v1/sts/ws?${queryString}`;
229
- let retryNumber = 0;
230
- const maxRetries = 14;
231
- const retryDelay = 15e3;
232
- while (true) {
233
- const connectResult = await this.connectToPhonicAPI(
234
- phonicApiWsUrl,
235
- config
236
- );
237
- if (connectResult.data !== null) {
238
- return connectResult;
239
- }
240
- if (connectResult.error.code === "insuffucient_capacity_available") {
241
- if (retryNumber >= maxRetries) {
242
- return connectResult;
243
- }
244
- console.info(
245
- `${connectResult.error.message}, will retry in ${retryDelay / 1e3}sec`
246
- );
247
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
248
- retryNumber += 1;
249
- console.info(`Retrying... ${retryNumber}/${maxRetries}`);
250
- continue;
251
- }
252
- return connectResult;
253
- }
200
+ const ws = new import_ws.default(phonicApiWsUrl, {
201
+ headers: this.phonic.headers
202
+ });
203
+ return new PhonicSTSWebSocket(ws, config);
254
204
  }
255
205
  };
256
206
 
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.15.0";
2
+ var version = "0.16.0";
3
3
 
4
4
  // src/conversations/index.ts
5
5
  var Conversations = class {
@@ -51,8 +51,21 @@ import WebSocket from "ws";
51
51
 
52
52
  // src/sts/websocket.ts
53
53
  var PhonicSTSWebSocket = class {
54
- constructor(ws) {
54
+ constructor(ws, config) {
55
55
  this.ws = ws;
56
+ this.config = config;
57
+ this.buffer.push(
58
+ JSON.stringify({
59
+ type: "config",
60
+ ...this.config
61
+ })
62
+ );
63
+ this.ws.onopen = () => {
64
+ for (const message of this.buffer) {
65
+ this.ws.send(message);
66
+ }
67
+ this.isOpen = true;
68
+ };
56
69
  this.ws.onmessage = (event) => {
57
70
  if (this.onMessageCallback === null) {
58
71
  return;
@@ -86,6 +99,8 @@ var PhonicSTSWebSocket = class {
86
99
  onMessageCallback = null;
87
100
  onCloseCallback = null;
88
101
  onErrorCallback = null;
102
+ buffer = [];
103
+ isOpen = false;
89
104
  onMessage(callback) {
90
105
  this.onMessageCallback = callback;
91
106
  }
@@ -96,28 +111,37 @@ var PhonicSTSWebSocket = class {
96
111
  this.onErrorCallback = callback;
97
112
  }
98
113
  audioChunk({ audio }) {
99
- this.ws.send(
100
- JSON.stringify({
101
- type: "audio_chunk",
102
- audio
103
- })
104
- );
114
+ const audiochunkMessage = JSON.stringify({
115
+ type: "audio_chunk",
116
+ audio
117
+ });
118
+ if (this.isOpen) {
119
+ this.ws.send(audiochunkMessage);
120
+ } else {
121
+ this.buffer.push(audiochunkMessage);
122
+ }
105
123
  }
106
124
  updateSystemPrompt({ systemPrompt }) {
107
- this.ws.send(
108
- JSON.stringify({
109
- type: "update_system_prompt",
110
- system_prompt: systemPrompt
111
- })
112
- );
125
+ const updateSystemPromptMessage = JSON.stringify({
126
+ type: "update_system_prompt",
127
+ system_prompt: systemPrompt
128
+ });
129
+ if (this.isOpen) {
130
+ this.ws.send(updateSystemPromptMessage);
131
+ } else {
132
+ this.buffer.push(updateSystemPromptMessage);
133
+ }
113
134
  }
114
135
  setExternalId({ externalId }) {
115
- this.ws.send(
116
- JSON.stringify({
117
- type: "set_external_id",
118
- external_id: externalId
119
- })
120
- );
136
+ const setExternalIdMessage = JSON.stringify({
137
+ type: "set_external_id",
138
+ external_id: externalId
139
+ });
140
+ if (this.isOpen) {
141
+ this.ws.send(setExternalIdMessage);
142
+ } else {
143
+ this.buffer.push(setExternalIdMessage);
144
+ }
121
145
  }
122
146
  close(code) {
123
147
  this.ws.close(code ?? 1e3);
@@ -125,64 +149,11 @@ var PhonicSTSWebSocket = class {
125
149
  };
126
150
 
127
151
  // src/sts/index.ts
128
- var phonicApiCloseCodes = {
129
- insuffucientCapacityAvailable: 4004
130
- };
131
152
  var SpeechToSpeech = class {
132
153
  constructor(phonic) {
133
154
  this.phonic = phonic;
134
155
  }
135
- async connectToPhonicAPI(phonicApiWsUrl, config) {
136
- return new Promise((resolve) => {
137
- const ws = new WebSocket(phonicApiWsUrl, {
138
- headers: this.phonic.headers
139
- });
140
- ws.onopen = () => {
141
- ws.send(
142
- JSON.stringify({
143
- type: "config",
144
- ...config
145
- })
146
- );
147
- };
148
- ws.onmessage = (event) => {
149
- if (typeof event.data !== "string") {
150
- throw new Error("Received non-string message");
151
- }
152
- const dataObj = JSON.parse(
153
- event.data
154
- );
155
- if (dataObj.type === "ready_to_start_conversation") {
156
- resolve({
157
- data: {
158
- phonicWebSocket: new PhonicSTSWebSocket(ws)
159
- },
160
- error: null
161
- });
162
- }
163
- };
164
- ws.onerror = (error) => {
165
- resolve({
166
- data: null,
167
- error: {
168
- message: error.message
169
- }
170
- });
171
- };
172
- ws.onclose = (event) => {
173
- if (event.code === phonicApiCloseCodes.insuffucientCapacityAvailable) {
174
- resolve({
175
- data: null,
176
- error: {
177
- message: event.reason,
178
- code: "insuffucient_capacity_available"
179
- }
180
- });
181
- }
182
- };
183
- });
184
- }
185
- async websocket(config) {
156
+ websocket(config) {
186
157
  const wsBaseUrl = this.phonic.baseUrl.replace(/^http/, "ws");
187
158
  const queryString = new URLSearchParams({
188
159
  ...this.phonic.__downstreamWebSocketUrl !== null && {
@@ -190,31 +161,10 @@ var SpeechToSpeech = class {
190
161
  }
191
162
  }).toString();
192
163
  const phonicApiWsUrl = `${wsBaseUrl}/v1/sts/ws?${queryString}`;
193
- let retryNumber = 0;
194
- const maxRetries = 14;
195
- const retryDelay = 15e3;
196
- while (true) {
197
- const connectResult = await this.connectToPhonicAPI(
198
- phonicApiWsUrl,
199
- config
200
- );
201
- if (connectResult.data !== null) {
202
- return connectResult;
203
- }
204
- if (connectResult.error.code === "insuffucient_capacity_available") {
205
- if (retryNumber >= maxRetries) {
206
- return connectResult;
207
- }
208
- console.info(
209
- `${connectResult.error.message}, will retry in ${retryDelay / 1e3}sec`
210
- );
211
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
212
- retryNumber += 1;
213
- console.info(`Retrying... ${retryNumber}/${maxRetries}`);
214
- continue;
215
- }
216
- return connectResult;
217
- }
164
+ const ws = new WebSocket(phonicApiWsUrl, {
165
+ headers: this.phonic.headers
166
+ });
167
+ return new PhonicSTSWebSocket(ws, config);
218
168
  }
219
169
  };
220
170
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phonic",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Phonic Node.js SDK",
5
5
  "scripts": {
6
6
  "build": "tsup",