@alia.onl/sdk 13.2.0 → 15.3.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 (48) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +53 -21
  3. package/package.json +9 -5
  4. package/src/components/AliaAvatarIcon.tsx +30 -0
  5. package/src/components/AliaChatContent.tsx +4 -7
  6. package/src/components/AliaChatMessageList.tsx +2 -10
  7. package/src/components/AliaChatScreen.tsx +1 -1
  8. package/src/components/AliaChatSheet.tsx +15 -4
  9. package/src/components/AliaComposer.tsx +62 -16
  10. package/src/components/voice/VoiceControls.tsx +28 -4
  11. package/src/components/voice/VoiceSession.tsx +23 -7
  12. package/src/hooks/useAliaActivity.ts +31 -0
  13. package/src/hooks/useAliaChat.ts +14 -3
  14. package/src/hooks/useAmbientWave.ts +3 -3
  15. package/src/hooks/useAudioLevelMonitor.ts +2 -2
  16. package/src/hooks/useDictation.ts +295 -0
  17. package/src/hooks/useTTS.ts +24 -63
  18. package/src/hooks/useVoiceRoom.ts +148 -736
  19. package/src/index.ts +9 -3
  20. package/src/lib/activity-socket.ts +102 -0
  21. package/src/lib/activity-store.ts +53 -0
  22. package/src/lib/api-url.ts +2 -0
  23. package/src/lib/audio-level.ts +2 -2
  24. package/src/lib/dictation-recorder-types.ts +37 -0
  25. package/src/lib/dictation-recorder.native.ts +90 -0
  26. package/src/lib/dictation-recorder.ts +131 -0
  27. package/src/lib/dictation.ts +208 -0
  28. package/src/lib/labels.tsx +3 -0
  29. package/src/lib/pcm.ts +57 -0
  30. package/src/lib/speech-language.ts +1 -1
  31. package/src/lib/speech-messages.ts +43 -97
  32. package/src/lib/speech-synthesis.ts +5 -1
  33. package/src/lib/turn-state.ts +15 -0
  34. package/src/lib/voice-audio-engine-types.ts +59 -0
  35. package/src/lib/voice-audio-engine.native.ts +232 -0
  36. package/src/lib/voice-audio-engine.ts +350 -0
  37. package/src/lib/voice-call.ts +539 -0
  38. package/src/lib/voice-levels.ts +7 -3
  39. package/src/lib/voice-protocol.ts +309 -0
  40. package/src/types.ts +49 -4
  41. package/src/voice.ts +31 -12
  42. package/tsconfig.json +2 -0
  43. package/src/hooks/useSpeechToText.ts +0 -268
  44. package/src/lib/speech-recognition-types.ts +0 -108
  45. package/src/lib/speech-recognition.native.ts +0 -237
  46. package/src/lib/speech-recognition.ts +0 -308
  47. package/src/lib/voice-text.ts +0 -198
  48. package/src/lib/voice-turn.ts +0 -89
@@ -136,18 +136,9 @@ export function useTTS(options: UseTTSOptions = {}) {
136
136
  reset();
137
137
  }, [reset, releasePlayer]);
138
138
 
139
- /**
140
- * Play a stored clip.
141
- *
142
- * `onUnplayable` is how a caller says "I can make this again". Stored audio
143
- * is not permanent — `tts/` objects age out — and a link to something that is
144
- * gone fails in the player, not at the request, so the only place to notice
145
- * is here. Without a fallback the failure is reported; with one, the caller
146
- * gets to rebuild rather than the person getting an error about a button they
147
- * pressed twice.
148
- */
139
+ /** Play the clip the route answered with. A clip that will not play is a failure, reported. */
149
140
  const playFromUrl = useCallback(
150
- (audioUrl: string, _messageId: string, signal: AbortSignal, onUnplayable?: () => void) => {
141
+ (audioUrl: string, signal: AbortSignal) => {
151
142
  releasePlayer();
152
143
 
153
144
  (async () => {
@@ -199,10 +190,6 @@ export function useTTS(options: UseTTSOptions = {}) {
199
190
  if (signal.aborted || playerRef.current !== player) return;
200
191
  if (status.error) {
201
192
  releasePlayer();
202
- if (onUnplayable) {
203
- onUnplayable();
204
- return;
205
- }
206
193
  setError(status.error);
207
194
  return;
208
195
  }
@@ -216,10 +203,6 @@ export function useTTS(options: UseTTSOptions = {}) {
216
203
  setPlaybackState('playing');
217
204
  } catch {
218
205
  if (signal.aborted) return;
219
- if (onUnplayable) {
220
- onUnplayable();
221
- return;
222
- }
223
206
  setError('Audio playback not available');
224
207
  }
225
208
  })();
@@ -228,7 +211,7 @@ export function useTTS(options: UseTTSOptions = {}) {
228
211
  );
229
212
 
230
213
  const readAloud = useCallback(
231
- async (messageId: string, text: string, conversationId?: string, audioUrl?: string) => {
214
+ async (messageId: string, text: string, conversationId?: string) => {
232
215
  // Read volatile playback state at call time (not via closure) so this
233
216
  // callback's identity survives every playback transition. It is passed to
234
217
  // memoized message rows — an unstable identity re-renders all of them.
@@ -260,51 +243,29 @@ export function useTTS(options: UseTTSOptions = {}) {
260
243
  setPlaybackState('loading');
261
244
 
262
245
  /**
263
- * Ask the server to make it, and play what comes back.
264
- *
265
- * Hoisted out of the cached branch below so a clip that has aged out of
266
- * storage can fall back to it. `playFromUrl` is called WITHOUT a fallback
267
- * here: a freshly made clip that will not play is a real failure, and
268
- * retrying it would be a loop.
246
+ * Always ask the route: it answers a clip this person already had made —
247
+ * same text, voice, speed and model — from storage, without
248
+ * synthesizing it again, and makes it only when there is none. So a
249
+ * second press, here or after a reload, costs nothing and plays at once.
250
+ * `conversationId` (with the message id) files the clip under that
251
+ * conversation, so deleting the conversation deletes it.
269
252
  */
270
- const synthesize = async () => {
271
- const token = getToken();
272
- if (!token) {
273
- throw new Error('Not authenticated');
274
- }
275
-
276
- const audioUrl = await requestSpeechClip({
277
- apiUrl,
278
- token,
279
- model: voiceModel,
280
- input: text,
281
- voice: getTTSVoice(),
282
- speed: getTTSSpeed(),
283
- conversationId,
284
- messageId,
285
- signal: controller.signal,
286
- });
287
- playFromUrl(audioUrl, messageId, controller.signal);
288
- };
289
-
290
- /**
291
- * A stored clip plays straight away, and is remade if it has gone.
292
- *
293
- * The row keeps its key long after a `tts/` object ages out of storage,
294
- * so the link is signed, valid, and points at nothing — a failure only
295
- * the player ever sees. Remaking it is cheaper than keeping every clip
296
- * forever, and the person notices nothing.
297
- */
298
- if (audioUrl) {
299
- playFromUrl(audioUrl, messageId, controller.signal, () => {
300
- void synthesize().catch((e: unknown) => {
301
- if (!controller.signal.aborted) setError(errorMessage(e, 'Failed to read aloud'));
302
- });
303
- });
304
- return;
253
+ const token = getToken();
254
+ if (!token) {
255
+ throw new Error('Not authenticated');
305
256
  }
306
-
307
- await synthesize();
257
+ const audioUrl = await requestSpeechClip({
258
+ apiUrl,
259
+ token,
260
+ model: voiceModel,
261
+ input: text,
262
+ voice: getTTSVoice(),
263
+ speed: getTTSSpeed(),
264
+ conversationId,
265
+ messageId,
266
+ signal: controller.signal,
267
+ });
268
+ playFromUrl(audioUrl, controller.signal);
308
269
  } catch (e: unknown) {
309
270
  if (controller.signal.aborted) return;
310
271
  console.error('[TTS] Error:', e);