This commit is contained in:
Simon Lübeß
2025-05-20 16:58:17 +02:00
parent e2764bd984
commit aeb0b87351
8 changed files with 584 additions and 75 deletions

View File

@ -0,0 +1,109 @@
import {useState, useRef, useEffect} from 'react';
function SessionActive({stopSession} : {stopSession: () => void}) {
return <button onClick={stopSession}>Stop session</button>
}
function SessionStopped({startSession} : {startSession: () => void}) {
return <button onClick={startSession}>Start session</button>
}
function SessionControl( { isSessionActive, startSession, stopSession }: { isSessionActive: boolean, startSession : () => void, stopSession : () => void })
{
return (isSessionActive
? <SessionActive stopSession={stopSession}/>
: <SessionStopped startSession={startSession}/>);
}
interface OpenAiToken {
ephemeralToken: string;
expiresAt: number;
}
export function ChatControl() {
const [isSessionActive, setSessionActive] = useState<boolean>(false);
const [dataChannel, setDataChannel] = useState<RTCDataChannel | null>(null);
const audioElement = useRef<HTMLAudioElement>(null);
const peerConnection = useRef<RTCPeerConnection>(null);
async function StartSession() {
// Get a session token for OpenAI Realtime API
const response = await fetch('ephemeral_token');
if (!response.ok) {
throw new Error(response.statusText);
}
const data: OpenAiToken = await response.json();
const ephemeralToken = data.ephemeralToken;
// Create a peer connection
const pc = new RTCPeerConnection();
// Set up to play remote audio from the model
audioElement.current = document.createElement("audio");
audioElement.current.autoplay = true;
pc.ontrack = (e) => {
if (audioElement.current !== null)
{
audioElement.current.srcObject = e.streams[0];
}
}
// Add local audio track for microphone input in the browser
const microphoneStream = await navigator.mediaDevices.getUserMedia({audio: true});
pc.addTrack(microphoneStream.getTracks()[0]);
// Set up data channel for sending and receving events
const dc = pc.createDataChannel("oai-events");
setDataChannel(dc)
// Start the session using the Session Description Protocol (SDP)
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// Start Realtime Session
const baseUrl = "https://api.openai.com/v1/realtime";
const model = "gpt-4o-realtime-preview";
const sdpResponse = await fetch(`${baseUrl}?model=${model}`, {
method: "POST",
body: offer.sdp,
headers: {
Authorization: `Bearer ${ephemeralToken}`,
"Content-Type": "application/sdp",
},
});
const answer:RTCSessionDescriptionInit = {
type: "answer",
sdp: await sdpResponse.text(),
};
await pc.setRemoteDescription(answer);
peerConnection.current = pc;
}
function stopSession() {
if (dataChannel) {
dataChannel.close();
setDataChannel(null);
}
if (peerConnection.current !== null)
{
peerConnection.current.close();
peerConnection.current = null;
}
setSessionActive(false);
}
useEffect(() => {
if (dataChannel) {
dataChannel.addEventListener("open", () => {
setSessionActive(true);
});
}
}, [dataChannel])
return <SessionControl isSessionActive={isSessionActive} startSession={StartSession} stopSession={stopSession}/>;
}