fciannella commited on
Commit
e821a83
·
1 Parent(s): 12cfb7b

setting up the public stun server

Browse files
examples/voice_agent_webrtc_langgraph/pipeline.py CHANGED
@@ -452,6 +452,30 @@ if UI_DIST_DIR.exists():
452
  app.mount("/", StaticFiles(directory=str(UI_DIST_DIR), html=True), name="static")
453
 
454
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  if __name__ == "__main__":
456
  parser = argparse.ArgumentParser(description="WebRTC demo")
457
  parser.add_argument("--host", default="0.0.0.0", help="Host for HTTP server (default: localhost)")
 
452
  app.mount("/", StaticFiles(directory=str(UI_DIST_DIR), html=True), name="static")
453
 
454
 
455
+ @app.get("/rtc-config")
456
+ async def rtc_config():
457
+ """Expose browser RTC ICE configuration based on environment variables.
458
+
459
+ Reads TURN_SERVER_URL, TURN_USERNAME, TURN_PASSWORD and returns a structure
460
+ consumable by the browser: { "iceServers": [ { urls, username?, credential? } ] }.
461
+ Always includes a public STUN as a fallback.
462
+ """
463
+ ice_servers: list[dict] = []
464
+ turn_url = os.getenv("TURN_SERVER_URL") or os.getenv("TURN_URL")
465
+ turn_user = os.getenv("TURN_USERNAME") or os.getenv("TURN_USER")
466
+ turn_pass = os.getenv("TURN_PASSWORD") or os.getenv("TURN_PASS")
467
+ if turn_url:
468
+ server: dict = {"urls": turn_url}
469
+ if turn_user:
470
+ server["username"] = turn_user
471
+ if turn_pass:
472
+ server["credential"] = turn_pass
473
+ ice_servers.append(server)
474
+ # Public STUN fallback to aid connectivity when TURN is not provided
475
+ ice_servers.append({"urls": "stun:stun.l.google.com:19302"})
476
+ return {"iceServers": ice_servers}
477
+
478
+
479
  if __name__ == "__main__":
480
  parser = argparse.ArgumentParser(description="WebRTC demo")
481
  parser.add_argument("--host", default="0.0.0.0", help="Host for HTTP server (default: localhost)")
examples/voice_agent_webrtc_langgraph/ui/src/App.tsx CHANGED
@@ -6,7 +6,7 @@ import { useEffect, useState } from "react";
6
  import { AudioStream } from "./AudioStream";
7
  import { AudioWaveForm } from "./AudioWaveForm";
8
  import { Toaster } from "./components/ui/sonner";
9
- import { RTC_CONFIG, RTC_OFFER_URL, DYNAMIC_PROMPT, POLL_PROMPT_URL, ASSISTANTS_URL } from "./config";
10
  import usePipecatWebRTC from "./hooks/use-pipecat-webrtc";
11
  import { Transcripts } from "./Transcripts";
12
  import WebRTCButton from "./WebRTCButton";
@@ -19,10 +19,11 @@ function App() {
19
  const [assistants, setAssistants] = useState<Array<{ assistant_id: string; name?: string | null; graph_id?: string | null; display_name?: string | null }>>([]);
20
  const [selectedAssistant, setSelectedAssistant] = useState<string | null>(null);
21
  const [selectedAssistantName, setSelectedAssistantName] = useState<string>("Speech to Speech Demo");
 
22
 
23
  const webRTC = usePipecatWebRTC({
24
  url: RTC_OFFER_URL,
25
- rtcConfig: RTC_CONFIG,
26
  onError: (e) => toast.error(e.message),
27
  assistant: selectedAssistant,
28
  });
@@ -77,6 +78,23 @@ function App() {
77
  fetchAssistants();
78
  }, []);
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  // Send current prompt IMMEDIATELY when WebRTC connection is established
81
  useEffect(() => {
82
  if (webRTC.status === "connected" && currentPrompt.trim()) {
 
6
  import { AudioStream } from "./AudioStream";
7
  import { AudioWaveForm } from "./AudioWaveForm";
8
  import { Toaster } from "./components/ui/sonner";
9
+ import { RTC_OFFER_URL, DYNAMIC_PROMPT, POLL_PROMPT_URL, ASSISTANTS_URL } from "./config";
10
  import usePipecatWebRTC from "./hooks/use-pipecat-webrtc";
11
  import { Transcripts } from "./Transcripts";
12
  import WebRTCButton from "./WebRTCButton";
 
19
  const [assistants, setAssistants] = useState<Array<{ assistant_id: string; name?: string | null; graph_id?: string | null; display_name?: string | null }>>([]);
20
  const [selectedAssistant, setSelectedAssistant] = useState<string | null>(null);
21
  const [selectedAssistantName, setSelectedAssistantName] = useState<string>("Speech to Speech Demo");
22
+ const [rtcConfig, setRtcConfig] = useState<ConstructorParameters<typeof RTCPeerConnection>[0]>({});
23
 
24
  const webRTC = usePipecatWebRTC({
25
  url: RTC_OFFER_URL,
26
+ rtcConfig,
27
  onError: (e) => toast.error(e.message),
28
  assistant: selectedAssistant,
29
  });
 
78
  fetchAssistants();
79
  }, []);
80
 
81
+ // Fetch ICE configuration for RTC
82
+ useEffect(() => {
83
+ const fetchRtcConfig = async () => {
84
+ try {
85
+ const res = await fetch("/rtc-config");
86
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
87
+ const data = await res.json();
88
+ setRtcConfig(data || {});
89
+ } catch (e) {
90
+ console.warn("Failed to fetch rtc-config", e);
91
+ // Fallback STUN if server not available
92
+ setRtcConfig({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] });
93
+ }
94
+ };
95
+ fetchRtcConfig();
96
+ }, []);
97
+
98
  // Send current prompt IMMEDIATELY when WebRTC connection is established
99
  useEffect(() => {
100
  if (webRTC.status === "connected" && currentPrompt.trim()) {