ES LEBT!
This commit is contained in:
109
usentrycoach.client/src/ChatClient/ChatControl.tsx
Normal file
109
usentrycoach.client/src/ChatClient/ChatControl.tsx
Normal 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}/>;
|
||||
}
|
||||
Reference in New Issue
Block a user