Skip to content

Commit 199adf2

Browse files
committed
init realtime session
1 parent 67be14a commit 199adf2

File tree

6 files changed

+103
-12
lines changed

6 files changed

+103
-12
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ npm run dev
2929

3030
# or start the server and open the app in a new browser tab
3131
npm run dev -- --open
32+
33+
# or start the server with different .env
34+
npm run dev -- --mode botsharp
3235
```
3336

3437
You can override the `.env` values by creating a local env file named `.env.local` if needed.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @typedef {Object} RealtimeSession
3+
* @property {string} id
4+
* @property {string} object
5+
* @property {string} model
6+
* @property {RealtimeSessionClientSecret} clientSecret
7+
*/
8+
9+
/**
10+
* @typedef {Object} RealtimeSessionClientSecret
11+
* @property {string} value
12+
* @property {BigInt} expiresAt
13+
*/
14+
15+
export default {};

src/lib/services/api-endpoints.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export const endpoints = {
4444
// agent instruct
4545
instructCompletionUrl: `${host}/instruct/{agentId}`,
4646

47+
// agent realtime interaction
48+
agentInitRealtimeSessionUrl: `${host}/agent/{agentId}/realtime/session`,
49+
4750
// router
4851
routerSettingUrl: `${host}/router/settings`,
4952

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { endpoints } from '$lib/services/api-endpoints.js';
2+
import { replaceUrl } from '$lib/helpers/http';
3+
import axios from 'axios';
4+
5+
export const llmRealtime = {
6+
/** @type {RTCPeerConnection} */
7+
pc: new RTCPeerConnection(),
8+
/** @param {string} agentId */
9+
async start(agentId) {
10+
const session = await initRealtimeSession(agentId);
11+
const EPHEMERAL_KEY = session.client_secret.value;
12+
13+
this.pc = new RTCPeerConnection();
14+
15+
// Set up to play remote audio from the model
16+
const audioEl = document.createElement("audio");
17+
audioEl.autoplay = true;
18+
this.pc.ontrack = e => audioEl.srcObject = e.streams[0];
19+
20+
// Add local audio track for microphone input in the browser
21+
const ms = await navigator.mediaDevices.getUserMedia({
22+
audio: true
23+
});
24+
this.pc.addTrack(ms.getTracks()[0]);
25+
26+
// Set up data channel for sending and receiving events
27+
const dc = this.pc.createDataChannel("oai-events");
28+
dc.addEventListener("message", (e) => {
29+
// Realtime server events appear here!
30+
var data = JSON.parse(e.data);
31+
console.log(data);
32+
});
33+
34+
// Start the session using the Session Description Protocol (SDP)
35+
const offer = await this.pc.createOffer();
36+
await this.pc.setLocalDescription(offer);
37+
38+
const baseUrl = "https://api.openai.com/v1/realtime";
39+
const sdpResponse = await fetch(`${baseUrl}?model=${session.model}`, {
40+
method: "POST",
41+
body: offer.sdp,
42+
headers: {
43+
Authorization: `Bearer ${EPHEMERAL_KEY}`,
44+
"Content-Type": "application/sdp"
45+
},
46+
});
47+
48+
/** @type {RTCSessionDescriptionInit} */
49+
const answer = {
50+
type: "answer",
51+
sdp: await sdpResponse.text(),
52+
};
53+
await this.pc.setRemoteDescription(answer);
54+
},
55+
56+
stop() {
57+
// Stop the peer connection
58+
this.pc.close();
59+
}
60+
}
61+
62+
/**
63+
* Execute agent realtime interaction
64+
* @param {string} agentId
65+
* @returns {Promise<import('$realtimeTypes').RealtimeSession>}
66+
*/
67+
export async function initRealtimeSession(agentId) {
68+
let url = replaceUrl(endpoints.agentInitRealtimeSessionUrl, {agentId: agentId});
69+
var response = await axios.get(url);
70+
return response.data;
71+
}

src/routes/chat/[agentId]/[conversationId]/chat-box.svelte

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
} from '$env/static/public';
4747
import { BOT_SENDERS, LEARNER_ID, TRAINING_MODE, USER_SENDERS, ADMIN_ROLES } from '$lib/helpers/constants';
4848
import { signalr } from '$lib/services/signalr-service.js';
49-
import { webSpeech } from '$lib/services/web-speech.js';
49+
import { llmRealtime } from '$lib/services/llm-realtime-service.js';
5050
import { newConversation } from '$lib/services/conversation-service';
5151
import DialogModal from '$lib/common/DialogModal.svelte';
5252
import HeadTitle from '$lib/common/HeadTitle.svelte';
@@ -181,6 +181,7 @@
181181
let isOpenTagModal = false;
182182
let isSendingMsg = false;
183183
let isThinking = false;
184+
let isListening = false;
184185
let isLite = false;
185186
let isFrame = false;
186187
let loadEditor = false;
@@ -470,7 +471,6 @@
470471
471472
/** @param {import('$conversationTypes').ChatResponseModel} message */
472473
function onMessageReceivedFromAssistant(message) {
473-
// webSpeech.utter(message.text);
474474
dialogs.push({
475475
...message,
476476
is_chat_message: true
@@ -658,17 +658,15 @@
658658
async function startListen() {
659659
if (disableSpeech) return;
660660
661-
microphoneIcon = "microphone";
662-
webSpeech.onSpeechToTextDetected = (transcript) => {
663-
if (!!!_.trim(transcript) || isSendingMsg) return;
664-
665-
sendChatMessage(transcript).then(() => {
666-
microphoneIcon = "microphone-off";
667-
}).catch(() => {
668-
microphoneIcon = "microphone-off";
669-
});
661+
if (!isListening) {
662+
llmRealtime.start(params.agentId);
663+
isListening = true;
664+
microphoneIcon = "microphone";
665+
} else {
666+
llmRealtime.stop();
667+
isListening = false;
668+
microphoneIcon = "microphone-off";
670669
}
671-
webSpeech.start();
672670
}
673671
674672
/** @param {any} e */

svelte.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const config = {
1616
$userTypes: './src/lib/helpers/types/userTypes.js',
1717
$roleTypes: './src/lib/helpers/types/roleTypes.js',
1818
$pluginTypes: './src/lib/helpers/types/pluginTypes.js',
19+
$realtimeTypes: './src/lib/helpers/types/realtimeTypes.js',
1920
},
2021

2122
// for static deployment

0 commit comments

Comments
 (0)