Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(flutter): pre-defined response formats #7128

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,13 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
},
sendMessage: (
String message,
PredefinedFormat? format,
Map<String, dynamic>? metadata,
) {
numSendMessage += 1;

_clearRelatedQuestions();
_startStreamingMessage(message, metadata);
_startStreamingMessage(message, format, metadata);
lastSentMessage = null;

emit(
Expand Down Expand Up @@ -231,9 +232,9 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
),
);
},
regenerateAnswer: (id) {
regenerateAnswer: (id, format) {
_clearRelatedQuestions();
_regenerateAnswer(id);
_regenerateAnswer(id, format);
lastSentMessage = null;

emit(
Expand Down Expand Up @@ -398,6 +399,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {

Future<void> _startStreamingMessage(
String message,
PredefinedFormat? format,
Map<String, dynamic>? metadata,
) async {
await answerStream?.dispose();
Expand All @@ -420,6 +422,9 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
answerStreamPort: Int64(answerStream!.nativePort),
metadata: await metadataPBFromMetadata(metadata),
);
if (format != null) {
payload.format = format.toPB();
}

// stream the question to the server
await AIEventStreamMessage(payload).send().fold(
Expand Down Expand Up @@ -460,7 +465,10 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
);
}

void _regenerateAnswer(String answerMessageIdString) async {
void _regenerateAnswer(
String answerMessageIdString,
PredefinedFormat? format,
) async {
final id = temporaryMessageIDMap.entries
.firstWhereOrNull((e) => e.value == answerMessageIdString)
?.key ??
Expand All @@ -479,6 +487,9 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
answerMessageId: answerMessageId,
answerStreamPort: Int64(answerStream!.nativePort),
);
if (format != null) {
payload.format = format.toPB();
}

await AIEventRegenerateResponse(payload).send().fold(
(success) {
Expand Down Expand Up @@ -586,13 +597,17 @@ class ChatEvent with _$ChatEvent {
// send message
const factory ChatEvent.sendMessage({
required String message,
PredefinedFormat? format,
Map<String, dynamic>? metadata,
}) = _SendMessage;
const factory ChatEvent.finishSending() = _FinishSendMessage;
const factory ChatEvent.failedSending() = _FailSendMessage;

// regenerate
const factory ChatEvent.regenerateAnswer(String id) = _RegenerateAnswer;
const factory ChatEvent.regenerateAnswer(
String id,
PredefinedFormat? format,
) = _RegenerateAnswer;

// streaming answer
const factory ChatEvent.stopStream() = _StopStream;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import 'dart:io';

import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-ai/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:path/path.dart' as path;
Expand Down Expand Up @@ -137,3 +141,87 @@ enum LoadChatMessageStatus {
loadingRemote,
ready,
}

class PredefinedFormat extends Equatable {
const PredefinedFormat({
required this.imageFormat,
required this.textFormat,
});

const PredefinedFormat.auto()
: imageFormat = ImageFormat.text,
textFormat = TextFormat.auto;

final ImageFormat imageFormat;
final TextFormat? textFormat;

PredefinedFormatPB toPB() {
return PredefinedFormatPB(
imageFormat: switch (imageFormat) {
ImageFormat.text => ResponseImageFormatPB.TextOnly,
ImageFormat.image => ResponseImageFormatPB.ImageOnly,
ImageFormat.textAndImage => ResponseImageFormatPB.TextAndImage,
},
textFormat: switch (textFormat) {
TextFormat.auto => ResponseTextFormatPB.Paragraph,
TextFormat.bulletList => ResponseTextFormatPB.BulletedList,
TextFormat.numberedList => ResponseTextFormatPB.NumberedList,
TextFormat.table => ResponseTextFormatPB.Table,
null => null
richardshiue marked this conversation as resolved.
Show resolved Hide resolved
},
);
}

@override
List<Object?> get props => [imageFormat, textFormat];
}

enum ImageFormat {
text,
image,
textAndImage;

bool get hasText => this == text || this == textAndImage;

FlowySvgData get icon {
return switch (this) {
ImageFormat.text => FlowySvgs.ai_text_s,
ImageFormat.image => FlowySvgs.ai_image_s,
ImageFormat.textAndImage => FlowySvgs.ai_text_image_s,
};
}

String get i18n {
return switch (this) {
ImageFormat.text => LocaleKeys.chat_changeFormat_textOnly.tr(),
ImageFormat.image => LocaleKeys.chat_changeFormat_imageOnly.tr(),
ImageFormat.textAndImage =>
LocaleKeys.chat_changeFormat_textAndImage.tr(),
};
}
}

enum TextFormat {
auto,
bulletList,
numberedList,
table;

FlowySvgData get icon {
return switch (this) {
TextFormat.auto => FlowySvgs.ai_paragraph_s,
TextFormat.bulletList => FlowySvgs.ai_list_s,
TextFormat.numberedList => FlowySvgs.ai_number_list_s,
TextFormat.table => FlowySvgs.ai_table_s,
};
}

String get i18n {
return switch (this) {
TextFormat.auto => LocaleKeys.chat_changeFormat_text.tr(),
TextFormat.bulletList => LocaleKeys.chat_changeFormat_bullet.tr(),
TextFormat.numberedList => LocaleKeys.chat_changeFormat_number.tr(),
TextFormat.table => LocaleKeys.chat_changeFormat_table.tr(),
};
}
}
11 changes: 8 additions & 3 deletions frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,10 @@ class _ChatContentPage extends StatelessWidget {
_onSelectMetadata(context, metadata),
onRegenerate: () => context
.read<ChatBloc>()
.add(ChatEvent.regenerateAnswer(message.id)),
.add(ChatEvent.regenerateAnswer(message.id, null)),
onChangeFormat: (format) => context
.read<ChatBloc>()
.add(ChatEvent.regenerateAnswer(message.id, format)),
);
},
);
Expand Down Expand Up @@ -288,10 +291,11 @@ class _ChatContentPage extends StatelessWidget {
onStopStreaming: () {
chatBloc.add(const ChatEvent.stopStream());
},
onSubmitted: (text, metadata) {
onSubmitted: (text, format, metadata) {
chatBloc.add(
ChatEvent.sendMessage(
message: text,
format: format,
metadata: metadata,
),
);
Expand All @@ -310,10 +314,11 @@ class _ChatContentPage extends StatelessWidget {
onStopStreaming: () {
chatBloc.add(const ChatEvent.stopStream());
},
onSubmitted: (text, metadata) {
onSubmitted: (text, format, metadata) {
chatBloc.add(
ChatEvent.sendMessage(
message: text,
format: format,
metadata: metadata,
),
);
Expand Down
Loading
Loading