Skip to content

Commit 550ba48

Browse files
committed
feat: Make openai_realtime_dart client to strong-typed
1 parent 9c855f1 commit 550ba48

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+90201
-697
lines changed

packages/openai_dart/oas/main.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'dart:io';
33

44
import 'package:openapi_spec/openapi_spec.dart';
55

6-
/// Generates Chroma API client Dart code from the OpenAPI spec.
6+
/// Generates OpenAI API client Dart code from the OpenAPI spec.
77
/// Official spec: https://github.com/openai/openai-openapi/blob/master/openapi.yaml
88
void main() async {
99
final spec = OpenApi.fromFile(source: 'oas/openapi_curated.yaml');
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
targets:
2+
$default:
3+
builders:
4+
source_gen|combining_builder:
5+
options:
6+
ignore_for_file:
7+
- prefer_final_parameters
8+
- require_trailing_commas
9+
- non_constant_identifier_names
10+
- unnecessary_null_checks
11+
json_serializable:
12+
options:
13+
explicit_to_json: true

packages/openai_realtime_dart/example/openai_realtime_dart_example.dart

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,29 @@ Future<void> main() async {
99
);
1010

1111
// Can set parameters ahead of connecting, either separately or all at once
12-
client.updateSession(instructions: 'You are a great, upbeat friend.');
13-
client.updateSession(voice: 'alloy');
14-
client.updateSession(
15-
turnDetection: {'type': 'none'},
16-
inputAudioTranscription: {'model': 'whisper-1'},
12+
await client.updateSession(instructions: 'You are a great, upbeat friend.');
13+
await client.updateSession(voice: Voice.alloy);
14+
await client.updateSession(
15+
turnDetection: null,
16+
inputAudioTranscription:
17+
const InputAudioTranscriptionConfig(model: 'whisper-1'),
1718
);
1819

1920
// Set up event handling
20-
client.on('conversation.updated', (event) {
21+
client.on(RealtimeEventType.conversationUpdated, (e) {
22+
final event = e as RealtimeEventConversationUpdated;
2123
// item is the current item being updated
22-
final item = event?['item'];
24+
final item = event.result.item;
2325
// delta can be null or populated
24-
final delta = event?['delta'];
26+
final delta = event.result.delta;
2527
// you can fetch a full list of items at any time
2628
});
2729

2830
// Connect to Realtime API
2931
await client.connect();
3032

3133
// Send a item and triggers a generation
32-
client.sendUserMessageContent([
33-
{'type': 'input_text', 'text': 'How are you?'},
34+
await client.sendUserMessageContent(const [
35+
ContentPart.text(text: 'How are you?'),
3436
]);
3537
}

packages/openai_realtime_dart/lib/openai_realtime_dart.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export 'src/api.dart';
55
export 'src/client.dart';
66
export 'src/conversation.dart';
77
export 'src/event_handler.dart';
8+
export 'src/schema/schema.dart';

packages/openai_realtime_dart/lib/src/api.dart

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:web_socket_channel/status.dart' as status;
77
import 'package:web_socket_channel/web_socket_channel.dart';
88

99
import 'event_handler.dart';
10+
import 'schema/generated/schema/schema.dart';
1011
import 'utils.dart';
1112
import 'web_socket/web_socket.dart';
1213

@@ -76,15 +77,27 @@ class RealtimeAPI extends RealtimeEventHandler {
7677
_ws!.stream.listen(
7778
(data) {
7879
final message = json.decode(data) as Map<String, dynamic>;
79-
receive(message['type'], message);
80+
receive(message);
8081
},
8182
onError: (dynamic error) {
8283
_log.severe('Error', error);
83-
dispatch('close', {'error': true});
84+
dispatch(
85+
RealtimeEventType.close,
86+
RealtimeEvent.close(
87+
eventId: RealtimeUtils.generateId(),
88+
error: true,
89+
),
90+
);
8491
},
8592
onDone: () {
8693
_log.info('Disconnected from "$url"');
87-
dispatch('close', {'error': false});
94+
dispatch(
95+
RealtimeEventType.close,
96+
RealtimeEvent.close(
97+
eventId: RealtimeUtils.generateId(),
98+
error: false,
99+
),
100+
);
88101
},
89102
);
90103

@@ -119,42 +132,44 @@ class RealtimeAPI extends RealtimeEventHandler {
119132
}
120133

121134
/// Receives an event from WebSocket and dispatches as
122-
/// "server.{eventName}" and "server.*" events.
123-
void receive(String eventName, Map<String, dynamic> event) {
124-
_logEvent(eventName, event, fromClient: false);
125-
dispatch('server.$eventName', event);
126-
dispatch('server.*', event);
135+
/// "[RealtimeEventType]" and "[RealtimeEventType.serverAll]" events.
136+
Future<void> receive(Map<String, dynamic> eventData) async {
137+
final event = RealtimeEvent.fromJson(eventData);
138+
_logEvent(event, fromClient: false);
139+
await dispatch(event.type, event);
140+
await dispatch(RealtimeEventType.serverAll, event);
141+
await dispatch(RealtimeEventType.all, event);
127142
}
128143

129-
/// Sends an event to WebSocket and dispatches as "client.{eventName}"
130-
/// and "client.*" events.
131-
void send(String eventName, [Map<String, dynamic>? data]) {
144+
/// Sends an event to WebSocket and dispatches as "[RealtimeEventType]"
145+
/// and "[RealtimeEventType.clientAll]" events.
146+
Future<void> send(RealtimeEvent event) async {
132147
if (!isConnected()) {
133148
throw Exception('RealtimeAPI is not connected');
134149
}
135150

136-
final event = {
137-
'event_id': RealtimeUtils.generateId('evt_'),
138-
'type': eventName,
139-
...?data,
140-
};
151+
final finalEvent = event.copyWith(
152+
eventId: RealtimeUtils.generateId(),
153+
);
141154

142-
dispatch('client.$eventName', event);
143-
dispatch('client.*', event);
144-
_logEvent(eventName, event, fromClient: true);
155+
_logEvent(finalEvent, fromClient: true);
156+
await dispatch(finalEvent.type, finalEvent);
157+
await dispatch(RealtimeEventType.clientAll, finalEvent);
158+
await dispatch(RealtimeEventType.all, finalEvent);
145159

146-
_ws!.sink.add(json.encode(event));
160+
_ws!.sink.add(json.encode(finalEvent.toJson()));
147161
}
148162

149163
void _logEvent(
150-
String name,
151-
Map<String, dynamic> event, {
164+
RealtimeEvent event, {
152165
required bool fromClient,
153166
}) {
154167
final eventString = event.toString();
155168
final eventLength = eventString.length;
156169
final eventFormatted =
157170
eventString.substring(0, eventLength > 200 ? 200 : eventLength);
158-
_log.info('${fromClient ? 'sent' : 'received'}: $name $eventFormatted');
171+
_log.info(
172+
'${fromClient ? 'sent' : 'received'}: ${event.type.name} $eventFormatted',
173+
);
159174
}
160175
}

0 commit comments

Comments
 (0)