Skip to content

Commit a0ccbe3

Browse files
authored
Update to use the agents SDK api (#27)
* Update to use the agents SDK api * forgot to commit * update taskfile * fix up lint issues
1 parent 47d519c commit a0ccbe3

File tree

12 files changed

+212
-392
lines changed

12 files changed

+212
-392
lines changed

app.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@
4949
"backgroundColor": "#ffffff"
5050
}
5151
],
52-
"expo-font",
53-
"expo-web-browser"
52+
"expo-font"
5453
],
5554
"experiments": {
5655
"typedRoutes": true

app/(start)/index.tsx

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { ConnectionDetails, fetchToken } from '@/hooks/useConnectionDetails';
1+
import { useConnection } from '@/hooks/useConnection';
22
import { useRouter } from 'expo-router';
3-
import { useEffect, useState } from 'react';
3+
import { useEffect } from 'react';
44
import {
55
StyleSheet,
66
View,
@@ -12,37 +12,18 @@ import {
1212

1313
export default function StartScreen() {
1414
const router = useRouter();
15-
16-
let [isConnecting, setConnecting] = useState(false);
17-
let [connectionDetails, setConnectionDetails] = useState<
18-
ConnectionDetails | undefined
19-
>(undefined);
20-
21-
// Fetch token when we're connecting.
22-
useEffect(() => {
23-
if (isConnecting) {
24-
fetchToken().then((details) => {
25-
console.log(details);
26-
setConnectionDetails(details);
27-
if (!details) {
28-
setConnecting(false);
29-
}
30-
});
31-
}
32-
}, [isConnecting]);
15+
const { isConnectionActive, connect } = useConnection();
3316

3417
// Navigate to Assistant screen when we have the connection details.
3518
useEffect(() => {
36-
if (isConnecting && connectionDetails) {
37-
setConnecting(false);
38-
setConnectionDetails(undefined);
19+
if (isConnectionActive) {
3920
router.navigate('../assistant');
4021
}
41-
}, [isConnecting, router, connectionDetails]);
22+
}, [isConnectionActive, router]);
4223

4324
let connectText: string;
4425

45-
if (isConnecting) {
26+
if (isConnectionActive) {
4627
connectText = 'Connecting';
4728
} else {
4829
connectText = 'Start Voice Assistant';
@@ -58,13 +39,13 @@ export default function StartScreen() {
5839

5940
<TouchableOpacity
6041
onPress={() => {
61-
setConnecting(true);
42+
connect();
6243
}}
6344
style={styles.button}
6445
activeOpacity={0.7}
65-
disabled={isConnecting} // Disable button while loading
46+
disabled={isConnectionActive} // Disable button while loading
6647
>
67-
{isConnecting ? (
48+
{isConnectionActive ? (
6849
<ActivityIndicator
6950
size="small"
7051
color="#ffffff"

app/_layout.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'react-native-reanimated';
99

1010
import { useColorScheme } from '@/hooks/useColorScheme';
1111
import { registerGlobals } from '@livekit/react-native';
12+
import { ConnectionProvider } from '@/hooks/useConnection';
1213

1314
// Do required setup for LiveKit React-Native
1415
registerGlobals();
@@ -17,12 +18,14 @@ export default function RootLayout() {
1718
const colorScheme = useColorScheme();
1819

1920
return (
20-
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
21-
<Stack>
22-
<Stack.Screen name="(start)" options={{ headerShown: false }} />
23-
<Stack.Screen name="assistant" options={{ headerShown: false }} />
24-
</Stack>
25-
<StatusBar style="auto" />
26-
</ThemeProvider>
21+
<ConnectionProvider>
22+
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
23+
<Stack>
24+
<Stack.Screen name="(start)" options={{ headerShown: false }} />
25+
<Stack.Screen name="assistant" options={{ headerShown: false }} />
26+
</Stack>
27+
<StatusBar style="auto" />
28+
</ThemeProvider>
29+
</ConnectionProvider>
2730
);
2831
}

app/assistant/index.tsx

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,25 @@ import {
1010
import React, { useCallback, useEffect, useState } from 'react';
1111
import {
1212
AudioSession,
13-
LiveKitRoom,
1413
useIOSAudioManagement,
1514
useLocalParticipant,
1615
useParticipantTracks,
1716
useRoomContext,
1817
VideoTrack,
1918
} from '@livekit/react-native';
20-
import { useConnectionDetails } from '@/hooks/useConnectionDetails';
2119
import { SafeAreaView } from 'react-native-safe-area-context';
2220
import { useRouter } from 'expo-router';
2321
import ControlBar from './ui/ControlBar';
2422
import ChatBar from './ui/ChatBar';
2523
import ChatLog from './ui/ChatLog';
2624
import AgentVisualization from './ui/AgentVisualization';
27-
import useDataStreamTranscriptions from '@/hooks/useDataStreamTranscriptions';
2825
import { Track } from 'livekit-client';
26+
import {
27+
TrackReference,
28+
useSessionMessages,
29+
useTrackToggle,
30+
} from '@livekit/components-react';
31+
import { useConnection } from '@/hooks/useConnection';
2932

3033
export default function AssistantScreen() {
3134
// Start the audio session first.
@@ -40,27 +43,18 @@ export default function AssistantScreen() {
4043
};
4144
}, []);
4245

43-
const connectionDetails = useConnectionDetails();
44-
4546
return (
4647
<SafeAreaView>
47-
<LiveKitRoom
48-
serverUrl={connectionDetails?.url}
49-
token={connectionDetails?.token}
50-
connect={true}
51-
audio={true}
52-
video={false}
53-
>
54-
<RoomView />
55-
</LiveKitRoom>
48+
<RoomView />
5649
</SafeAreaView>
5750
);
5851
}
5952

6053
const RoomView = () => {
6154
const router = useRouter();
62-
55+
const connection = useConnection();
6356
const room = useRoomContext();
57+
6458
useIOSAudioManagement(room, true);
6559

6660
const {
@@ -79,45 +73,41 @@ const RoomView = () => {
7973

8074
const localVideoTrack =
8175
localCameraTrack && isCameraEnabled
82-
? {
76+
? ({
8377
participant: localParticipant,
8478
publication: localCameraTrack,
8579
source: Track.Source.Camera,
86-
}
80+
} satisfies TrackReference)
8781
: localScreenShareTrack.length > 0 && isScreenShareEnabled
8882
? localScreenShareTrack[0]
8983
: null;
9084

91-
// Transcriptions
92-
const transcriptionState = useDataStreamTranscriptions();
93-
const addTranscription = transcriptionState.addTranscription;
85+
// Messages
86+
const { messages, send } = useSessionMessages();
9487
const [isChatEnabled, setChatEnabled] = useState(false);
9588
const [chatMessage, setChatMessage] = useState('');
9689

9790
const onChatSend = useCallback(
9891
(message: string) => {
99-
addTranscription(localParticipantIdentity, message);
92+
send(message);
10093
setChatMessage('');
10194
},
102-
[localParticipantIdentity, addTranscription, setChatMessage]
95+
[setChatMessage, send]
10396
);
10497

10598
// Control callbacks
106-
const onMicClick = useCallback(() => {
107-
localParticipant.setMicrophoneEnabled(!isMicrophoneEnabled);
108-
}, [isMicrophoneEnabled, localParticipant]);
109-
const onCameraClick = useCallback(() => {
110-
localParticipant.setCameraEnabled(!isCameraEnabled);
111-
}, [isCameraEnabled, localParticipant]);
112-
const onScreenShareClick = useCallback(() => {
113-
localParticipant.setScreenShareEnabled(!isScreenShareEnabled);
114-
}, [isScreenShareEnabled, localParticipant]);
99+
const micToggle = useTrackToggle({ source: Track.Source.Microphone });
100+
const cameraToggle = useTrackToggle({ source: Track.Source.Camera });
101+
const screenShareToggle = useTrackToggle({
102+
source: Track.Source.ScreenShare,
103+
});
115104
const onChatClick = useCallback(() => {
116105
setChatEnabled(!isChatEnabled);
117106
}, [isChatEnabled, setChatEnabled]);
118107
const onExitClick = useCallback(() => {
108+
connection.disconnect();
119109
router.back();
120-
}, [router]);
110+
}, [connection, router]);
121111

122112
// Layout positioning
123113
const [containerWidth, setContainerWidth] = useState(
@@ -159,10 +149,7 @@ const RoomView = () => {
159149
}}
160150
>
161151
<View style={styles.spacer} />
162-
<ChatLog
163-
style={styles.logContainer}
164-
transcriptions={transcriptionState.transcriptions}
165-
/>
152+
<ChatLog style={styles.logContainer} messages={messages} />
166153
<ChatBar
167154
style={styles.chatBar}
168155
value={chatMessage}
@@ -194,10 +181,10 @@ const RoomView = () => {
194181
isCameraEnabled,
195182
isScreenShareEnabled,
196183
isChatEnabled,
197-
onMicClick,
198-
onCameraClick,
184+
onMicClick: micToggle.toggle,
185+
onCameraClick: cameraToggle.toggle,
199186
onChatClick,
200-
onScreenShareClick,
187+
onScreenShareClick: screenShareToggle.toggle,
201188
onExitClick,
202189
}}
203190
/>

app/assistant/ui/AgentVisualization.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useVoiceAssistant } from '@livekit/components-react';
1+
import { useAgent } from '@livekit/components-react';
22
import { BarVisualizer, VideoTrack } from '@livekit/react-native';
33
import React, { useCallback, useState } from 'react';
44
import {
@@ -16,17 +16,19 @@ type AgentVisualizationProps = {
1616
const barSize = 0.2;
1717

1818
export default function AgentVisualization({ style }: AgentVisualizationProps) {
19-
const { state, audioTrack, videoTrack } = useVoiceAssistant();
19+
const { state, microphoneTrack, cameraTrack } = useAgent();
2020
const [barWidth, setBarWidth] = useState(0);
2121
const [barBorderRadius, setBarBorderRadius] = useState(0);
22+
2223
const layoutCallback = useCallback((event: LayoutChangeEvent) => {
2324
const { x, y, width, height } = event.nativeEvent.layout;
2425
console.log(x, y, width, height);
2526
setBarWidth(barSize * height);
2627
setBarBorderRadius(barSize * height);
2728
}, []);
28-
let videoView = videoTrack ? (
29-
<VideoTrack trackRef={videoTrack} style={styles.videoTrack} />
29+
30+
let videoView = cameraTrack ? (
31+
<VideoTrack trackRef={cameraTrack} style={styles.videoTrack} />
3032
) : null;
3133
return (
3234
<View style={[style, styles.container]}>
@@ -40,7 +42,7 @@ export default function AgentVisualization({ style }: AgentVisualizationProps) {
4042
barColor: '#FFFFFF',
4143
barBorderRadius: barBorderRadius,
4244
}}
43-
trackRef={audioTrack}
45+
trackRef={microphoneTrack}
4446
style={styles.barVisualizer}
4547
/>
4648
</View>

app/assistant/ui/ChatLog.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { Transcription } from '@/hooks/useDataStreamTranscriptions';
2-
import { useLocalParticipant } from '@livekit/components-react';
1+
import {
2+
ReceivedMessage,
3+
useLocalParticipant,
4+
} from '@livekit/components-react';
35
import { useCallback } from 'react';
46
import {
57
ListRenderItemInfo,
@@ -14,28 +16,30 @@ import Animated, { LinearTransition } from 'react-native-reanimated';
1416

1517
export type ChatLogProps = {
1618
style: StyleProp<ViewStyle>;
17-
transcriptions: Transcription[];
19+
messages: ReceivedMessage[];
1820
};
19-
export default function ChatLog({ style, transcriptions }: ChatLogProps) {
21+
export default function ChatLog({
22+
style,
23+
messages: transcriptions,
24+
}: ChatLogProps) {
2025
const { localParticipant } = useLocalParticipant();
21-
const localParticipantIdentity = localParticipant.identity;
2226

2327
const renderItem = useCallback(
24-
({ item }: ListRenderItemInfo<Transcription>) => {
25-
const isLocalUser = item.identity === localParticipantIdentity;
28+
({ item }: ListRenderItemInfo<ReceivedMessage>) => {
29+
const isLocalUser = item.from === localParticipant;
2630
if (isLocalUser) {
27-
return <UserTranscriptionText text={item.segment.text} />;
31+
return <UserTranscriptionText text={item.message} />;
2832
} else {
29-
return <AgentTranscriptionText text={item.segment.text} />;
33+
return <AgentTranscriptionText text={item.message} />;
3034
}
3135
},
32-
[localParticipantIdentity]
36+
[localParticipant]
3337
);
3438

3539
return (
3640
<Animated.FlatList
3741
renderItem={renderItem}
38-
data={transcriptions}
42+
data={transcriptions.toReversed()}
3943
style={style}
4044
inverted={true}
4145
itemLayoutAnimation={LinearTransition}

0 commit comments

Comments
 (0)