phonic 0.6.1 → 0.8.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
@@ -17,7 +17,7 @@ npm i phonic
17
17
 
18
18
  ## Setup
19
19
 
20
- Grab an API key from [Phonic settings](https://phonic.co/settings) and pass it to the Phonic constructor.
20
+ Grab an API key from the [Phonic API Keys](https://phonic.co/api-keys) section and pass it to the Phonic constructor.
21
21
 
22
22
  ```ts
23
23
  import { Phonic } from "phonic";
@@ -38,7 +38,7 @@ if (error === null) {
38
38
  ```
39
39
 
40
40
 
41
- ### Get voice by id
41
+ ### Get voice by ID
42
42
 
43
43
  ```ts
44
44
  const { data, error } = await phonic.voices.get("meredith");
@@ -50,23 +50,10 @@ if (error === null) {
50
50
 
51
51
  ### Speech-to-speech via WebSocket
52
52
 
53
- Open a WebSocket connection:
53
+ To start a conversation, open a WebSocket connection:
54
54
 
55
55
  ```ts
56
- const { data, error } = await phonic.sts.websocket();
57
-
58
- if (error !== null) {
59
- throw new Error(error.message);
60
- }
61
-
62
- // Here we know that the WebSocket connection is open.
63
- const { phonicWebSocket } = data;
64
- ```
65
-
66
- Send config params for the conversation:
67
-
68
- ```ts
69
- phonicWebSocket.config({
56
+ const { data, error } = await phonic.sts.websocket({
70
57
  input_format: "mulaw_8000",
71
58
 
72
59
  // Optional fields
@@ -75,6 +62,12 @@ phonicWebSocket.config({
75
62
  voice_id: "meredith",
76
63
  output_format: "mulaw_8000"
77
64
  });
65
+
66
+ if (error !== null) {
67
+ throw new Error(`Failed to start conversation: ${error.message}`);
68
+ }
69
+
70
+ const { phonicWebSocket } = data;
78
71
  ```
79
72
 
80
73
  Stream input (user) audio chunks:
@@ -132,16 +125,6 @@ phonicWebSocket.onError((event) => {
132
125
  });
133
126
  ```
134
127
 
135
- ## Publish a new version on npm
136
-
137
- 1. `bunx changeset`
138
- 2. `git add .`
139
- 3. `git commit -m "Add changeset"`
140
- 4. `git push`
141
-
142
- This should trigger the `publish` github workflow that will create a Pull Request named "Version Packages".
143
- Once this Pull Request is merged, the new version will be published on npm.
144
-
145
128
  ## License
146
129
 
147
130
  MIT
package/dist/index.d.mts CHANGED
@@ -26,6 +26,8 @@ type PhonicSTSConfig = {
26
26
  output_format?: "pcm_44100" | "mulaw_8000";
27
27
  };
28
28
  type PhonicSTSWebSocketResponseMessage = {
29
+ type: "ready_to_start_conversation";
30
+ } | {
29
31
  type: "input_text";
30
32
  text: string;
31
33
  } | {
@@ -62,7 +64,6 @@ declare class PhonicSTSWebSocket {
62
64
  onMessage(callback: OnMessageCallback): void;
63
65
  onClose(callback: OnCloseCallback): void;
64
66
  onError(callback: OnErrorCallback): void;
65
- config(message: PhonicSTSConfig): void;
66
67
  audioChunk(message: {
67
68
  audio: string;
68
69
  }): void;
@@ -72,7 +73,8 @@ declare class PhonicSTSWebSocket {
72
73
  declare class SpeechToSpeech {
73
74
  private readonly phonic;
74
75
  constructor(phonic: Phonic);
75
- websocket(): DataOrError<{
76
+ private connectToPhonicAPI;
77
+ websocket(config: PhonicSTSConfig): DataOrError<{
76
78
  phonicWebSocket: PhonicSTSWebSocket;
77
79
  }>;
78
80
  }
package/dist/index.d.ts CHANGED
@@ -26,6 +26,8 @@ type PhonicSTSConfig = {
26
26
  output_format?: "pcm_44100" | "mulaw_8000";
27
27
  };
28
28
  type PhonicSTSWebSocketResponseMessage = {
29
+ type: "ready_to_start_conversation";
30
+ } | {
29
31
  type: "input_text";
30
32
  text: string;
31
33
  } | {
@@ -62,7 +64,6 @@ declare class PhonicSTSWebSocket {
62
64
  onMessage(callback: OnMessageCallback): void;
63
65
  onClose(callback: OnCloseCallback): void;
64
66
  onError(callback: OnErrorCallback): void;
65
- config(message: PhonicSTSConfig): void;
66
67
  audioChunk(message: {
67
68
  audio: string;
68
69
  }): void;
@@ -72,7 +73,8 @@ declare class PhonicSTSWebSocket {
72
73
  declare class SpeechToSpeech {
73
74
  private readonly phonic;
74
75
  constructor(phonic: Phonic);
75
- websocket(): DataOrError<{
76
+ private connectToPhonicAPI;
77
+ websocket(config: PhonicSTSConfig): DataOrError<{
76
78
  phonicWebSocket: PhonicSTSWebSocket;
77
79
  }>;
78
80
  }
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.6.1";
38
+ var version = "0.8.0";
39
39
 
40
40
  // src/sts/index.ts
41
41
  var import_ws = __toESM(require("ws"));
@@ -71,7 +71,6 @@ var PhonicSTSWebSocket = class {
71
71
  this.onMessage = this.onMessage.bind(this);
72
72
  this.onClose = this.onClose.bind(this);
73
73
  this.onError = this.onError.bind(this);
74
- this.config = this.config.bind(this);
75
74
  this.audioChunk = this.audioChunk.bind(this);
76
75
  this.close = this.close.bind(this);
77
76
  }
@@ -87,14 +86,6 @@ var PhonicSTSWebSocket = class {
87
86
  onError(callback) {
88
87
  this.onErrorCallback = callback;
89
88
  }
90
- config(message) {
91
- this.ws.send(
92
- JSON.stringify({
93
- type: "config",
94
- ...message
95
- })
96
- );
97
- }
98
89
  audioChunk(message) {
99
90
  this.ws.send(
100
91
  JSON.stringify({
@@ -109,21 +100,43 @@ var PhonicSTSWebSocket = class {
109
100
  };
110
101
 
111
102
  // src/sts/index.ts
103
+ var phonicApiCloseCodes = {
104
+ insuffucientCapacityAvailable: 4004
105
+ };
112
106
  var SpeechToSpeech = class {
113
107
  constructor(phonic) {
114
108
  this.phonic = phonic;
115
109
  }
116
- async websocket() {
110
+ async connectToPhonicAPI(phonicApiWsUrl, config) {
117
111
  return new Promise((resolve) => {
118
- const wsBaseUrl = this.phonic.baseUrl.replace(/^http/, "ws");
119
- const ws = new import_ws.default(`${wsBaseUrl}/v1/sts/ws`, {
112
+ const ws = new import_ws.default(phonicApiWsUrl, {
120
113
  headers: {
121
114
  Authorization: `Bearer ${this.phonic.apiKey}`
122
115
  }
123
116
  });
124
117
  ws.onopen = () => {
125
- const phonicWebSocket = new PhonicSTSWebSocket(ws);
126
- resolve({ data: { phonicWebSocket }, error: null });
118
+ ws.send(
119
+ JSON.stringify({
120
+ type: "config",
121
+ ...config
122
+ })
123
+ );
124
+ };
125
+ ws.onmessage = (event) => {
126
+ if (typeof event.data !== "string") {
127
+ throw new Error("Received non-string message");
128
+ }
129
+ const dataObj = JSON.parse(
130
+ event.data
131
+ );
132
+ if (dataObj.type === "ready_to_start_conversation") {
133
+ resolve({
134
+ data: {
135
+ phonicWebSocket: new PhonicSTSWebSocket(ws)
136
+ },
137
+ error: null
138
+ });
139
+ }
127
140
  };
128
141
  ws.onerror = (error) => {
129
142
  resolve({
@@ -133,8 +146,48 @@ var SpeechToSpeech = class {
133
146
  }
134
147
  });
135
148
  };
149
+ ws.onclose = (event) => {
150
+ if (event.code === phonicApiCloseCodes.insuffucientCapacityAvailable) {
151
+ resolve({
152
+ data: null,
153
+ error: {
154
+ message: event.reason,
155
+ code: "insuffucient_capacity_available"
156
+ }
157
+ });
158
+ }
159
+ };
136
160
  });
137
161
  }
162
+ async websocket(config) {
163
+ const wsBaseUrl = this.phonic.baseUrl.replace(/^http/, "ws");
164
+ const phonicApiWsUrl = `${wsBaseUrl}/v1/sts/ws`;
165
+ let retryNumber = 0;
166
+ const maxRetries = 14;
167
+ const retryDelay = 15e3;
168
+ while (true) {
169
+ const connectResult = await this.connectToPhonicAPI(
170
+ phonicApiWsUrl,
171
+ config
172
+ );
173
+ if (connectResult.data !== null) {
174
+ return connectResult;
175
+ }
176
+ if (connectResult.error.code === "insuffucient_capacity_available") {
177
+ if (retryNumber >= maxRetries) {
178
+ return connectResult;
179
+ }
180
+ console.info(
181
+ `${connectResult.error.message}, will retry in ${retryDelay / 1e3}sec`
182
+ );
183
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
184
+ retryNumber += 1;
185
+ console.info(`Retrying... ${retryNumber}/${maxRetries}`);
186
+ continue;
187
+ }
188
+ return connectResult;
189
+ }
190
+ }
138
191
  };
139
192
 
140
193
  // src/voices/index.ts
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.6.1";
2
+ var version = "0.8.0";
3
3
 
4
4
  // src/sts/index.ts
5
5
  import WebSocket from "ws";
@@ -35,7 +35,6 @@ var PhonicSTSWebSocket = class {
35
35
  this.onMessage = this.onMessage.bind(this);
36
36
  this.onClose = this.onClose.bind(this);
37
37
  this.onError = this.onError.bind(this);
38
- this.config = this.config.bind(this);
39
38
  this.audioChunk = this.audioChunk.bind(this);
40
39
  this.close = this.close.bind(this);
41
40
  }
@@ -51,14 +50,6 @@ var PhonicSTSWebSocket = class {
51
50
  onError(callback) {
52
51
  this.onErrorCallback = callback;
53
52
  }
54
- config(message) {
55
- this.ws.send(
56
- JSON.stringify({
57
- type: "config",
58
- ...message
59
- })
60
- );
61
- }
62
53
  audioChunk(message) {
63
54
  this.ws.send(
64
55
  JSON.stringify({
@@ -73,21 +64,43 @@ var PhonicSTSWebSocket = class {
73
64
  };
74
65
 
75
66
  // src/sts/index.ts
67
+ var phonicApiCloseCodes = {
68
+ insuffucientCapacityAvailable: 4004
69
+ };
76
70
  var SpeechToSpeech = class {
77
71
  constructor(phonic) {
78
72
  this.phonic = phonic;
79
73
  }
80
- async websocket() {
74
+ async connectToPhonicAPI(phonicApiWsUrl, config) {
81
75
  return new Promise((resolve) => {
82
- const wsBaseUrl = this.phonic.baseUrl.replace(/^http/, "ws");
83
- const ws = new WebSocket(`${wsBaseUrl}/v1/sts/ws`, {
76
+ const ws = new WebSocket(phonicApiWsUrl, {
84
77
  headers: {
85
78
  Authorization: `Bearer ${this.phonic.apiKey}`
86
79
  }
87
80
  });
88
81
  ws.onopen = () => {
89
- const phonicWebSocket = new PhonicSTSWebSocket(ws);
90
- resolve({ data: { phonicWebSocket }, error: null });
82
+ ws.send(
83
+ JSON.stringify({
84
+ type: "config",
85
+ ...config
86
+ })
87
+ );
88
+ };
89
+ ws.onmessage = (event) => {
90
+ if (typeof event.data !== "string") {
91
+ throw new Error("Received non-string message");
92
+ }
93
+ const dataObj = JSON.parse(
94
+ event.data
95
+ );
96
+ if (dataObj.type === "ready_to_start_conversation") {
97
+ resolve({
98
+ data: {
99
+ phonicWebSocket: new PhonicSTSWebSocket(ws)
100
+ },
101
+ error: null
102
+ });
103
+ }
91
104
  };
92
105
  ws.onerror = (error) => {
93
106
  resolve({
@@ -97,8 +110,48 @@ var SpeechToSpeech = class {
97
110
  }
98
111
  });
99
112
  };
113
+ ws.onclose = (event) => {
114
+ if (event.code === phonicApiCloseCodes.insuffucientCapacityAvailable) {
115
+ resolve({
116
+ data: null,
117
+ error: {
118
+ message: event.reason,
119
+ code: "insuffucient_capacity_available"
120
+ }
121
+ });
122
+ }
123
+ };
100
124
  });
101
125
  }
126
+ async websocket(config) {
127
+ const wsBaseUrl = this.phonic.baseUrl.replace(/^http/, "ws");
128
+ const phonicApiWsUrl = `${wsBaseUrl}/v1/sts/ws`;
129
+ let retryNumber = 0;
130
+ const maxRetries = 14;
131
+ const retryDelay = 15e3;
132
+ while (true) {
133
+ const connectResult = await this.connectToPhonicAPI(
134
+ phonicApiWsUrl,
135
+ config
136
+ );
137
+ if (connectResult.data !== null) {
138
+ return connectResult;
139
+ }
140
+ if (connectResult.error.code === "insuffucient_capacity_available") {
141
+ if (retryNumber >= maxRetries) {
142
+ return connectResult;
143
+ }
144
+ console.info(
145
+ `${connectResult.error.message}, will retry in ${retryDelay / 1e3}sec`
146
+ );
147
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
148
+ retryNumber += 1;
149
+ console.info(`Retrying... ${retryNumber}/${maxRetries}`);
150
+ continue;
151
+ }
152
+ return connectResult;
153
+ }
154
+ }
102
155
  };
103
156
 
104
157
  // src/voices/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phonic",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "Phonic Node.js SDK",
5
5
  "scripts": {
6
6
  "build": "tsup",
@@ -39,8 +39,8 @@
39
39
  "@biomejs/biome": "1.9.4",
40
40
  "@changesets/changelog-github": "0.5.1",
41
41
  "@changesets/cli": "2.28.1",
42
- "@types/bun": "1.2.3",
43
- "tsup": "8.3.6",
42
+ "@types/bun": "1.2.4",
43
+ "tsup": "8.4.0",
44
44
  "typescript": "5.7.3",
45
45
  "zod": "3.24.2"
46
46
  },