AI-translated from English; not yet reviewed by a fluent editor.
# Napravite lokalni glasovni razgovor u pregledaču uz OpenAI Realtime
> Napravite lokalni razgovor preko mikrofona pregledača uz OpenAI Realtime i WebRTC. Potpun kod servera i stranice čuva API ključ na serveru i pokazuje proveru zvuka, zaustavljanje mikrofona i obradu grešaka.
By BIG CHANGE Editorial
Published: 2026-10-19T19:20:34.863Z
Updated: 2026-10-19T19:20:34.863Z
Canonical: https://bigchange.ai/blog/openai-realtime-browser-voice-webrtc-guide

AI-generated conceptual illustration by BIG CHANGE; no actual OpenAI interface, browser test or heard reply is depicted.
OpenAI Realtime API omogućava pregledaču slanje zvuka mikrofona i reprodukciju govornog odgovora preko WebRTC-a. Vodič pravi jedan lokalni razgovor uz `gpt-realtime-2.1`. API ključ ostaje u Node serveru na vašem računaru. Potreban je OpenAI API projekat sa pristupom modelu i naplatom; ChatGPT pretplata ne plaća API upotrebu. BIG CHANGE je proverio aktuelnu dokumentaciju i primer koda, ali nije otvorio plaćenu sesiju niti čuo odgovor.
## Velika promena
- **Šta se promenilo:** OpenAI dokumentuje WebRTC putanju za razgovor Realtime u pregledaču, uz lokalni server koji čuva ključ projekta i razmenjuje ponudu veze pregledača za odgovor.
- **Zašto je važno:** Programer može proveriti ceo put od mikrofona do govornog odgovora na jednoj lokalnoj stranici pre razvoja većeg govornog proizvoda. Vlasnik projekta upravlja ključem i plaća API.
- **Na šta paziti:** Uspešna lokalna razmena ne dokazuje spremnost za produkciju. Deljena verzija zahteva autentifikaciju pozivaoca i kontrolu troška; otkazan ili istekao zahtev je možda već stigao do OpenAI-a.
## Pre početka
Instalirajte Node.js 22.6 ili noviji i koristite aktuelan pregledač sa ispravnim mikrofonom i zvučnikom. Mikrofon zahteva bezbedan kontekst; `http://localhost` odgovara ovom lokalnom primeru. Pripremite OpenAI API ključ projekta sa pristupom `gpt-realtime-2.1` i uključenom naplatom. Najpre proverite ograničenja učestalosti i troška. Stranica modela navodi da Free nivo nije podržan. Čuvajte ključ u okruženju serverskog procesa kroz uobičajeni upravljač tajnama ili lokalno shell podešavanje. Ne stavljajte ga u `index.html`, ne commit-ujte ga u `.env` datoteci i ne lepite u transkript. [OpenAI WebRTC vodič](https://developers.openai.com/api/docs/guides/voice-webrtc) dokumentuje razmenu preko pouzdanog servera; [stranica modela](https://developers.openai.com/api/docs/models/gpt-realtime-2.1) navodi aktuelni pristup i cene.
Ovo je objedinjena `/v1/realtime/calls` putanja Realtime API-ja. WebRTC vodič opisuje i GPT-Live preko `/v1/live/sessions`, sa drugačijim protokolom sesije. Koristite navedenu putanju i sadržaj zahteva zajedno. Primer je vezan za vaš računar; lokalne Host i Origin provere ne autentifikuju korisnike deljenog servisa.
1. Napravite prazan direktorijum za primer i iz njega instalirajte Express:
```bash
npm init -y
npm install express
```
Instalacija pravi `package.json` i dodaje zavisnost za web server. Sačuvajte naredne dve datoteke u istom direktorijumu. Kod koristi Node ugrađene `fetch`, `FormData` i `AbortSignal.timeout`; provereno podešavanje zahteva Node.js 22.6 ili noviji.
2. Sačuvajte sledeće kao `server.mjs`. Server prima SDP ponudu pregledača (opis predložene WebRTC veze) i šalje je OpenAI-u sa Realtime podešavanjima. Standardni API ključ čita se samo na ovom serveru.
```javascript
import express from "express";
const app = express();
// This demo accepts only the local page, including protection against DNS rebinding.
app.use((req, res, next) => {
const hosts = ["localhost:3000", "127.0.0.1:3000"];
if (!hosts.includes(req.get("host"))) return res.status(403).send("Local host required");
if (req.method === "POST" && req.get("origin") !== `http://${req.get("host")}`) {
return res.status(403).send("Same-origin local page required");
}
next();
});
app.use(express.text({ type: "application/sdp", limit: "64kb" }));
app.get("/", (_req, res) => res.sendFile("index.html", { root: process.cwd() }));
app.post("/session", async (req, res) => {
if (!process.env.OPENAI_API_KEY) return res.status(500).send("Server API key missing");
if (typeof req.body !== "string" || !req.body.startsWith("v=")) {
return res.status(400).send("Expected an SDP offer");
}
const form = new FormData();
form.set("sdp", req.body);
form.set("session", JSON.stringify({
type: "realtime",
model: "gpt-realtime-2.1",
output_modalities: ["audio"],
audio: { output: { voice: "marin" } },
instructions: "Be a concise, helpful voice assistant. Do not claim access to external tools."
}));
try {
const upstream = await fetch("https://api.openai.com/v1/realtime/calls", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
body: form,
signal: AbortSignal.timeout(25000)
});
const answer = await upstream.text();
if (!upstream.ok) {
console.error("Realtime session rejected", upstream.status);
return res.status(502).send(`OpenAI session request failed (${upstream.status})`);
}
res.type("application/sdp").send(answer);
} catch (error) {
console.error("Realtime session network failure", error.name);
res.status(502).send("Could not reach the Realtime API");
}
});
app.listen(3000, "127.0.0.1", () => console.log("Open http://localhost:3000"));
```
Server se vezuje za `127.0.0.1:3000`, prihvata samo host i POST origin lokalne stranice i vraća SDP odgovor pregledaču. Rok od 25 sekundi za zahtev ka dobavljaču izbor je ovog primera. Istek roka ili prekid pregledača ne dokazuje da OpenAI nije prihvatio zahtev; proverite upotrebu pre ponavljanja neizvesnog pokretanja.
3. Sačuvajte sledeće kao `index.html`. Stranica traži dozvolu mikrofona nakon **Start voice**, šalje ponudu lokalnom serveru i reprodukuje dolazni zvuk. **Stop** zatvara vezu i zaustavlja trake mikrofona, čak i tokom povezivanja. Tajmer od 30 sekundi je takođe izbor primera, ne API garancija.
```html
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Local Realtime voice check</title>
<button id="start">Start voice</button>
<button id="stop" disabled>Stop</button>
<p id="status" role="status">Idle</p>
<audio id="reply" autoplay controls></audio>
<script type="module">
const start = document.querySelector("#start");
const stop = document.querySelector("#stop");
const status = document.querySelector("#status");
const reply = document.querySelector("#reply");
let current;
function closeSession(session = current, message = "Stopped") {
if (!session) return;
session.closed = true;
clearTimeout(session.timer);
session.abort.abort();
session.mic?.getTracks().forEach(track => track.stop());
if (session.pc) session.pc.onconnectionstatechange = null;
session.dc?.close();
session.pc?.close();
if (current === session) {
current = null;
reply.srcObject = null;
start.disabled = false;
stop.disabled = true;
status.textContent = message;
}
}
start.onclick = async () => {
const session = { abort: new AbortController(), closed: false };
current = session;
start.disabled = true;
stop.disabled = false;
status.textContent = "Requesting microphone";
session.timer = setTimeout(() => closeSession(session, "Setup timed out; microphone stopped"), 30000);
try {
const mic = await navigator.mediaDevices.getUserMedia({ audio: true });
if (session.closed) { mic.getTracks().forEach(track => track.stop()); return; }
session.mic = mic;
const pc = session.pc = new RTCPeerConnection();
pc.ontrack = event => { if (!session.closed) reply.srcObject = event.streams[0]; };
pc.onconnectionstatechange = () => {
if (session.closed) return;
status.textContent = `Connection: ${pc.connectionState}`;
if (pc.connectionState === "connected") clearTimeout(session.timer);
if (["failed", "closed", "disconnected"].includes(pc.connectionState)) {
closeSession(session, "Connection ended; microphone stopped. Start a new session.");
}
};
pc.addTrack(mic.getAudioTracks()[0], mic);
const dc = session.dc = pc.createDataChannel("oai-events");
dc.onmessage = message => {
if (session.closed) return;
try {
const event = JSON.parse(message.data);
if (event.type === "session.created") status.textContent = "Session ready: speak when connected";
if (event.type === "response.done") {
status.textContent = `Response status: ${event.response?.status || "unknown"}; verify audible playback`;
}
if (event.type === "error") closeSession(session, `Realtime error: ${event.error?.message || "unknown"}; stopped`);
} catch { closeSession(session, "Invalid event; stopped"); }
};
dc.onclose = () => { if (!session.closed) closeSession(session, "Event channel closed; stopped"); };
dc.onerror = () => closeSession(session, "Event channel failed; stopped");
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
if (session.closed) return;
// The official WebRTC example submits the generated offer directly.
const response = await fetch("/session", {
method: "POST",
headers: { "Content-Type": "application/sdp" },
body: offer.sdp,
signal: session.abort.signal
});
if (!response.ok) throw new Error(await response.text());
const answer = await response.text();
if (session.closed) return;
await pc.setRemoteDescription({ type: "answer", sdp: answer });
if (!session.closed && pc.connectionState !== "connected") status.textContent = "Signaling complete; waiting for connection";
} catch (error) {
if (!session.closed) closeSession(session, `Start failed: ${error.message}`);
}
};
stop.onclick = () => closeSession();
window.addEventListener("pagehide", () => closeSession());
</script>
</html>
```
Pregledač zatvara sesiju i kod kvara druge strane ili kanala događaja i pri napuštanju stranice. Dozvola mikrofona može stići posle zaustavljanja ili isteka roka; zakašnjela traka se zaustavlja. Statusni red prikazuje `response.done` sa prijavljenim stanjem. Sam događaj ne dokazuje da se odgovor čuo niti da je otkazan ili neuspešan odgovor uspeo.
4. Pokrenite server iz direktorijuma obe datoteke, sa `OPENAI_API_KEY` već podešenim u okruženju tog procesa:
```bash
node server.mjs
```
Otvorite `http://localhost:3000`. Treba da vidite **Start voice**, **Stop**, stanje **Idle** i audio-plejer. Kliknite **Start voice**, dozvolite mikrofon i izgovorite kratko pitanje bez osetljivih podataka. OpenAI [vodič za Realtime razgovore](https://developers.openai.com/api/docs/guides/realtime-conversations) kaže da podrazumevano prepoznavanje govorne aktivnosti može pokrenuti odgovor nakon pauze. Za uspešno sopstveno pokretanje proverite aktivan mikrofon, vezu u stanju `connected`, prikazan događaj `session.created`, zvuk govora i da `response.done` prijavljuje dovršen odgovor. Kliknite **Stop** i proverite da se indikator mikrofona ugasio. Ove provere obavljate vi; BIG CHANGE nije pokrenuo ovu plaćenu sesiju.
## Ako provera ne uspe
Ako je mikrofon odbijen ili nema ulaznog uređaja, proverite dozvole i uređaj, pa koristite lokalni `localhost` URL ili HTTPS. Ako stranica prijavi `/session` grešku, proverite serverski HTTP status, ključ, pristup modelu, naplatu, ograničenja i endpoint. Server namerno ne ispisuje ključ niti OpenAI telo odgovora. Status 502 može biti mrežna greška ili rok 25 sekundi; ne govori da li je neizvestan zahtev prihvaćen.
Ako signalizacija uspe, a veza ne, proverite status stranice, mrežna ograničenja i vraćeni SDP odgovor. Za aktivnu ali tihu vezu proverite mikrofon i izabrani izlaz. Pregledač može blokirati automatsku reprodukciju: kliknite **Play** na audio-kontroli, pa po potrebi zaustavite i otvorite novu sesiju. Pri svakom neuspehu koristite **Stop** i potvrdite ugašen indikator mikrofona. Proverite upotrebu pre ponovljenih pokretanja sa neizvesnim ishodom zahteva.
## Trošak, privatnost i ograničenja
OpenAI za `gpt-realtime-2.1` navodi tekstualni ulaz/izlaz bez keša po **$4/$24 za milion tokena** i audio-ulaz/izlaz po **$32/$64 za milion tokena**, provereno 19. oktobra 2026. Trošak zavisi od tokena, uključujući kasnije poteze sa prethodnim kontekstom. Pre upotrebe proverite [cene i ograničenja modela](https://developers.openai.com/api/docs/models/gpt-realtime-2.1) i upotrebu projekta; samo trajanje nije dovoljno za procenu računa. [Vodič za Realtime razgovore](https://developers.openai.com/api/docs/guides/realtime-conversations) navodi najviše 60 minuta po sesiji.
Zvuk mikrofona tokom sesije ide OpenAI-u. Koristite govor koji smete da pošaljete i izbegavajte privatni ili tuđi zvuk. Lokalna stranica nema prijavu ni budžet po korisniku. Deljeni servis zahteva autentifikovan, ograničen `/session` pristup i bezbednosni identifikator izveden na serveru; lokalne Host i Origin provere nisu dovoljne. Primer ne poziva spoljne alate i ne telefonira. Nisu mereni kašnjenje, pouzdanost, kvalitet govora, uštede niti rezultat produkcionog puštanja.
## Izvori i dodatna literatura
- [OpenAI WebRTC vodič](https://developers.openai.com/api/docs/guides/voice-webrtc): objedinjena serverska Realtime razmena, zvuk pregledača i zaseban GPT-Live protokol.
- [OpenAI vodič za Realtime razgovore](https://developers.openai.com/api/docs/guides/realtime-conversations): događaji sesije, potezi, status odgovora i trajanje.
- [OpenAI `gpt-realtime-2.1` stranica modela](https://developers.openai.com/api/docs/models/gpt-realtime-2.1): pristup modelu, cene tokena i ograničenja. Ponovo proverite cene i pristup pri pokretanju.
## Sources
- [OpenAI WebRTC vodič](https://developers.openai.com/api/docs/guides/voice-webrtc) — Realtime objedinjeni calls interfejs, pouzdan ključ, SDP, zvuk pregledača i zaseban GPT-Live protokol.
- [OpenAI Realtime razgovori](https://developers.openai.com/api/docs/guides/realtime-conversations) — Događaji sesije, VAD, status odgovora i trajanje 60 minuta.
- [OpenAI gpt-realtime-2.1 model](https://developers.openai.com/api/docs/models/gpt-realtime-2.1) — Cene tokena i pristup za konkretan model.
- [Optimizacija OpenAI glasovnih troškova](https://developers.openai.com/api/docs/guides/voice-latency-cost) — Kontekst i upotreba po odgovoru.
- [OpenAI API i ChatGPT naplata](https://help.openai.com/en/articles/9039756-managing-billing-for-chatgpt-and-the-api-platform) — Odvojena naplata.
Bilten BIG CHANGE
Šira slika. Vašim tempom.
Nedavne priče o veštačkoj inteligenciji i robotici, promene vredne praćenja i praktične ideje za primenu. Izaberite dnevni pregled, nedeljni sažetak ili mesečnu perspektivu.
Next scheduled send (UTC): . Your first edition arrives at the next scheduled send after you confirm.
Vaša privatnost, vaš izbor.
Neophodno skladište doprinosi bezbednosti sajta i pamti vaše izbore. Opcioni Google Analytics ostaje isključen dok ga ne dozvolite. Sve članke možete čitati samo uz neophodno skladištenje podataka. Detalji o privatnosti