@facepe/widget-mobile-app 1.4.0 → 1.5.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 +8 -5
- package/dist/FacePeMobileAvatar.js +40 -23
- package/dist/types.d.ts +16 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -246,7 +246,8 @@ const styles = StyleSheet.create({
|
|
|
246
246
|
|
|
247
247
|
| Prop | Type | Default | Description |
|
|
248
248
|
| :--- | :--- | :--- | :--- |
|
|
249
|
-
| `
|
|
249
|
+
| `agentId` | `string` | first agent assigned to the user | The agent to talk to — its id from the FacePe dashboard (SDK page) or `GET /agents`. |
|
|
250
|
+
| `avatarKey` | `string` | *(deprecated)* | The same as `agentId`, for apps written before agents. |
|
|
250
251
|
| `language` | `string` | `'en'` | Spoken language code (`'en'`, `'hi'`, `'es'`, `'ta'`, `'te'`, etc.). |
|
|
251
252
|
| `style` | `ViewStyle` | `undefined` | Style applied to avatar container. |
|
|
252
253
|
| `videoStyle` | `ViewStyle` | `undefined` | Style applied to native video view. |
|
|
@@ -312,7 +313,7 @@ when the component unmounts.
|
|
|
312
313
|
```json
|
|
313
314
|
{
|
|
314
315
|
"conversationId": "cm_usage_123",
|
|
315
|
-
"
|
|
316
|
+
"agent": { "id": "cm_agent_456", "name": "Front Desk" },
|
|
316
317
|
"language": "en",
|
|
317
318
|
"platform": "android",
|
|
318
319
|
"sdkVersion": "1.3.0",
|
|
@@ -330,7 +331,7 @@ when the component unmounts.
|
|
|
330
331
|
| Field | Meaning |
|
|
331
332
|
|---|---|
|
|
332
333
|
| `conversationId` | The conversation's FacePe id (its usage record) |
|
|
333
|
-
| `
|
|
334
|
+
| `agent` | `{ id, name }` of the agent that held the conversation (`avatar` is the same, kept for older apps) |
|
|
334
335
|
| `language` | The language requested for the conversation |
|
|
335
336
|
| `platform` | `web`, `android` or `ios` |
|
|
336
337
|
| `sdkVersion` | The SDK version that recorded it |
|
|
@@ -338,7 +339,7 @@ when the component unmounts.
|
|
|
338
339
|
| `durationSec` | Length in seconds |
|
|
339
340
|
| `endReason` | `ended` (the user or your code ended it), `disconnected` (the stream dropped), `unmounted` (the component went away) |
|
|
340
341
|
| `transcript` | Every line in order: `{ role: "user" \| "assistant", text, at }` |
|
|
341
|
-
| `form` | For a form avatar: `{ title, complete, fields: [{ label, question, required, value }] }
|
|
342
|
+
| `form` | For a form avatar: `{ title, complete, fields: [{ label, question, required, value }], documents, uploadWhere }` — `documents` are what the avatar asked the customer to upload at the end; `null` for other avatars |
|
|
342
343
|
|
|
343
344
|
For an avatar that **fills a form**, the record also carries the form as the
|
|
344
345
|
conversation filled it: each question with the customer's answer (`value` is
|
|
@@ -354,7 +355,9 @@ For other avatars `form` is `null`.
|
|
|
354
355
|
{ "label": "Name", "question": "Could you please tell me your full name?", "required": true, "value": "Tony" },
|
|
355
356
|
{ "label": "Phone", "question": "What is the best phone number to reach you?", "required": true, "value": "9659390715" },
|
|
356
357
|
{ "label": "Email", "question": "May I have your email address?", "required": false, "value": "tony@gmail.com" }
|
|
357
|
-
]
|
|
358
|
+
],
|
|
359
|
+
"documents": ["ID proof (Aadhaar / PAN)", "Address proof"],
|
|
360
|
+
"uploadWhere": "on our website"
|
|
358
361
|
}
|
|
359
362
|
```
|
|
360
363
|
|
|
@@ -66,8 +66,18 @@ const joinNumberParts = (text) => text.replace(/\d+(?:[ \t]*[-\u2010\u2011\u2013
|
|
|
66
66
|
const digits = m.replace(/\D/g, '');
|
|
67
67
|
return digits.length >= 7 ? digits : m;
|
|
68
68
|
});
|
|
69
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Calls the gateway's agent route; a FacePe server from before agents has
|
|
71
|
+
* only the avatar route (404), so that one is tried next.
|
|
72
|
+
*/
|
|
73
|
+
async function gatewayFetch(base, agentPath, avatarPath, init) {
|
|
74
|
+
const res = await fetch(`${base}${agentPath}`, init);
|
|
75
|
+
return res.status === 404 ? fetch(`${base}${avatarPath}`, init) : res;
|
|
76
|
+
}
|
|
77
|
+
export const FacePeMobileAvatar = forwardRef(({ agentId, avatarKey, language = 'en', style, videoStyle, objectFit = 'cover', mirror = false, autoStart = true, enableMic = true, onTranscript, onSpeakStart, onSpeakEnd, onUserSpeakStart, onUserSpeakEnd, onOrderUpdate, onCheckout, onStatusChange, onError, onConversationEnd, posterUrl, }, ref) => {
|
|
70
78
|
const { token, loading: tokenLoading, error: tokenError, apiBaseUrl, refreshToken } = useFacePeMobileContext();
|
|
79
|
+
// The agent to talk to (`avatarKey` is its name from before agents).
|
|
80
|
+
const chosenAgent = agentId ?? avatarKey;
|
|
71
81
|
// The avatar's portrait, shown until its video arrives. Looked up from the
|
|
72
82
|
// avatars this token may use, unless the app gave one (or null for none).
|
|
73
83
|
const [autoPoster, setAutoPoster] = useState(null);
|
|
@@ -77,13 +87,14 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
77
87
|
let cancelled = false;
|
|
78
88
|
void (async () => {
|
|
79
89
|
try {
|
|
80
|
-
const res = await
|
|
90
|
+
const res = await gatewayFetch(apiBaseUrl.replace(/\/+$/, ''), '/agents', '/avatars', {
|
|
81
91
|
headers: { Authorization: `Bearer ${token}` },
|
|
82
92
|
});
|
|
83
93
|
if (!res.ok)
|
|
84
94
|
return;
|
|
85
|
-
const
|
|
86
|
-
const
|
|
95
|
+
const data = (await res.json())?.data;
|
|
96
|
+
const agents = data?.agents ?? data?.avatars ?? [];
|
|
97
|
+
const avatar = chosenAgent ? agents.find((a) => a.id === chosenAgent) : agents[0];
|
|
87
98
|
if (!cancelled)
|
|
88
99
|
setAutoPoster(avatar?.previewUrl ?? null);
|
|
89
100
|
}
|
|
@@ -94,7 +105,7 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
94
105
|
return () => {
|
|
95
106
|
cancelled = true;
|
|
96
107
|
};
|
|
97
|
-
}, [token, apiBaseUrl,
|
|
108
|
+
}, [token, apiBaseUrl, chosenAgent, posterUrl]);
|
|
98
109
|
const poster = posterUrl !== undefined ? posterUrl : autoPoster;
|
|
99
110
|
const [status, setStatus] = useState('idle');
|
|
100
111
|
const [videoTrack, setVideoTrack] = useState(null);
|
|
@@ -143,7 +154,8 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
143
154
|
const token = tokenRef.current;
|
|
144
155
|
if (conv.conversationId && transcript.length > 0 && token) {
|
|
145
156
|
try {
|
|
146
|
-
const
|
|
157
|
+
const id = encodeURIComponent(conv.conversationId);
|
|
158
|
+
const res = await gatewayFetch(apiBaseUrl.replace(/\/+$/, ''), `/agent-sessions/${id}/form`, `/avatar-sessions/${id}/form`, {
|
|
147
159
|
method: 'POST',
|
|
148
160
|
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
149
161
|
body: JSON.stringify({ transcript: transcript.map(({ role, text }) => ({ role, text })) }),
|
|
@@ -158,7 +170,8 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
158
170
|
try {
|
|
159
171
|
onConversationEndRef.current?.({
|
|
160
172
|
conversationId: conv.conversationId,
|
|
161
|
-
|
|
173
|
+
agent: conv.agent,
|
|
174
|
+
avatar: conv.agent,
|
|
162
175
|
language: conv.language,
|
|
163
176
|
platform: Platform.OS === 'ios' ? 'ios' : Platform.OS === 'android' ? 'android' : 'web',
|
|
164
177
|
sdkVersion: SDK_VERSION,
|
|
@@ -219,7 +232,7 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
219
232
|
return;
|
|
220
233
|
try {
|
|
221
234
|
const cleanBase = apiBaseUrl.replace(/\/+$/, '');
|
|
222
|
-
await
|
|
235
|
+
await gatewayFetch(cleanBase, `/agent-sessions/${uId}/end`, `/avatar-sessions/${uId}/end`, {
|
|
223
236
|
method: 'POST',
|
|
224
237
|
headers: {
|
|
225
238
|
'Content-Type': 'application/json',
|
|
@@ -256,29 +269,30 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
256
269
|
throw new Error('Authentication token not available. Please verify your token provider.');
|
|
257
270
|
}
|
|
258
271
|
const cleanBase = apiBaseUrl.replace(/\/+$/, '');
|
|
259
|
-
//
|
|
260
|
-
let
|
|
261
|
-
if (!
|
|
262
|
-
// No
|
|
263
|
-
const listRes = await
|
|
272
|
+
// The agent to talk to
|
|
273
|
+
let targetAgentId = chosenAgent;
|
|
274
|
+
if (!targetAgentId) {
|
|
275
|
+
// No agent given: use the first one assigned to this token's user.
|
|
276
|
+
const listRes = await gatewayFetch(cleanBase, '/agents', '/avatars', {
|
|
264
277
|
method: 'GET',
|
|
265
278
|
headers: {
|
|
266
279
|
Authorization: `Bearer ${currentToken}`,
|
|
267
280
|
},
|
|
268
281
|
});
|
|
269
282
|
if (listRes.ok) {
|
|
270
|
-
const listData = await listRes.json();
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
273
|
-
|
|
283
|
+
const listData = (await listRes.json())?.data;
|
|
284
|
+
const agents = listData?.agents || listData?.avatars || [];
|
|
285
|
+
if (agents.length > 0) {
|
|
286
|
+
targetAgentId = agents[0].id;
|
|
274
287
|
}
|
|
275
288
|
}
|
|
276
289
|
}
|
|
277
|
-
if (!
|
|
278
|
-
throw new Error('No
|
|
290
|
+
if (!targetAgentId) {
|
|
291
|
+
throw new Error('No agent is assigned to these keys. Create one on the FacePe dashboard (Agents).');
|
|
279
292
|
}
|
|
280
293
|
// Request live streaming session from FacePe SDK backend
|
|
281
|
-
const
|
|
294
|
+
const agentPath = encodeURIComponent(targetAgentId);
|
|
295
|
+
const sessionResp = await gatewayFetch(cleanBase, `/agents/${agentPath}/sessions`, `/avatars/${agentPath}/sessions`, {
|
|
282
296
|
method: 'POST',
|
|
283
297
|
headers: {
|
|
284
298
|
'Content-Type': 'application/json',
|
|
@@ -288,7 +302,7 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
288
302
|
});
|
|
289
303
|
if (!sessionResp.ok) {
|
|
290
304
|
const errBody = await sessionResp.json().catch(() => ({}));
|
|
291
|
-
const errMsg = errBody?.error?.message || errBody?.message || 'Failed to
|
|
305
|
+
const errMsg = errBody?.error?.message || errBody?.message || 'Failed to start the conversation';
|
|
292
306
|
throw new Error(errMsg);
|
|
293
307
|
}
|
|
294
308
|
const sessionPayload = await sessionResp.json();
|
|
@@ -296,7 +310,10 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
296
310
|
usageIdRef.current = sessionInfo?.usageId || null;
|
|
297
311
|
conversationRef.current = {
|
|
298
312
|
conversationId: usageIdRef.current,
|
|
299
|
-
|
|
313
|
+
agent: (() => {
|
|
314
|
+
const who = sessionInfo?.agent ?? sessionInfo?.avatar;
|
|
315
|
+
return { id: String(who?.id ?? targetAgentId), name: String(who?.name ?? '') };
|
|
316
|
+
})(),
|
|
300
317
|
language: language ?? null,
|
|
301
318
|
startedAt: new Date(),
|
|
302
319
|
lines: [],
|
|
@@ -554,7 +571,7 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
554
571
|
}
|
|
555
572
|
}, [
|
|
556
573
|
hasLiveKit,
|
|
557
|
-
|
|
574
|
+
chosenAgent,
|
|
558
575
|
language,
|
|
559
576
|
enableMic,
|
|
560
577
|
apiBaseUrl,
|
package/dist/types.d.ts
CHANGED
|
@@ -35,10 +35,20 @@ export interface FacePeFilledForm {
|
|
|
35
35
|
/** Every required question has an answer. */
|
|
36
36
|
complete: boolean;
|
|
37
37
|
fields: FacePeFormAnswer[];
|
|
38
|
+
/** Documents the customer was asked to upload once the form was done (may be empty). */
|
|
39
|
+
documents: string[];
|
|
40
|
+
/** Where to upload them, as the company wrote it (empty when not set). */
|
|
41
|
+
uploadWhere: string;
|
|
38
42
|
}
|
|
39
43
|
export interface FacePeConversation {
|
|
40
44
|
/** The conversation's id (its FacePe usage record), or null if none was opened. */
|
|
41
45
|
conversationId: string | null;
|
|
46
|
+
/** The agent that held the conversation. */
|
|
47
|
+
agent: {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
};
|
|
51
|
+
/** @deprecated The same as `agent` (kept for apps from before agents). */
|
|
42
52
|
avatar: {
|
|
43
53
|
id: string;
|
|
44
54
|
name: string;
|
|
@@ -84,7 +94,12 @@ export interface TokenProviderParams {
|
|
|
84
94
|
}
|
|
85
95
|
export type TokenProvider = (params: TokenProviderParams) => Promise<string>;
|
|
86
96
|
export interface FacePeMobileAvatarProps {
|
|
87
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* The agent to talk to (its id, from the FacePe dashboard's SDK page or
|
|
99
|
+
* GET /agents). Without one, the first agent assigned to the keys' user.
|
|
100
|
+
*/
|
|
101
|
+
agentId?: string;
|
|
102
|
+
/** @deprecated Use `agentId` (the same id; kept for apps from before agents). */
|
|
88
103
|
avatarKey?: string;
|
|
89
104
|
/** Language to converse in ('en', 'es', 'hi', etc.) */
|
|
90
105
|
language?: string;
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** This package's version, reported in conversation records. Keep in step with package.json. */
|
|
2
|
-
export declare const SDK_VERSION = "1.
|
|
2
|
+
export declare const SDK_VERSION = "1.5.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** This package's version, reported in conversation records. Keep in step with package.json. */
|
|
2
|
-
export const SDK_VERSION = '1.
|
|
2
|
+
export const SDK_VERSION = '1.5.0';
|
package/package.json
CHANGED