Skip to content

Commit cc036ee

Browse files
committed
fix streaming error handling
1 parent c4cef6b commit cc036ee

File tree

6 files changed

+48
-30
lines changed

6 files changed

+48
-30
lines changed

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,22 @@ A new Flutter project which communicates with [OpenAI API](https://platform.open
77
![Screenshot 1](/document/readme_screenshot_01.png)
88
![Screenshot 2](/document/readme_screenshot_02.png)
99

10-
### Architecture
10+
## Features
1111

12-
It uses [Flutter framework](https://flutter.dev/), and uses [BLoC pattern](https://pub.dev/packages/flutter_bloc) to implement state management.
12+
- Support [requesting organization](https://platform.openai.com/docs/api-reference/requesting-organization)
13+
- Support [system message](https://platform.openai.com/docs/guides/chat/introduction)
14+
- Support [streaming message](https://platform.openai.com/docs/api-reference/chat/create#chat/create-stream) like ChatGPT
15+
16+
## How to use
17+
18+
1. Get [OpenAI API Key](https://platform.openai.com/docs/api-reference/authentication)
19+
2. Tap setting button on top right corner to set API Key (required) and Organization (optional)
20+
3. Add a new conversation
21+
4. Chat with Open AI!
22+
23+
## Architecture
24+
25+
It uses [Flutter framework](https://flutter.dev/), and uses [BLoC pattern](https://bloclibrary.dev/) to implement state management.
1326

1427
## How to build
1528

@@ -32,7 +45,7 @@ Mainly used Flutter packages:
3245
- [shared_preferences](https://pub.dev/packages/shared_preferences) to store app settings & conversations
3346
- [flutter_bloc](https://pub.dev/packages/flutter_bloc) for state management
3447
- [settings_ui](https://pub.dev/packages/settings_ui) for setting page
35-
- [flutter_spinkit](https://pub.dev/packages/flutter_spinkit) to show a fancy loading indicators
48+
- [flutter_markdown](https://pub.dev/packages/flutter_markdown) to render messages in markdown format
3649

3750
## License
3851

document/readme_screenshot_02.png

29.2 KB
Loading

lib/api/openai_api.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ class OpenAiApi {
7676
// data: [DONE]
7777
final String data = utf8.decode(value);
7878
final List<String> dataLines = data
79-
.split('\n')
80-
.where((element) => element.isNotEmpty)
81-
.toList();
79+
.split('\n')
80+
.where((element) => element.isNotEmpty)
81+
.toList();
8282
for (String line in dataLines) {
8383
if (line.startsWith('data: ')) {
8484
final String data = line.substring(6);
@@ -108,6 +108,9 @@ class OpenAiApi {
108108
onError: (error, stackTrace) {
109109
controller.addError(error, stackTrace);
110110
}); // response.stream.listen
111+
},
112+
onError: (error, stackTrace) {
113+
controller.addError(error, stackTrace);
111114
}); // httpClient.send(request).then
112115

113116
return controller.stream;

lib/models/chat.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class ChatMessage {
2929

3030
class ChatRequest {
3131
/// ID of the model to use. Currently, only gpt-3.5-turbo and gpt-3.5-turbo-0301 are supported.
32-
final String model = 'gpt-3.5-turbo-0301';
32+
final String model = 'gpt-3.5-turbo';
3333
/// The messages to generate chat completions for
3434
final List<ChatMessage> messages;
3535
///

lib/services/chat_service.dart

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ class ChatService {
7373
return conversation;
7474
}
7575

76+
Future _handleErrorGetResponseStream(dynamic error, Conversation conversation) async {
77+
conversation.error = error.toString();
78+
if (conversation.error.startsWith('Exception: '))
79+
conversation.error = conversation.error.substring(11);
80+
conversation.messages.last.isError = true;
81+
conversation.lastUpdated = DateTime.now();
82+
await updateConversation(conversation);
83+
}
84+
7685
Stream<Conversation> getResponseStreamFromServer(Conversation conversation) {
7786
final conversationStream = StreamController<Conversation>();
7887

@@ -82,30 +91,23 @@ class ChatService {
8291
var messages = conversation.messages.map((e) => e.toChatMessage()).toList();
8392
messages.insert(0, systemMessage);
8493

85-
try {
86-
var responseStream = _apiServer.chatCompletionStream(messages);
87-
responseStream.listen((chatStream) {
88-
if (chatStream.choices[0].delta.role.isNotEmpty)
89-
conversation.messages.add(ConversationMessage(chatStream.choices[0].delta.role, ''));
90-
if (chatStream.choices[0].delta.content.isNotEmpty)
91-
conversation.messages.last.content += chatStream.choices[0].delta.content;
92-
conversation.lastUpdated = DateTime.now();
93-
updateConversation(conversation);
94-
conversationStream.add(conversation);
95-
},
96-
onDone: () {
97-
conversationStream.close();
98-
});
99-
} catch (e) {
100-
// drop 'Exception: '
101-
conversation.error = e.toString();
102-
if (conversation.error.startsWith('Exception: '))
103-
conversation.error = conversation.error.substring(11);
104-
conversation.messages.last.isError = true;
94+
var responseStream = _apiServer.chatCompletionStream(messages);
95+
responseStream.listen((chatStream) {
96+
if (chatStream.choices[0].delta.role.isNotEmpty)
97+
conversation.messages.add(ConversationMessage(chatStream.choices[0].delta.role, ''));
98+
if (chatStream.choices[0].delta.content.isNotEmpty)
99+
conversation.messages.last.content += chatStream.choices[0].delta.content;
105100
conversation.lastUpdated = DateTime.now();
106-
updateConversation(conversation);
107101
conversationStream.add(conversation);
108-
}
102+
},
103+
onDone: () async {
104+
await updateConversation(conversation);
105+
conversationStream.close();
106+
},
107+
onError: (error) async {
108+
await _handleErrorGetResponseStream(error, conversation);
109+
conversationStream.add(conversation);
110+
});
109111

110112
return conversationStream.stream;
111113
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
1616
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
1717
# In Windows, build-name is used as the major, minor, and patch parts
1818
# of the product and file versions while build-number is used as the build suffix.
19-
version: 1.0.2+1
19+
version: 1.0.3+1
2020

2121
environment:
2222
sdk: '>=2.19.3 <3.0.0'

0 commit comments

Comments
 (0)