@rqmii_immersive/agent-react-sdk 1.0.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.
@@ -0,0 +1,244 @@
1
+ # Rqmii React Agent SDK - Framework Recipes
2
+
3
+ This guide shows integration patterns for Vite, Next.js, and CRA.
4
+
5
+ ## 1. Vite
6
+
7
+ ### Install
8
+
9
+ ```bash
10
+ npm install @rqmii_immersive/agent-react-sdk
11
+ ```
12
+
13
+ ### Env
14
+
15
+ `.env`:
16
+
17
+ ```env
18
+ VITE_FASTAPI_BACKEND_URL=https://api.example.com
19
+ VITE_WS_AUDIO_URL=wss://realtime.example.com
20
+ ```
21
+
22
+ ### App Example
23
+
24
+ ```jsx
25
+ import {
26
+ RqmiiAgentRoom,
27
+ useRqmiiMessages,
28
+ } from "@rqmii_immersive/agent-react-sdk";
29
+
30
+ function UI() {
31
+ const { sendTextMessage } = useRqmiiMessages();
32
+ return <button onClick={() => sendTextMessage("Hi")}>Send</button>;
33
+ }
34
+
35
+ export default function App() {
36
+ return (
37
+ <RqmiiAgentRoom
38
+ wsUrl={import.meta.env.VITE_WS_AUDIO_URL}
39
+ restUrl={import.meta.env.VITE_FASTAPI_BACKEND_URL}
40
+ session={{ mode: "initialize", apiKey: "YOUR_API_KEY", language: "en" }}
41
+ connect
42
+ >
43
+ <UI />
44
+ </RqmiiAgentRoom>
45
+ );
46
+ }
47
+ ```
48
+
49
+ ## 2. Next.js (App Router)
50
+
51
+ Use client components for SDK usage.
52
+
53
+ ### Install
54
+
55
+ ```bash
56
+ npm install @rqmii_immersive/agent-react-sdk
57
+ ```
58
+
59
+ ### Env
60
+
61
+ `.env.local`:
62
+
63
+ ```env
64
+ NEXT_PUBLIC_FASTAPI_BACKEND_URL=https://api.example.com
65
+ NEXT_PUBLIC_WS_AUDIO_URL=wss://realtime.example.com
66
+ ```
67
+
68
+ ### Client Component
69
+
70
+ `app/agent/page.jsx`:
71
+
72
+ ```jsx
73
+ "use client";
74
+
75
+ import {
76
+ RqmiiAgentRoom,
77
+ useRqmiiConnection,
78
+ useRqmiiMessages,
79
+ } from "@rqmii_immersive/agent-react-sdk";
80
+
81
+ function UI() {
82
+ const { connectionState } = useRqmiiConnection();
83
+ const { messages, sendTextMessage } = useRqmiiMessages();
84
+
85
+ return (
86
+ <div>
87
+ <div>{connectionState}</div>
88
+ <button onClick={() => sendTextMessage("Hello")}>Send</button>
89
+ <pre>{JSON.stringify(messages, null, 2)}</pre>
90
+ </div>
91
+ );
92
+ }
93
+
94
+ export default function AgentPage() {
95
+ return (
96
+ <RqmiiAgentRoom
97
+ wsUrl={process.env.NEXT_PUBLIC_WS_AUDIO_URL}
98
+ restUrl={process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}
99
+ session={{ mode: "initialize", apiKey: "YOUR_API_KEY", language: "en" }}
100
+ connect
101
+ >
102
+ <UI />
103
+ </RqmiiAgentRoom>
104
+ );
105
+ }
106
+ ```
107
+
108
+ ### Next.js Notes
109
+
110
+ - Do not use SDK in server components.
111
+ - Keep all SDK consumers under `'use client'`.
112
+ - For cloud avatar player, render it only on client.
113
+
114
+ ## 3. Create React App (CRA)
115
+
116
+ ### Install
117
+
118
+ ```bash
119
+ npm install @rqmii_immersive/agent-react-sdk
120
+ ```
121
+
122
+ ### Env
123
+
124
+ `.env`:
125
+
126
+ ```env
127
+ REACT_APP_FASTAPI_BACKEND_URL=https://api.example.com
128
+ REACT_APP_WS_AUDIO_URL=wss://realtime.example.com
129
+ ```
130
+
131
+ ### App Example
132
+
133
+ ```jsx
134
+ import {
135
+ RqmiiAgentRoom,
136
+ useRqmiiMessages,
137
+ } from "@rqmii_immersive/agent-react-sdk";
138
+
139
+ function UI() {
140
+ const { sendTextMessage } = useRqmiiMessages();
141
+ return <button onClick={() => sendTextMessage("Hello")}>Send</button>;
142
+ }
143
+
144
+ export default function App() {
145
+ return (
146
+ <RqmiiAgentRoom
147
+ wsUrl={process.env.REACT_APP_WS_AUDIO_URL}
148
+ restUrl={process.env.REACT_APP_FASTAPI_BACKEND_URL}
149
+ session={{ mode: "initialize", apiKey: "YOUR_API_KEY", language: "en" }}
150
+ connect
151
+ >
152
+ <UI />
153
+ </RqmiiAgentRoom>
154
+ );
155
+ }
156
+ ```
157
+
158
+ ## 4. Common Recipes
159
+
160
+ ### Cloud Avatar
161
+
162
+ ```jsx
163
+ import {
164
+ RqmiiCloudAvatarPlayer,
165
+ useRqmiiAvatar,
166
+ } from "@rqmii_immersive/agent-react-sdk";
167
+
168
+ function AvatarPanel() {
169
+ const { cloudStreamState, isAvatarSpeaking } = useRqmiiAvatar();
170
+ return (
171
+ <>
172
+ <div>stream: {cloudStreamState}</div>
173
+ <div>speaking: {String(isAvatarSpeaking)}</div>
174
+ <RqmiiCloudAvatarPlayer muted={false} />
175
+ </>
176
+ );
177
+ }
178
+ ```
179
+
180
+ ### Desktop Avatar Join
181
+
182
+ ```jsx
183
+ <RqmiiAgentRoom
184
+ wsUrl={WS_URL}
185
+ restUrl={REST_URL}
186
+ session={{ mode: "join_desktop", uniqueId: "A1B2C3" }}
187
+ connect
188
+ >
189
+ <UI />
190
+ </RqmiiAgentRoom>
191
+ ```
192
+
193
+ ### Manual Provider Control
194
+
195
+ ```jsx
196
+ import {
197
+ RqmiiAgentProvider,
198
+ useRqmiiConnection,
199
+ } from "@rqmii_immersive/agent-react-sdk";
200
+
201
+ function Controls() {
202
+ const { initializeSession, disconnect } = useRqmiiConnection();
203
+ return (
204
+ <>
205
+ <button
206
+ onClick={() => initializeSession({ apiKey: "KEY", language: "en" })}
207
+ >
208
+ Init
209
+ </button>
210
+ <button onClick={disconnect}>Disconnect</button>
211
+ </>
212
+ );
213
+ }
214
+
215
+ export default function App() {
216
+ return (
217
+ <RqmiiAgentProvider wsUrl={WS_URL} restUrl={REST_URL}>
218
+ <Controls />
219
+ </RqmiiAgentProvider>
220
+ );
221
+ }
222
+ ```
223
+
224
+ ## 5. Troubleshooting by Framework
225
+
226
+ ### Vite
227
+
228
+ - Ensure env keys start with `VITE_`.
229
+ - Restart dev server after env changes.
230
+
231
+ ### Next.js
232
+
233
+ - Ensure page/component is client-side (`'use client'`).
234
+ - Use `NEXT_PUBLIC_` env keys.
235
+
236
+ ### CRA
237
+
238
+ - Ensure env keys start with `REACT_APP_`.
239
+ - Restart `npm start` after env changes.
240
+
241
+ ## 6. Where to Read More
242
+
243
+ - Full technical docs: `README.md`
244
+ - Quick docs: `README.simple.md`
package/README.md ADDED
@@ -0,0 +1,430 @@
1
+ # Rqmii React Agent SDK
2
+
3
+ Standalone React SDK for integrating React apps with:
4
+
5
+ - `RqmiiAgentBackendAPI` REST endpoints (`/sessions/init`, `/sessions/join`)
6
+ - `RqmiiAgentBackendJs` realtime Socket.IO server
7
+
8
+ Quick navigation:
9
+
10
+ - Full guide: `README.md` (this file)
11
+ - Simple guide: `README.simple.md`
12
+ - Framework recipes (Vite/Next.js/CRA): `README.frameworks.md`
13
+
14
+ This SDK gives you a LiveKit-style integration model:
15
+
16
+ - room wrapper component (`RqmiiAgentRoom`)
17
+ - provider (`RqmiiAgentProvider`)
18
+ - hooks usable by any child component (`useRqmiiConnection`, `useRqmiiMessages`, `useRqmiiVoice`, `useRqmiiAvatar`)
19
+
20
+ ## 1. Installation
21
+
22
+ ```bash
23
+ npm install @rqmii_immersive/agent-react-sdk
24
+ ```
25
+
26
+ If you are testing locally from this mono-repo:
27
+
28
+ ```bash
29
+ npm install file:../RqmiiAgentReactSDK
30
+ ```
31
+
32
+ ## 2. Required Backend Configuration
33
+
34
+ You must provide both URLs to the SDK:
35
+
36
+ - `restUrl`: FastAPI backend base URL (for session init/join)
37
+ - `wsUrl`: realtime coordinator Socket.IO URL
38
+
39
+ Typical env values:
40
+
41
+ ```env
42
+ VITE_FASTAPI_BACKEND_URL=https://api.example.com
43
+ VITE_WS_AUDIO_URL=wss://realtime.example.com
44
+ ```
45
+
46
+ ## 3. Import Surface
47
+
48
+ From the package root:
49
+
50
+ ```js
51
+ import {
52
+ RqmiiAgentProvider,
53
+ RqmiiAgentRoom,
54
+ RqmiiCloudAvatarPlayer,
55
+ EpicPixelStreamingPlayer,
56
+ useRqmiiAgent,
57
+ useRqmiiConnection,
58
+ useRqmiiMessages,
59
+ useRqmiiVoice,
60
+ useRqmiiAvatar,
61
+ useRqmiiAudioElement,
62
+ useRqmiiPressToTalk,
63
+ useRqmiiVAD,
64
+ AGENT_TYPES,
65
+ CONNECTION_STATE,
66
+ CLOUD_STREAM_STATE,
67
+ SOCKET_EVENTS,
68
+ } from "@rqmii_immersive/agent-react-sdk";
69
+ ```
70
+
71
+ If your bundler requires explicit style import, also import:
72
+
73
+ ```js
74
+ import "@rqmii_immersive/agent-react-sdk/style.css";
75
+ ```
76
+
77
+ ## 4. Quick Start (Room Wrapper)
78
+
79
+ ```jsx
80
+ import {
81
+ RqmiiAgentRoom,
82
+ RqmiiCloudAvatarPlayer,
83
+ useRqmiiConnection,
84
+ useRqmiiMessages,
85
+ useRqmiiAvatar,
86
+ } from "@rqmii_immersive/agent-react-sdk";
87
+
88
+ function ChatUI() {
89
+ const { messages, sendTextMessage } = useRqmiiMessages();
90
+ const { connectionState, isSessionReady, error } = useRqmiiConnection();
91
+ const { isAvatarSpeaking, cloudStreamState } = useRqmiiAvatar();
92
+
93
+ return (
94
+ <div>
95
+ <div>Conn: {connectionState}</div>
96
+ <div>Ready: {String(isSessionReady)}</div>
97
+ <div>Avatar Speaking: {String(isAvatarSpeaking)}</div>
98
+ <div>Cloud Stream: {cloudStreamState}</div>
99
+ {error && <div style={{ color: "red" }}>{error}</div>}
100
+
101
+ <button onClick={() => sendTextMessage("Hello")}>Send</button>
102
+ <pre>{JSON.stringify(messages, null, 2)}</pre>
103
+
104
+ <RqmiiCloudAvatarPlayer muted={false} />
105
+ </div>
106
+ );
107
+ }
108
+
109
+ export default function App() {
110
+ return (
111
+ <RqmiiAgentRoom
112
+ wsUrl={import.meta.env.VITE_WS_AUDIO_URL}
113
+ restUrl={import.meta.env.VITE_FASTAPI_BACKEND_URL}
114
+ session={{ mode: "initialize", apiKey: "YOUR_API_KEY", language: "en" }}
115
+ connect
116
+ onError={(e) => console.error(e)}
117
+ onInitialized={(data) => console.log("session initialized", data)}
118
+ >
119
+ <ChatUI />
120
+ </RqmiiAgentRoom>
121
+ );
122
+ }
123
+ ```
124
+
125
+ ## 5. Agent-Type Integration Flows
126
+
127
+ ### 5.1 Cloud Avatar
128
+
129
+ Use `session.mode = 'initialize'` with `apiKey` (or `publicChatId` / `agentId`).
130
+
131
+ - backend returns `streaming_url`
132
+ - render `RqmiiCloudAvatarPlayer`
133
+ - speaking state comes from avatar events, not audio chunks
134
+
135
+ ```jsx
136
+ <RqmiiAgentRoom
137
+ wsUrl={WS_URL}
138
+ restUrl={REST_URL}
139
+ session={{ mode: "initialize", apiKey: API_KEY, language: "en" }}
140
+ connect
141
+ >
142
+ <RqmiiCloudAvatarPlayer muted={false} />
143
+ </RqmiiAgentRoom>
144
+ ```
145
+
146
+ ### 5.2 Desktop Avatar (User Join)
147
+
148
+ User joins existing desktop avatar session by `uniqueId`:
149
+
150
+ ```jsx
151
+ <RqmiiAgentRoom
152
+ wsUrl={WS_URL}
153
+ restUrl={REST_URL}
154
+ session={{ mode: "join_desktop", uniqueId: "A1B2C3" }}
155
+ connect
156
+ >
157
+ <DesktopUI />
158
+ </RqmiiAgentRoom>
159
+ ```
160
+
161
+ ### 5.3 Text Agent
162
+
163
+ - initialize session
164
+ - send text using `sendTextMessage`
165
+ - read assistant chunks/final from `messages`
166
+
167
+ ### 5.4 Voice / Text-and-Voice Agent
168
+
169
+ - send microphone chunks via `sendAudioChunk`
170
+ - signal utterance end via `endAudioStream`
171
+ - audio playback handled internally via `pcm-stream-player`
172
+
173
+ ## 6. Avatar State Model (Important)
174
+
175
+ `isAvatarSpeaking` is server-event driven only:
176
+
177
+ - starts on `avatar_speaking`
178
+ - ends on `avatar_speaking_done`
179
+
180
+ This means avatar speaking state is independent from PCM playback.
181
+
182
+ For cloud avatar rendering lifecycle, `useRqmiiAvatar()` exposes:
183
+
184
+ - `cloudStreamState`
185
+ - `cloudStreamError`
186
+ - `isCloudStreamConnected`
187
+ - `isCloudStreamLoading`
188
+
189
+ `cloudStreamState` values:
190
+
191
+ - `idle`
192
+ - `connecting`
193
+ - `loading`
194
+ - `connected`
195
+ - `disconnected`
196
+ - `error`
197
+
198
+ ## 7. Reconnection Model
199
+
200
+ The backend uses one-time session JWTs, so default Socket.IO auto-reconnect with the same token is not enough.
201
+
202
+ This SDK implements the coordinator reconnect contract:
203
+
204
+ 1. connect using initial `session_token`
205
+ 2. server emits `reconnect_credential` with `{ sessionId, reconnectToken, role }`
206
+ 3. on disconnect, SDK reconnects with reconnect handshake auth
207
+ 4. on success, server emits `reconnect_success` and rotates token
208
+
209
+ Configurable with `reconnectOptions` on provider/room:
210
+
211
+ ```jsx
212
+ <RqmiiAgentRoom
213
+ wsUrl={WS_URL}
214
+ restUrl={REST_URL}
215
+ reconnectOptions={{
216
+ enabled: true,
217
+ maxAttempts: 8,
218
+ initialDelayMs: 500,
219
+ maxDelayMs: 5000,
220
+ timeoutMs: 8000,
221
+ }}
222
+ session={{ mode: "initialize", apiKey: API_KEY }}
223
+ connect
224
+ >
225
+ <UI />
226
+ </RqmiiAgentRoom>
227
+ ```
228
+
229
+ ## 8. Provider-Only Integration (No Room Wrapper)
230
+
231
+ Use this when you need full manual control:
232
+
233
+ ```jsx
234
+ import {
235
+ RqmiiAgentProvider,
236
+ useRqmiiConnection,
237
+ } from "@rqmii_immersive/agent-react-sdk";
238
+
239
+ function Controller() {
240
+ const { initializeSession, joinDesktopSession, disconnect } =
241
+ useRqmiiConnection();
242
+
243
+ return (
244
+ <div>
245
+ <button
246
+ onClick={() => initializeSession({ apiKey: API_KEY, language: "en" })}
247
+ >
248
+ Init
249
+ </button>
250
+ <button onClick={() => joinDesktopSession({ uniqueId: "A1B2C3" })}>
251
+ Join Desktop
252
+ </button>
253
+ <button onClick={disconnect}>Disconnect</button>
254
+ </div>
255
+ );
256
+ }
257
+
258
+ export default function App() {
259
+ return (
260
+ <RqmiiAgentProvider wsUrl={WS_URL} restUrl={REST_URL}>
261
+ <Controller />
262
+ </RqmiiAgentProvider>
263
+ );
264
+ }
265
+ ```
266
+
267
+ ## 9. Hook Reference
268
+
269
+ ### `useRqmiiConnection()`
270
+
271
+ Returns:
272
+
273
+ - `connectionState`
274
+ - `isConnected`
275
+ - `isSessionReady`
276
+ - `error`
277
+ - `reconnectAttempt`
278
+ - `session`
279
+ - `initializeSession(args)`
280
+ - `joinDesktopSession(args)`
281
+ - `disconnect()`
282
+
283
+ ### `useRqmiiMessages()`
284
+
285
+ Returns:
286
+
287
+ - `messages`
288
+ - `sendTextMessage(text)`
289
+ - `clearMessages()`
290
+
291
+ Message stream behavior:
292
+
293
+ - server chunks update streaming assistant message (`agent_response_chunk`)
294
+ - final response completes that message (`agent_response`)
295
+
296
+ ### `useRqmiiVoice()`
297
+
298
+ Returns:
299
+
300
+ - `sendAudioChunk(audio)`
301
+ - `endAudioStream()`
302
+ - `isAudioPlaying`
303
+ - `transcript`
304
+ - `partialTranscript`
305
+ - `interrupt()`
306
+ - `resumeAudioPlayback()`
307
+ - `attachAudioElement(element)`
308
+
309
+ ### `useRqmiiAvatar()`
310
+
311
+ Returns:
312
+
313
+ - `streamingUrl`
314
+ - `isAvatarSpeaking`
315
+ - `speakingSource` (`'avatar_events'`)
316
+ - `isCloudAvatar`
317
+ - `isDesktopAvatar`
318
+ - `cloudStreamState`
319
+ - `cloudStreamError`
320
+ - `isCloudStreamConnected`
321
+ - `isCloudStreamLoading`
322
+ - `setCloudStreamState(nextState, error?)`
323
+
324
+ ### `useRqmiiAudioElement(audioRef)`
325
+
326
+ Optional helper to bind an audio element and improve autoplay unlock UX.
327
+
328
+ Note: voice playback does not require HTML audio element because SDK uses WebAudio (`pcm-stream-player`).
329
+
330
+ ### `useRqmiiPressToTalk(options?)`
331
+
332
+ Built-in press-and-release voice input mode.
333
+
334
+ - streams mic chunks to backend (`audio_chunk`)
335
+ - sends `audio_end` automatically on release
336
+ - exposes ready-to-spread button handlers via `eventHandlers`
337
+
338
+ Minimal usage:
339
+
340
+ ```jsx
341
+ const { eventHandlers, isRecording } = useRqmiiPressToTalk();
342
+ <button {...eventHandlers}>
343
+ {isRecording ? "Recording..." : "Hold to Talk"}
344
+ </button>;
345
+ ```
346
+
347
+ ### `useRqmiiVAD(options?)`
348
+
349
+ Built-in VAD (voice activity detection) mode using `@ricky0123/vad-react`.
350
+
351
+ - toggle VAD listening with `toggle()`
352
+ - auto interrupts on speech start (configurable)
353
+ - handles pre-speech padding and streaming chunks/end events
354
+
355
+ Minimal usage:
356
+
357
+ ```jsx
358
+ const { toggle, isListening, isSpeaking } = useRqmiiVAD();
359
+ ```
360
+
361
+ ## 10. Cloud Player Wrapper API
362
+
363
+ `RqmiiCloudAvatarPlayer` props:
364
+
365
+ - `className`
366
+ - `style`
367
+ - `autoConnect` (default `true`)
368
+ - `muted` (default `false`)
369
+ - `onConnected()`
370
+ - `onDisconnected()`
371
+ - `onError(error)`
372
+
373
+ Internally it forwards cloud stream lifecycle to context state.
374
+
375
+ ## 11. Error Handling Strategy
376
+
377
+ - fatal session/socket errors populate `error` in connection state
378
+ - `TTS_ERROR` socket errors are treated as non-fatal by default
379
+ - reconnect failures transition to `connectionState = failed`
380
+
381
+ Recommended UI behavior:
382
+
383
+ - show persistent banner for `error`
384
+ - show reconnect indicator while `connectionState === reconnecting`
385
+ - offer retry button that calls `initializeSession` again
386
+
387
+ ## 12. Production Checklist
388
+
389
+ - use secure `https/wss` URLs in production
390
+ - ensure CORS allows browser origins for REST and Socket.IO
391
+ - pass correct API key/public chat id per environment
392
+ - monitor reconnect failures and auth errors in logs
393
+ - handle browser autoplay policies with user gesture before first audio
394
+
395
+ ## 13. Troubleshooting
396
+
397
+ ### Session initializes but socket fails auth
398
+
399
+ Check:
400
+
401
+ - `wsUrl` points to `RqmiiAgentBackendJs`
402
+ - token is passed via SDK flow (do not manually override connect handshake)
403
+ - backend clocks are synchronized for JWT expiry
404
+
405
+ ### Cloud avatar not visible
406
+
407
+ Check:
408
+
409
+ - `/sessions/init` response includes `streaming_url`
410
+ - `RqmiiCloudAvatarPlayer` is rendered
411
+ - network allows signaling/WebRTC paths
412
+
413
+ ### Avatar speaking UI not updating
414
+
415
+ Check:
416
+
417
+ - server emits `avatar_speaking` and `avatar_speaking_done`
418
+ - your UI reads `isAvatarSpeaking` from `useRqmiiAvatar()`
419
+
420
+ ### Voice playback missing for text-and-voice
421
+
422
+ Check:
423
+
424
+ - agent type is not avatar (`cloud_avatar`/`desktop_avatar`)
425
+ - browser has user interaction to unlock audio context
426
+ - `agent_audio_chunk` events are arriving from server
427
+
428
+ ## 14. Current Scope
429
+
430
+ This SDK currently targets React integration and browser runtime. If you need Next.js SSR-specific wrappers or TypeScript declaration generation (`.d.ts`), add them in your package pipeline as a next step.