@keyframelabs/elements 0.0.3 → 0.0.5

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
@@ -1,8 +1,14 @@
1
1
  # @keyframelabs/elements
2
2
 
3
- Headless vanilla TypeScript library for embedding Persona avatars. Handles video/audio elements, session wiring, microphone input, and voice agent connections.
3
+ Framework-agnostic primitives for Keyframe Labs.
4
4
 
5
- No UI included that's your job (or use `@keyframelabs/components-react`).
5
+ **Which package should I use?**
6
+ - **@keyframelabs/sdk** (high control)
7
+ - You implement the UI, state management, and agent/llm binding yourself
8
+ - **@keyframelabs/elements** (custom UI)
9
+ - You implement the UI; we handle the state and agent/llm binding (framework-agnostic)
10
+ - **@keyframelabs/react**: (drop-in)
11
+ - We handle the UI, state, and agent/llm binding
6
12
 
7
13
  ## Install
8
14
 
@@ -12,13 +18,21 @@ pnpm add @keyframelabs/elements
12
18
 
13
19
  ## Usage
14
20
 
21
+ This package provides two primary classes depending on your integration strategy:
22
+ 1. **`PersonaEmbed`**: Fully managed. Uses a publishable key. Best for rapid frontend integration.
23
+ 2. **`PersonaView`**: Bring your own backend. Uses session tokens generated by your server.
24
+
25
+ ### Option A: PersonaEmbed (managed)
26
+
27
+ Use this if you have configured an embed in the Keyframe platform dashboard.
28
+
15
29
  ```ts
16
30
  import { PersonaEmbed } from '@keyframelabs/elements';
17
31
 
18
32
  const embed = new PersonaEmbed({
19
- container: document.getElementById('avatar')!,
33
+ container: document.getElementById('persona-container'),
20
34
  publishableKey: 'kfl_pk_live_...',
21
- videoFit: 'contain', // 'cover' (default) or 'contain'
35
+ videoFit: 'cover',
22
36
  onStateChange: (status) => console.log('Status:', status),
23
37
  onAgentStateChange: (state) => console.log('Agent:', state),
24
38
  onDisconnect: () => console.log('Disconnected'),
@@ -28,49 +42,116 @@ const embed = new PersonaEmbed({
28
42
  await embed.connect();
29
43
 
30
44
  // Later...
31
- embed.toggleMute();
32
45
  embed.disconnect();
33
46
  ```
34
47
 
35
- ## API
48
+ ### Option B: PersonaView (bring your own backend)
49
+
50
+ Use this when you want to manage authentication through your own backend.
51
+
52
+ ```ts
53
+ import { PersonaView } from '@keyframelabs/elements';
54
+
55
+ const view = new PersonaView({
56
+ container: document.getElementById('persona-container')!,
57
+ sessionDetails,
58
+ voiceAgentDetails,
59
+ videoFit: 'contain',
60
+ onStateChange: (status) => console.log('Status:', status),
61
+ onAgentStateChange: (state) => console.log('Agent:', state),
62
+ onDisconnect: () => console.log('Disconnected'),
63
+ onError: (err) => console.error(err),
64
+ });
65
+
66
+ await view.connect();
36
67
 
37
- Create your embed config at platform.keyframelabs.com to get a publishable key.
68
+ // Later...
69
+ view.disconnect();
70
+ ```
71
+
72
+ ## Supported agents and real-time LLMs
73
+
74
+ Supports ElevenLabs Agents, Cartesia Line, and Gemini Live.
75
+
76
+ For `PersonaEmbed`, this is determined by the values you set in the Keyframe platform dashboard.
77
+
78
+ For `PersonaView`, this is determined by `voiceAgentDetails`.
79
+
80
+ ## API
38
81
 
39
82
  ### `PersonaEmbed`
40
83
 
41
- #### Constructor Options
84
+ #### Options
42
85
 
43
- | Option | Type | Default | Description |
44
- |--------|------|---------|-------------|
45
- | `container` | `HTMLElement` | required | Target container for video/audio elements |
46
- | `publishableKey` | `string` | required | Your embed config publishable key |
47
- | `apiBaseUrl` | `string` | production | API endpoint |
48
- | `videoFit` | `'cover' \| 'contain'` | `'cover'` | Video scaling mode |
49
- | `onStateChange` | `(status: EmbedStatus) => void` | - | Status callback |
50
- | `onAgentStateChange` | `(state: AgentState) => void` | - | Agent state callback |
51
- | `onDisconnect` | `() => void` | - | Disconnect callback |
52
- | `onError` | `(err: Error) => void` | - | Error callback |
86
+ | Option | Type | Default | Description |
87
+ | -------------------- | ------------------------------- | -------------------------------- | ---------------------------------------------- |
88
+ | `container` | `HTMLElement` | Required | Target container for video and audio elements. |
89
+ | `publishableKey` | `string` | Required | Your publishable embed key. |
90
+ | `apiBaseUrl` | `string` | `'https://api.keyframelabs.com'` | Base URL for the Keyframe API. |
91
+ | `videoFit` | `'cover' \| 'contain'` | `'cover'` | Video scaling mode (`object-fit`). |
92
+ | `onStateChange` | `(status: EmbedStatus) => void` | | Fired when connection status changes. |
93
+ | `onAgentStateChange` | `(state: AgentState) => void` | | Fired when agent state changes. |
94
+ | `onDisconnect` | `() => void` | | Fired when the session disconnects. |
95
+ | `onError` | `(err: Error) => void` | | Fired on fatal errors. |
96
+
97
+ #### Methods
98
+
99
+ | Method | Signature | Description |
100
+ | ------------ | --------------------- | -------------------------------------------------- |
101
+ | `connect` | `() => Promise<void>` | Starts the session and establishes the connection. |
102
+ | `disconnect` | `() => void` | Disconnects the session and cleans up resources. |
103
+ | `toggleMute` | `() => void` | Toggles the user's microphone mute state. |
53
104
 
54
105
  #### Properties
55
106
 
56
- | Property | Type | Description |
57
- |----------|------|-------------|
58
- | `status` | `EmbedStatus` | `'connecting' \| 'connected' \| 'error' \| 'disconnected'` |
59
- | `agentState` | `AgentState` | `'idle' \| 'listening' \| 'thinking' \| 'speaking'` |
60
- | `isMuted` | `boolean` | Microphone mute state |
61
- | `videoElement` | `HTMLVideoElement` | The video element |
62
- | `audioElement` | `HTMLAudioElement` | The audio element |
107
+ | Property | Type | Description |
108
+ | -------------- | ------------------ | -------------------------------------------------------------------------------------- |
109
+ | `status` | `EmbedStatus` | Current connection status: `'connecting' \| 'connected' \| 'disconnected' \| 'error'`. |
110
+ | `agentState` | `AgentState` | Current agent state: `'idle' \| 'listening' \| 'thinking' \| 'speaking'`. |
111
+ | `isMuted` | `boolean` | Whether the microphone is currently muted. |
112
+ | `videoElement` | `HTMLVideoElement` | The underlying video element used for rendering. |
113
+ | `audioElement` | `HTMLAudioElement` | The underlying audio element used for playback. |
114
+
115
+ ### `PersonaView`
116
+
117
+ #### Options
118
+
119
+ | Option | Type | Default | Description |
120
+ | -------------------- | ------------------------------- | --------- | ---------------------------------------------- |
121
+ | `container` | `HTMLElement` | Required | Target container for video and audio elements. |
122
+ | `sessionDetails` | `SessionDetails` | Required | Session configuration from your backend. |
123
+ | `voiceAgentDetails` | `VoiceAgentDetails` | Required | Voice agent configuration from your backend. |
124
+ | `videoFit` | `'cover' \| 'contain'` | `'cover'` | Video scaling mode (`object-fit`). |
125
+ | `onStateChange` | `(status: EmbedStatus) => void` | — | Fired when connection status changes. |
126
+ | `onAgentStateChange` | `(state: AgentState) => void` | — | Fired when agent state changes. |
127
+ | `onDisconnect` | `() => void` | — | Fired when the session disconnects. |
128
+ | `onError` | `(err: Error) => void` | — | Fired on fatal errors. |
63
129
 
64
130
  #### Methods
65
131
 
66
- | Method | Description |
67
- |--------|-------------|
68
- | `connect()` | Connect to session (async) |
69
- | `disconnect()` | Disconnect and cleanup |
70
- | `toggleMute()` | Toggle microphone mute |
132
+ Same as `PersonaEmbed`.
133
+
134
+ #### Properties
135
+
136
+ Same as `PersonaEmbed`.
71
137
 
72
- ## Voice Agents
138
+ #### Types
139
+
140
+ ```ts
141
+ type SessionDetails = {
142
+ server_url: string;
143
+ participant_token: string;
144
+ agent_identity: string;
145
+ };
146
+
147
+ type VoiceAgentDetails = {
148
+ type: 'gemini' | 'elevenlabs' | 'cartesia';
149
+ token?: string; // For gemini, cartesia
150
+ agent_id?: string; // For elevenlabs, cartesia
151
+ signed_url?: string; // For elevenlabs
152
+ };
153
+ ```
73
154
 
74
- Supports Gemini Live, ElevenLabs, and Cartesia agents. The agent type is determined by your embed config.
155
+ ## License
75
156
 
76
- If you need support for new voice agent platforms built in, drop us a line at support@keyframelabs.com
157
+ MIT
@@ -0,0 +1,20 @@
1
+ export type ApiErrorPayload = {
2
+ code?: string;
3
+ message?: string;
4
+ details?: unknown;
5
+ };
6
+ export declare class ApiError extends Error {
7
+ status: number;
8
+ payload?: ApiErrorPayload;
9
+ url?: string;
10
+ constructor(opts: {
11
+ message: string;
12
+ status: number;
13
+ payload?: ApiErrorPayload;
14
+ url?: string;
15
+ });
16
+ }
17
+ export type ApiErrorResponse = {
18
+ detail?: ApiErrorPayload | string;
19
+ };
20
+ export declare function normalizeFastApiPayload(x: ApiErrorResponse | null): ApiErrorPayload | undefined;
@@ -1,7 +1,7 @@
1
1
  import { AgentState } from './agents';
2
- export type EmbedStatus = 'connecting' | 'connected' | 'error' | 'disconnected';
3
- export type VideoFit = 'cover' | 'contain';
4
- export interface PersonaEmbedOptions {
2
+ import { EmbedStatus, VideoFit, BaseCallbacks } from './types';
3
+ export type { EmbedStatus, VideoFit } from './types';
4
+ export interface PersonaEmbedOptions extends BaseCallbacks {
5
5
  /** Target container element */
6
6
  container: HTMLElement;
7
7
  /** Publishable key from your embed config */
@@ -10,14 +10,6 @@ export interface PersonaEmbedOptions {
10
10
  apiBaseUrl?: string;
11
11
  /** Video fit mode. 'cover' fills container (may crop), 'contain' shows full video (may have black bars). Default: 'cover' */
12
12
  videoFit?: VideoFit;
13
- /** Called when session disconnects */
14
- onDisconnect?: () => void;
15
- /** Called on error */
16
- onError?: (err: Error) => void;
17
- /** Called when status changes */
18
- onStateChange?: (status: EmbedStatus) => void;
19
- /** Called when agent state changes */
20
- onAgentStateChange?: (state: AgentState) => void;
21
13
  }
22
14
  /**
23
15
  * Headless Persona avatar with voice agent integration.
@@ -0,0 +1,67 @@
1
+ import { AgentState } from './agents';
2
+ import { EmbedStatus, VideoFit, VoiceAgentDetails, SessionDetails, BaseCallbacks } from './types';
3
+ export interface PersonaViewOptions extends BaseCallbacks {
4
+ /** Target container element */
5
+ container: HTMLElement;
6
+ /** Voice agent configuration from your backend */
7
+ voiceAgentDetails: VoiceAgentDetails;
8
+ /** Session configuration from your backend */
9
+ sessionDetails: SessionDetails;
10
+ /** Video fit mode. 'cover' fills container (may crop), 'contain' shows full video (may have black bars). Default: 'cover' */
11
+ videoFit?: VideoFit;
12
+ }
13
+ /**
14
+ * Headless Persona avatar with voice agent integration (BYOB mode).
15
+ *
16
+ * Creates video/audio elements and handles all wiring.
17
+ * UI (overlays, controls, status) is the consumer's responsibility.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * // Fetch session details from your own backend
22
+ * const { sessionDetails, voiceAgentDetails } = await myBackend.createSession();
23
+ *
24
+ * const view = new PersonaView({
25
+ * container: document.getElementById('avatar'),
26
+ * sessionDetails,
27
+ * voiceAgentDetails,
28
+ * onStateChange: (status) => updateUI(status),
29
+ * });
30
+ * await view.connect();
31
+ * ```
32
+ */
33
+ export declare class PersonaView {
34
+ private readonly voiceAgentDetails;
35
+ private readonly sessionDetails;
36
+ private readonly callbacks;
37
+ private readonly connectionId;
38
+ private readonly _video;
39
+ private readonly _audio;
40
+ private session;
41
+ private agent;
42
+ private audioContext;
43
+ private processor;
44
+ private stream;
45
+ private _status;
46
+ private _agentState;
47
+ private _isMuted;
48
+ private mounted;
49
+ constructor(options: PersonaViewOptions);
50
+ get status(): EmbedStatus;
51
+ get agentState(): AgentState;
52
+ get isMuted(): boolean;
53
+ get videoElement(): HTMLVideoElement;
54
+ get audioElement(): HTMLAudioElement;
55
+ /** Connect to the session */
56
+ connect(): Promise<void>;
57
+ /** Disconnect and cleanup */
58
+ disconnect(): void;
59
+ /** Toggle microphone mute */
60
+ toggleMute(): void;
61
+ private setStatus;
62
+ private setAgentState;
63
+ private initSession;
64
+ private initMicrophone;
65
+ private connectAgent;
66
+ private cleanup;
67
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export { PersonaEmbed } from './PersonaEmbed';
2
- export type { PersonaEmbedOptions, EmbedStatus, VideoFit } from './PersonaEmbed';
2
+ export type { PersonaEmbedOptions } from './PersonaEmbed';
3
+ export { PersonaView } from './PersonaView';
4
+ export type { PersonaViewOptions } from './PersonaView';
5
+ export type { EmbedStatus, VideoFit, VoiceAgentDetails, SessionDetails, BaseCallbacks, } from './types';
3
6
  export { createAgent, GeminiLiveAgent, ElevenLabsAgent, CartesiaAgent, BaseAgent, AGENT_REGISTRY, getAgentInfo, } from './agents';
4
7
  export type { AgentType, AgentState, AgentConfig, AgentEventMap, Agent, AnyAgent, AgentTypeInfo, GeminiLiveConfig, ElevenLabsConfig, CartesiaConfig, } from './agents';
5
8
  export { floatTo16BitPCM, resamplePcm, base64ToBytes, bytesToBase64, SAMPLE_RATE, createEventEmitter, } from './agents';
9
+ export { ApiError as KeyframeApiError } from './ApiError';
10
+ export type { ApiErrorPayload as KeyframeApiErrorPayload } from './ApiError';