openai_webrtc 0.2.0
openai_webrtc: ^0.2.0 copied to clipboard
webrtc support for openai_core and the OpenAI realtime api
openai_webrtc #
Flutter widgets and helpers to connect to OpenAI's Realtime API over WebRTC. It wraps flutter_webrtc
and openai_core
so you can capture microphone audio, play back the model’s audio, exchange realtime events over a data channel, and use tool calling — all with a simple widget.
Repository: https://github.com/meshagent/openai_webrtc
Features #
- Simple widget:
RealtimeSessionConnection
manages the full WebRTC session lifecycle. - Controller API:
WebRTCSessionController
(extendsRealtimeSessionController
) to send events and observe server events. - Audio in/out: captures mic input and plays remote audio from the model.
- Tool calling: provide
RealtimeFunctionToolHandler
instances viainitialTools
. - Flexible options:
RealtimeSessionOptions
maps directly to Realtime session configuration (model, voice, formats, tool choice, etc.).
Requirements #
- Dart
>=3.8.0
- Flutter (stable channel recommended)
- Platforms supported by
flutter_webrtc
(Android, iOS, macOS, web, desktop)
Platform notes for microphone/audio:
- iOS: Add
NSMicrophoneUsageDescription
toios/Runner/Info.plist
. - Android: Add
<uses-permission android:name="android.permission.RECORD_AUDIO" />
inandroid/app/src/main/AndroidManifest.xml
. - Web: Serve over HTTPS (or
localhost
) forgetUserMedia
to work and grant microphone permission.
Installation #
Add the package to your pubspec.yaml
:
dependencies:
openai_webrtc: ^0.2.0
Then fetch packages:
flutter pub get
Quickstart #
Minimal example showing how to connect, listen for events, and send a message.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:openai_core/openai_core.dart';
import 'package:openai_webrtc/openai_webrtc.dart';
class GetTimeTool extends RealtimeFunctionToolHandler {
GetTimeTool()
: super(
metadata: RealtimeFunctionTool(
name: 'get_current_time',
parameters: {
'type': 'object',
'additionalProperties': false,
'required': [],
'properties': {},
},
),
);
@override
Future<String> execute(controller, arguments) async {
return DateTime.now().toString();
}
}
class RealtimeDemo extends StatelessWidget {
const RealtimeDemo({super.key});
@override
Widget build(BuildContext context) {
final openai = OpenAIClient(apiKey: 'YOUR_OPENAI_API_KEY');
return RealtimeSessionConnection(
openai: openai,
options: RealtimeSessionOptions(
model: RealtimeModel.gpt4oRealtimePreview,
initialTools: [GetTimeTool()],
// voice: SpeechVoice.alloy, // optional
),
onReady: (controller) {
// Observe events from the server
controller.serverEvents
.map((e) => jsonEncode(e.toJson()))
.listen(debugPrint);
// Send a simple text input as a response creation event
controller.send(
RealtimeResponseCreateEvent(
response: RealtimeResponse(
input: [
RealtimeMessageItem(
role: 'user',
content: [RealtimeInputText('hello there!')],
status: null,
),
],
),
),
);
},
builder: (context, controller) {
if (controller == null) {
return const Center(child: CircularProgressIndicator());
}
return const Center(child: Text('Connected to Realtime API'));
},
);
}
}
See a full runnable sample in example/
.
Widgets and APIs #
-
RealtimeSessionConnection
:- Props:
openai
(OpenAIClient
),options
(RealtimeSessionOptions
),onReady
, andbuilder
. - Behavior: establishes a WebRTC peer connection, opens a data channel (
oai-events
), captures mic input, plays remote audio, and exposes aWebRTCSessionController
to your UI. - UI: renders nothing by default; your
builder
decides the UI.
- Props:
-
RealtimeSessionOptions
(selected fields):model
:RealtimeModel
(required)voice
:SpeechVoice
(defaultalloy
)instructions
: optional system promptinputAudioFormat
/outputAudioFormat
: defaultpcm16
inputAudioTranscription
,inputAudioNoiseReduction
,turnDetection
initialTools
:List<RealtimeFunctionToolHandler>
toolChoice
,temperature
,maxResponseOutputTokens
,speed
,tracing
clientSecretAnchor
,clientSecretSeconds
-
WebRTCSessionController
(extendsRealtimeSessionController
):send(RealtimeEvent event)
: send an event over the data channelserverEvents
: stream ofRealtimeEvent
from the model
How it works #
- Creates a Realtime session via
openai_core
. - Uses
flutter_webrtc
to create a peer connection and data channel. - Publishes the local microphone track and plays remote audio.
- Sends and receives Realtime events as JSON over the data channel.
Security note (production) #
The example passes your API key directly to the client to create the Realtime session. For production apps, do not ship your API key. Instead, expose a server endpoint that creates the session on your behalf and return an ephemeral client secret.
Example (Node/Express):
app.get('/realtime-session', async (req, res) => {
const r = await fetch('https://api.openai.com/v1/realtime/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4o-realtime-preview',
voice: 'alloy',
}),
});
res.status(r.status).json(await r.json());
});
Your Flutter app can then call your endpoint to fetch the ephemeral client_secret.value
and use it to complete the SDP exchange. The example/
app demonstrates a direct client-side flow for simplicity.
Troubleshooting #
- Microphone permission denied: ensure platform permissions are added and granted.
- Web requires HTTPS (or
localhost
) for audio capture to work. - No audio playback: check system output device and that remote track is attached (the package uses an offscreen
RTCVideoRenderer
). - ICE/connection failures: verify network conditions and STUN/TURN configuration if you customize the peer connection.
License #
MIT — see LICENSE
.