mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-12-11 23:21:57 +00:00
feat: regenerate ai response (#7006)
* feat: regenerate ai response * chore: find question id instead of assuming * chore: fix clippy * chore: show local messages if they were there * chore: remove duplicate code * chore: fix loading message * chore: revert unintended translation key removal * chore: update translation for ai service unavailable * chore: fix initial chat message load --------- Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
parent
04a013f7ee
commit
e73fd56152
@ -34,7 +34,7 @@ class MobileQuickActionButton extends StatelessWidget {
|
|||||||
enable ? null : const WidgetStatePropertyAll(Colors.transparent),
|
enable ? null : const WidgetStatePropertyAll(Colors.transparent),
|
||||||
splashColor: Colors.transparent,
|
splashColor: Colors.transparent,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 52,
|
height: 44,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -30,7 +30,8 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
super(ChatState.initial()) {
|
super(ChatState.initial()) {
|
||||||
_startListening();
|
_startListening();
|
||||||
_dispatch();
|
_dispatch();
|
||||||
_init();
|
_loadMessages();
|
||||||
|
_loadSetting();
|
||||||
}
|
}
|
||||||
|
|
||||||
final String chatId;
|
final String chatId;
|
||||||
@ -59,16 +60,6 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
AnswerStream? answerStream;
|
AnswerStream? answerStream;
|
||||||
int numSendMessage = 0;
|
int numSendMessage = 0;
|
||||||
|
|
||||||
/// a counter used to determine whether the initial loading state should be
|
|
||||||
/// set to finish. It should hit two before we emit: one for the local fetch
|
|
||||||
/// and another for the server fetch.
|
|
||||||
///
|
|
||||||
/// This is to work around a bug where if an ai chat that is not yet on the
|
|
||||||
/// user local storage is opened but has messages in the server, it will
|
|
||||||
/// remain stuck on the welcome screen until the user switches to another page
|
|
||||||
/// then come back.
|
|
||||||
int initialFetchCounter = 0;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await answerStream?.dispose();
|
await answerStream?.dispose();
|
||||||
@ -86,14 +77,23 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
await chatController.insert(message, index: 0);
|
await chatController.insert(message, index: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialFetchCounter < 2) {
|
switch (state.loadingState) {
|
||||||
initialFetchCounter++;
|
case LoadChatMessageStatus.loading
|
||||||
}
|
when chatController.messages.isEmpty:
|
||||||
|
emit(
|
||||||
if (state.loadingState.isLoading && initialFetchCounter >= 2) {
|
state.copyWith(
|
||||||
emit(
|
loadingState: LoadChatMessageStatus.loadingRemote,
|
||||||
state.copyWith(loadingState: const ChatLoadingState.finish()),
|
),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
|
case LoadChatMessageStatus.loading:
|
||||||
|
case LoadChatMessageStatus.loadingRemote:
|
||||||
|
emit(
|
||||||
|
state.copyWith(loadingState: LoadChatMessageStatus.ready),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loadPreviousMessages: () {
|
loadPreviousMessages: () {
|
||||||
@ -165,18 +165,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
) {
|
) {
|
||||||
numSendMessage += 1;
|
numSendMessage += 1;
|
||||||
|
|
||||||
final relatedQuestionMessages = chatController.messages
|
_clearRelatedQuestions();
|
||||||
.where(
|
|
||||||
(message) =>
|
|
||||||
onetimeMessageTypeFromMeta(message.metadata) ==
|
|
||||||
OnetimeShotType.relatedQuestion,
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (final message in relatedQuestionMessages) {
|
|
||||||
chatController.remove(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
_startStreamingMessage(message, metadata);
|
_startStreamingMessage(message, metadata);
|
||||||
lastSentMessage = null;
|
lastSentMessage = null;
|
||||||
|
|
||||||
@ -229,7 +218,6 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
answerStreamMessageId = '';
|
answerStreamMessageId = '';
|
||||||
},
|
},
|
||||||
startAnswerStreaming: (Message message) {
|
startAnswerStreaming: (Message message) {
|
||||||
chatController.insert(message);
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
promptResponseState: PromptResponseState.streamingAnswer,
|
promptResponseState: PromptResponseState.streamingAnswer,
|
||||||
@ -247,6 +235,17 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
regenerateAnswer: (id) {
|
||||||
|
_clearRelatedQuestions();
|
||||||
|
_regenerateAnswer(id);
|
||||||
|
lastSentMessage = null;
|
||||||
|
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
promptResponseState: PromptResponseState.sendingQuestion,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
didReceiveChatSettings: (settings) {
|
didReceiveChatSettings: (settings) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(selectedSourceIds: settings.ragIds),
|
state.copyWith(selectedSourceIds: settings.ragIds),
|
||||||
@ -348,10 +347,10 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _init() async {
|
void _loadSetting() async {
|
||||||
final getChatSettingsPayload =
|
final getChatSettingsPayload =
|
||||||
AIEventGetChatSettings(ChatId(value: chatId));
|
AIEventGetChatSettings(ChatId(value: chatId));
|
||||||
final getChatSettingsFuture = getChatSettingsPayload.send().fold(
|
await getChatSettingsPayload.send().fold(
|
||||||
(settings) {
|
(settings) {
|
||||||
if (!isClosed) {
|
if (!isClosed) {
|
||||||
add(ChatEvent.didReceiveChatSettings(settings: settings));
|
add(ChatEvent.didReceiveChatSettings(settings: settings));
|
||||||
@ -359,13 +358,14 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
},
|
},
|
||||||
Log.error,
|
Log.error,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _loadMessages() async {
|
||||||
final loadMessagesPayload = LoadNextChatMessagePB(
|
final loadMessagesPayload = LoadNextChatMessagePB(
|
||||||
chatId: chatId,
|
chatId: chatId,
|
||||||
limit: Int64(10),
|
limit: Int64(10),
|
||||||
);
|
);
|
||||||
final loadMessagesFuture =
|
await AIEventLoadNextMessage(loadMessagesPayload).send().fold(
|
||||||
AIEventLoadNextMessage(loadMessagesPayload).send().fold(
|
|
||||||
(list) {
|
(list) {
|
||||||
if (!isClosed) {
|
if (!isClosed) {
|
||||||
final messages = list.messages.map(_createTextMessage).toList();
|
final messages = list.messages.map(_createTextMessage).toList();
|
||||||
@ -374,8 +374,6 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
},
|
},
|
||||||
(err) => Log.error("Failed to load messages: $err"),
|
(err) => Log.error("Failed to load messages: $err"),
|
||||||
);
|
);
|
||||||
|
|
||||||
await Future.wait([getChatSettingsFuture, loadMessagesFuture]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isOneTimeMessage(Message message) {
|
bool _isOneTimeMessage(Message message) {
|
||||||
@ -433,6 +431,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
add(ChatEvent.finishSending(question));
|
add(ChatEvent.finishSending(question));
|
||||||
|
add(ChatEvent.receiveMessage(streamAnswer));
|
||||||
add(ChatEvent.startAnswerStreaming(streamAnswer));
|
add(ChatEvent.startAnswerStreaming(streamAnswer));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -460,6 +459,37 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _regenerateAnswer(String answerMessageIdString) async {
|
||||||
|
final answerMessageId = Int64.tryParseInt(answerMessageIdString);
|
||||||
|
if (answerMessageId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await answerStream?.dispose();
|
||||||
|
answerStream = AnswerStream();
|
||||||
|
|
||||||
|
final payload = RegenerateResponsePB(
|
||||||
|
chatId: chatId,
|
||||||
|
answerMessageId: answerMessageId,
|
||||||
|
answerStreamPort: Int64(answerStream!.nativePort),
|
||||||
|
);
|
||||||
|
|
||||||
|
await AIEventRegenerateResponse(payload).send().fold(
|
||||||
|
(success) {
|
||||||
|
if (!isClosed) {
|
||||||
|
final streamAnswer = _createAnswerStreamMessage(
|
||||||
|
answerStream!,
|
||||||
|
answerMessageId - 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
add(ChatEvent.receiveMessage(streamAnswer));
|
||||||
|
add(ChatEvent.startAnswerStreaming(streamAnswer));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => Log.error("Failed to send message: ${err.msg}"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Message _createAnswerStreamMessage(
|
Message _createAnswerStreamMessage(
|
||||||
AnswerStream stream,
|
AnswerStream stream,
|
||||||
Int64 questionMessageId,
|
Int64 questionMessageId,
|
||||||
@ -518,6 +548,20 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _clearRelatedQuestions() {
|
||||||
|
final relatedQuestionMessages = chatController.messages
|
||||||
|
.where(
|
||||||
|
(message) =>
|
||||||
|
onetimeMessageTypeFromMeta(message.metadata) ==
|
||||||
|
OnetimeShotType.relatedQuestion,
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
for (final message in relatedQuestionMessages) {
|
||||||
|
chatController.remove(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@ -539,6 +583,9 @@ class ChatEvent with _$ChatEvent {
|
|||||||
_FinishSendMessage;
|
_FinishSendMessage;
|
||||||
const factory ChatEvent.failedSending() = _FailSendMessage;
|
const factory ChatEvent.failedSending() = _FailSendMessage;
|
||||||
|
|
||||||
|
// regenerate
|
||||||
|
const factory ChatEvent.regenerateAnswer(String id) = _RegenerateAnswer;
|
||||||
|
|
||||||
// streaming answer
|
// streaming answer
|
||||||
const factory ChatEvent.startAnswerStreaming(Message message) =
|
const factory ChatEvent.startAnswerStreaming(Message message) =
|
||||||
_StartAnswerStreaming;
|
_StartAnswerStreaming;
|
||||||
@ -567,13 +614,13 @@ class ChatEvent with _$ChatEvent {
|
|||||||
class ChatState with _$ChatState {
|
class ChatState with _$ChatState {
|
||||||
const factory ChatState({
|
const factory ChatState({
|
||||||
required List<String> selectedSourceIds,
|
required List<String> selectedSourceIds,
|
||||||
required ChatLoadingState loadingState,
|
required LoadChatMessageStatus loadingState,
|
||||||
required PromptResponseState promptResponseState,
|
required PromptResponseState promptResponseState,
|
||||||
}) = _ChatState;
|
}) = _ChatState;
|
||||||
|
|
||||||
factory ChatState.initial() => const ChatState(
|
factory ChatState.initial() => const ChatState(
|
||||||
selectedSourceIds: [],
|
selectedSourceIds: [],
|
||||||
loadingState: ChatLoadingState.loading(),
|
loadingState: LoadChatMessageStatus.loading,
|
||||||
promptResponseState: PromptResponseState.ready,
|
promptResponseState: PromptResponseState.ready,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,3 +132,9 @@ const onetimeShotType = "OnetimeShotType";
|
|||||||
OnetimeShotType? onetimeMessageTypeFromMeta(Map<String, dynamic>? metadata) {
|
OnetimeShotType? onetimeMessageTypeFromMeta(Map<String, dynamic>? metadata) {
|
||||||
return metadata?[onetimeShotType];
|
return metadata?[onetimeShotType];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum LoadChatMessageStatus {
|
||||||
|
loading,
|
||||||
|
loadingRemote,
|
||||||
|
ready,
|
||||||
|
}
|
||||||
|
|||||||
@ -120,20 +120,13 @@ class _ChatContentPage extends StatelessWidget {
|
|||||||
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||||
child: BlocBuilder<ChatBloc, ChatState>(
|
child: BlocBuilder<ChatBloc, ChatState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return state.loadingState.when(
|
return switch (state.loadingState) {
|
||||||
loading: () {
|
LoadChatMessageStatus.ready => Column(
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator.adaptive(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
finish: (_) {
|
|
||||||
final chatController =
|
|
||||||
context.read<ChatBloc>().chatController;
|
|
||||||
return Column(
|
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Chat(
|
child: Chat(
|
||||||
chatController: chatController,
|
chatController:
|
||||||
|
context.read<ChatBloc>().chatController,
|
||||||
user: User(id: userProfile.id.toString()),
|
user: User(id: userProfile.id.toString()),
|
||||||
darkTheme: ChatTheme.fromThemeData(Theme.of(context)),
|
darkTheme: ChatTheme.fromThemeData(Theme.of(context)),
|
||||||
theme: ChatTheme.fromThemeData(Theme.of(context)),
|
theme: ChatTheme.fromThemeData(Theme.of(context)),
|
||||||
@ -148,9 +141,9 @@ class _ChatContentPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
_buildInput(context),
|
_buildInput(context),
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
_ => const Center(child: CircularProgressIndicator.adaptive()),
|
||||||
);
|
};
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -223,6 +216,8 @@ class _ChatContentPage extends StatelessWidget {
|
|||||||
isLastMessage: isLastMessage,
|
isLastMessage: isLastMessage,
|
||||||
onSelectedMetadata: (metadata) =>
|
onSelectedMetadata: (metadata) =>
|
||||||
_onSelectMetadata(context, metadata),
|
_onSelectMetadata(context, metadata),
|
||||||
|
onRegenerate: (id) =>
|
||||||
|
context.read<ChatBloc>().add(ChatEvent.regenerateAnswer(id)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -30,12 +30,14 @@ class ChatAIMessageBubble extends StatelessWidget {
|
|||||||
required this.child,
|
required this.child,
|
||||||
required this.showActions,
|
required this.showActions,
|
||||||
this.isLastMessage = false,
|
this.isLastMessage = false,
|
||||||
|
this.onRegenerate,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Message message;
|
final Message message;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final bool showActions;
|
final bool showActions;
|
||||||
final bool isLastMessage;
|
final bool isLastMessage;
|
||||||
|
final void Function(String)? onRegenerate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -60,6 +62,7 @@ class ChatAIMessageBubble extends StatelessWidget {
|
|||||||
Widget _wrapBottomActions(Widget child) {
|
Widget _wrapBottomActions(Widget child) {
|
||||||
return ChatAIBottomInlineActions(
|
return ChatAIBottomInlineActions(
|
||||||
message: message,
|
message: message,
|
||||||
|
onRegenerate: onRegenerate,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -67,6 +70,7 @@ class ChatAIMessageBubble extends StatelessWidget {
|
|||||||
Widget _wrapHover(Widget child) {
|
Widget _wrapHover(Widget child) {
|
||||||
return ChatAIMessageHover(
|
return ChatAIMessageHover(
|
||||||
message: message,
|
message: message,
|
||||||
|
onRegenerate: onRegenerate,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -74,6 +78,7 @@ class ChatAIMessageBubble extends StatelessWidget {
|
|||||||
Widget _wrapPopMenu(Widget child) {
|
Widget _wrapPopMenu(Widget child) {
|
||||||
return ChatAIMessagePopup(
|
return ChatAIMessagePopup(
|
||||||
message: message,
|
message: message,
|
||||||
|
onRegenerate: onRegenerate,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -84,10 +89,12 @@ class ChatAIBottomInlineActions extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.message,
|
required this.message,
|
||||||
|
this.onRegenerate,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Message message;
|
final Message message;
|
||||||
|
final void Function(String)? onRegenerate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -107,6 +114,9 @@ class ChatAIBottomInlineActions extends StatelessWidget {
|
|||||||
CopyButton(
|
CopyButton(
|
||||||
textMessage: message as TextMessage,
|
textMessage: message as TextMessage,
|
||||||
),
|
),
|
||||||
|
RegenerateButton(
|
||||||
|
onTap: () => onRegenerate?.call(message.id),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -121,10 +131,12 @@ class ChatAIMessageHover extends StatefulWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.message,
|
required this.message,
|
||||||
|
this.onRegenerate,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Message message;
|
final Message message;
|
||||||
|
final void Function(String)? onRegenerate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatAIMessageHover> createState() => _ChatAIMessageHoverState();
|
State<ChatAIMessageHover> createState() => _ChatAIMessageHoverState();
|
||||||
@ -206,6 +218,10 @@ class _ChatAIMessageHoverState extends State<ChatAIMessageHover> {
|
|||||||
CopyButton(
|
CopyButton(
|
||||||
textMessage: widget.message as TextMessage,
|
textMessage: widget.message as TextMessage,
|
||||||
),
|
),
|
||||||
|
RegenerateButton(
|
||||||
|
onTap: () =>
|
||||||
|
widget.onRegenerate?.call(widget.message.id),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
@ -370,15 +386,44 @@ class CopyButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RegenerateButton extends StatelessWidget {
|
||||||
|
const RegenerateButton({
|
||||||
|
super.key,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function() onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FlowyTooltip(
|
||||||
|
message: LocaleKeys.chat_regenerate.tr(),
|
||||||
|
child: FlowyIconButton(
|
||||||
|
width: DesktopAIConvoSizes.actionBarIconSize,
|
||||||
|
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||||
|
radius: DesktopAIConvoSizes.actionBarIconRadius,
|
||||||
|
icon: FlowySvg(
|
||||||
|
FlowySvgs.ai_undo_s,
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
size: const Size.square(16),
|
||||||
|
),
|
||||||
|
onPressed: onTap,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ChatAIMessagePopup extends StatelessWidget {
|
class ChatAIMessagePopup extends StatelessWidget {
|
||||||
const ChatAIMessagePopup({
|
const ChatAIMessagePopup({
|
||||||
super.key,
|
super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.message,
|
required this.message,
|
||||||
|
this.onRegenerate,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Message message;
|
final Message message;
|
||||||
|
final void Function(String)? onRegenerate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -393,33 +438,11 @@ class ChatAIMessagePopup extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
MobileQuickActionButton(
|
const VSpace(16.0),
|
||||||
onTap: () async {
|
_copyButton(context, bottomSheetContext),
|
||||||
if (message is! TextMessage) {
|
const Divider(height: 8.5, thickness: 0.5),
|
||||||
return;
|
_regenerateButton(context),
|
||||||
}
|
const Divider(height: 8.5, thickness: 0.5),
|
||||||
final textMessage = message as TextMessage;
|
|
||||||
final document = customMarkdownToDocument(textMessage.text);
|
|
||||||
await getIt<ClipboardService>().setData(
|
|
||||||
ClipboardServiceData(
|
|
||||||
plainText: textMessage.text,
|
|
||||||
inAppJson: jsonEncode(document.toJson()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (bottomSheetContext.mounted) {
|
|
||||||
Navigator.of(bottomSheetContext).pop();
|
|
||||||
}
|
|
||||||
if (context.mounted) {
|
|
||||||
showToastNotification(
|
|
||||||
context,
|
|
||||||
message: LocaleKeys.grid_url_copiedNotification.tr(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: FlowySvgs.copy_s,
|
|
||||||
iconSize: const Size.square(20),
|
|
||||||
text: LocaleKeys.button_copy.tr(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -428,4 +451,46 @@ class ChatAIMessagePopup extends StatelessWidget {
|
|||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _copyButton(BuildContext context, BuildContext bottomSheetContext) {
|
||||||
|
return MobileQuickActionButton(
|
||||||
|
onTap: () async {
|
||||||
|
if (message is! TextMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final textMessage = message as TextMessage;
|
||||||
|
final document = customMarkdownToDocument(textMessage.text);
|
||||||
|
await getIt<ClipboardService>().setData(
|
||||||
|
ClipboardServiceData(
|
||||||
|
plainText: textMessage.text,
|
||||||
|
inAppJson: jsonEncode(document.toJson()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (bottomSheetContext.mounted) {
|
||||||
|
Navigator.of(bottomSheetContext).pop();
|
||||||
|
}
|
||||||
|
if (context.mounted) {
|
||||||
|
showToastNotification(
|
||||||
|
context,
|
||||||
|
message: LocaleKeys.grid_url_copiedNotification.tr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: FlowySvgs.copy_s,
|
||||||
|
iconSize: const Size.square(20),
|
||||||
|
text: LocaleKeys.button_copy.tr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _regenerateButton(BuildContext context) {
|
||||||
|
return MobileQuickActionButton(
|
||||||
|
onTap: () {
|
||||||
|
onRegenerate?.call(message.id);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
icon: FlowySvgs.ai_undo_s,
|
||||||
|
iconSize: const Size.square(20),
|
||||||
|
text: LocaleKeys.chat_regenerate.tr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class ChatAIMessageWidget extends StatelessWidget {
|
|||||||
required this.chatId,
|
required this.chatId,
|
||||||
required this.refSourceJsonString,
|
required this.refSourceJsonString,
|
||||||
this.onSelectedMetadata,
|
this.onSelectedMetadata,
|
||||||
|
this.onRegenerate,
|
||||||
this.isLastMessage = false,
|
this.isLastMessage = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ class ChatAIMessageWidget extends StatelessWidget {
|
|||||||
final String chatId;
|
final String chatId;
|
||||||
final String? refSourceJsonString;
|
final String? refSourceJsonString;
|
||||||
final void Function(ChatMessageRefSource metadata)? onSelectedMetadata;
|
final void Function(ChatMessageRefSource metadata)? onSelectedMetadata;
|
||||||
|
final void Function(String messageId)? onRegenerate;
|
||||||
final bool isLastMessage;
|
final bool isLastMessage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -80,6 +82,7 @@ class ChatAIMessageWidget extends StatelessWidget {
|
|||||||
message: message,
|
message: message,
|
||||||
isLastMessage: isLastMessage,
|
isLastMessage: isLastMessage,
|
||||||
showActions: stream == null && state.text.isNotEmpty,
|
showActions: stream == null && state.text.isNotEmpty,
|
||||||
|
onRegenerate: onRegenerate,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -101,11 +104,6 @@ class ChatAIMessageWidget extends StatelessWidget {
|
|||||||
onError: (error) {
|
onError: (error) {
|
||||||
return ChatErrorMessageWidget(
|
return ChatErrorMessageWidget(
|
||||||
errorMessage: LocaleKeys.chat_aiServerUnavailable.tr(),
|
errorMessage: LocaleKeys.chat_aiServerUnavailable.tr(),
|
||||||
onRetry: () {
|
|
||||||
context
|
|
||||||
.read<ChatAIMessageBloc>()
|
|
||||||
.add(const ChatAIMessageEvent.retry());
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onAIResponseLimit: () {
|
onAIResponseLimit: () {
|
||||||
|
|||||||
24
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
24
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -172,7 +172,7 @@ checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -192,7 +192,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-ai-client"
|
name = "appflowy-ai-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -888,7 +888,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -944,7 +944,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api-entity"
|
name = "client-api-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -957,7 +957,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1257,7 +1257,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-entity"
|
name = "collab-rt-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1282,7 +1282,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-protocol"
|
name = "collab-rt-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1679,7 +1679,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3268,7 +3268,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -3285,7 +3285,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3840,7 +3840,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -6576,7 +6576,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
|||||||
@ -59,7 +59,7 @@ collab-importer = { version = "0.1" }
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "abf827f2a6bebc70ef8829909dcf6acb3ce24715" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9f57fa63d5ee039f50b0d27b860679deb14fee2e" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
24
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
24
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -183,7 +183,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-ai-client"
|
name = "appflowy-ai-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -877,7 +877,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -933,7 +933,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api-entity"
|
name = "client-api-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -946,7 +946,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1255,7 +1255,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-entity"
|
name = "collab-rt-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1280,7 +1280,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-protocol"
|
name = "collab-rt-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1684,7 +1684,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3350,7 +3350,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -3367,7 +3367,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3927,7 +3927,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -6656,7 +6656,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
|||||||
@ -58,7 +58,7 @@ collab-importer = { version = "0.1" }
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "abf827f2a6bebc70ef8829909dcf6acb3ce24715" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9f57fa63d5ee039f50b0d27b860679deb14fee2e" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
@ -180,8 +180,8 @@
|
|||||||
"inputLocalAIMessageHint": "Ask @:appName Local AI",
|
"inputLocalAIMessageHint": "Ask @:appName Local AI",
|
||||||
"unsupportedCloudPrompt": "This feature is only available when using @:appName Cloud",
|
"unsupportedCloudPrompt": "This feature is only available when using @:appName Cloud",
|
||||||
"relatedQuestion": "Suggested",
|
"relatedQuestion": "Suggested",
|
||||||
"serverUnavailable": "Service Temporarily Unavailable. Please try again later.",
|
"serverUnavailable": "Connection lost. Please check your internet and",
|
||||||
"aiServerUnavailable": "Connection lost. Please check your internet and",
|
"aiServerUnavailable": "The AI service is temporarily unavailable. Please try again later.",
|
||||||
"retry": "Retry",
|
"retry": "Retry",
|
||||||
"clickToRetry": "Click to retry",
|
"clickToRetry": "Click to retry",
|
||||||
"regenerateAnswer": "Regenerate",
|
"regenerateAnswer": "Regenerate",
|
||||||
@ -205,7 +205,8 @@
|
|||||||
"questionDetail": "Hi {}! How can I help you today?",
|
"questionDetail": "Hi {}! How can I help you today?",
|
||||||
"indexingFile": "Indexing {}",
|
"indexingFile": "Indexing {}",
|
||||||
"generatingResponse": "Generating response",
|
"generatingResponse": "Generating response",
|
||||||
"selectSources": "Select Sources"
|
"selectSources": "Select Sources",
|
||||||
|
"regenerate": "Try again"
|
||||||
},
|
},
|
||||||
"trash": {
|
"trash": {
|
||||||
"text": "Trash",
|
"text": "Trash",
|
||||||
|
|||||||
42
frontend/rust-lib/Cargo.lock
generated
42
frontend/rust-lib/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -183,7 +183,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-ai-client"
|
name = "appflowy-ai-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -780,7 +780,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -836,7 +836,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api-entity"
|
name = "client-api-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -849,7 +849,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1118,7 +1118,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-entity"
|
name = "collab-rt-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1143,7 +1143,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-protocol"
|
name = "collab-rt-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1389,7 +1389,7 @@ dependencies = [
|
|||||||
"cssparser-macros",
|
"cssparser-macros",
|
||||||
"dtoa-short",
|
"dtoa-short",
|
||||||
"itoa",
|
"itoa",
|
||||||
"phf 0.11.2",
|
"phf 0.8.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1538,7 +1538,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2982,7 +2982,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2999,7 +2999,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3484,7 +3484,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -4499,7 +4499,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.8.0",
|
"phf_macros",
|
||||||
"phf_shared 0.8.0",
|
"phf_shared 0.8.0",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
]
|
]
|
||||||
@ -4519,7 +4519,6 @@ version = "0.11.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.11.2",
|
|
||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4587,19 +4586,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "phf_macros"
|
|
||||||
version = "0.11.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
|
||||||
dependencies = [
|
|
||||||
"phf_generator 0.11.2",
|
|
||||||
"phf_shared 0.11.2",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.47",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -5855,7 +5841,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=abf827f2a6bebc70ef8829909dcf6acb3ce24715#abf827f2a6bebc70ef8829909dcf6acb3ce24715"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9f57fa63d5ee039f50b0d27b860679deb14fee2e#9f57fa63d5ee039f50b0d27b860679deb14fee2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
|||||||
@ -107,8 +107,8 @@ dashmap = "6.0.1"
|
|||||||
# Run the script.add_workspace_members:
|
# Run the script.add_workspace_members:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "abf827f2a6bebc70ef8829909dcf6acb3ce24715" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9f57fa63d5ee039f50b0d27b860679deb14fee2e" }
|
||||||
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "abf827f2a6bebc70ef8829909dcf6acb3ce24715" }
|
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9f57fa63d5ee039f50b0d27b860679deb14fee2e" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
|||||||
@ -70,6 +70,13 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
|||||||
limit: u64,
|
limit: u64,
|
||||||
) -> Result<RepeatedChatMessage, FlowyError>;
|
) -> Result<RepeatedChatMessage, FlowyError>;
|
||||||
|
|
||||||
|
async fn get_question_from_answer_id(
|
||||||
|
&self,
|
||||||
|
workspace_id: &str,
|
||||||
|
chat_id: &str,
|
||||||
|
answer_message_id: i64,
|
||||||
|
) -> Result<ChatMessage, FlowyError>;
|
||||||
|
|
||||||
async fn get_related_message(
|
async fn get_related_message(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
|
|||||||
@ -37,6 +37,8 @@ pub trait AIQueryService: Send + Sync + 'static {
|
|||||||
parent_view_id: &str,
|
parent_view_id: &str,
|
||||||
chat_id: &str,
|
chat_id: &str,
|
||||||
) -> Result<Vec<String>, FlowyError>;
|
) -> Result<Vec<String>, FlowyError>;
|
||||||
|
|
||||||
|
async fn sync_rag_documents(&self, rag_ids: Vec<String>) -> Result<(), FlowyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AIManager {
|
pub struct AIManager {
|
||||||
@ -102,6 +104,7 @@ impl AIManager {
|
|||||||
if self.local_ai_controller.is_running() {
|
if self.local_ai_controller.is_running() {
|
||||||
self.local_ai_controller.open_chat(chat_id);
|
self.local_ai_controller.open_chat(chat_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +196,22 @@ impl AIManager {
|
|||||||
Ok(question)
|
Ok(question)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn stream_regenerate_response(
|
||||||
|
&self,
|
||||||
|
chat_id: &str,
|
||||||
|
answer_message_id: i64,
|
||||||
|
answer_stream_port: i64,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
let chat = self.get_or_create_chat_instance(chat_id).await?;
|
||||||
|
let question_message_id = chat
|
||||||
|
.get_question_id_from_answer_id(answer_message_id)
|
||||||
|
.await?;
|
||||||
|
chat
|
||||||
|
.stream_regenerate_response(question_message_id, answer_stream_port)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_or_create_chat_instance(&self, chat_id: &str) -> Result<Arc<Chat>, FlowyError> {
|
pub async fn get_or_create_chat_instance(&self, chat_id: &str) -> Result<Arc<Chat>, FlowyError> {
|
||||||
let chat = self.chats.get(chat_id).as_deref().cloned();
|
let chat = self.chats.get(chat_id).as_deref().cloned();
|
||||||
match chat {
|
match chat {
|
||||||
|
|||||||
@ -4,7 +4,10 @@ use crate::entities::{
|
|||||||
};
|
};
|
||||||
use crate::middleware::chat_service_mw::AICloudServiceMiddleware;
|
use crate::middleware::chat_service_mw::AICloudServiceMiddleware;
|
||||||
use crate::notification::{chat_notification_builder, ChatNotification};
|
use crate::notification::{chat_notification_builder, ChatNotification};
|
||||||
use crate::persistence::{insert_chat_messages, select_chat_messages, ChatMessageTable};
|
use crate::persistence::{
|
||||||
|
insert_chat_messages, select_chat_messages, select_message_where_match_reply_message_id,
|
||||||
|
ChatMessageTable,
|
||||||
|
};
|
||||||
use crate::stream_message::StreamMessage;
|
use crate::stream_message::StreamMessage;
|
||||||
use allo_isolate::Isolate;
|
use allo_isolate::Isolate;
|
||||||
use flowy_ai_pub::cloud::{
|
use flowy_ai_pub::cloud::{
|
||||||
@ -138,9 +141,60 @@ impl Chat {
|
|||||||
// Save message to disk
|
// Save message to disk
|
||||||
save_and_notify_message(uid, &self.chat_id, &self.user_service, question.clone())?;
|
save_and_notify_message(uid, &self.chat_id, &self.user_service, question.clone())?;
|
||||||
|
|
||||||
|
self.stream_response(
|
||||||
|
answer_stream_port,
|
||||||
|
answer_stream_buffer,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
question.message_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let question_pb = ChatMessagePB::from(question);
|
||||||
|
Ok(question_pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all, err)]
|
||||||
|
pub async fn stream_regenerate_response(
|
||||||
|
&self,
|
||||||
|
question_id: i64,
|
||||||
|
answer_stream_port: i64,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
trace!(
|
||||||
|
"[Chat] regenerate and stream chat message: chat_id={}",
|
||||||
|
self.chat_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// clear
|
||||||
|
self
|
||||||
|
.stop_stream
|
||||||
|
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
self.stream_buffer.lock().await.clear();
|
||||||
|
|
||||||
|
let answer_stream_buffer = self.stream_buffer.clone();
|
||||||
|
let uid = self.user_service.user_id()?;
|
||||||
|
let workspace_id = self.user_service.workspace_id()?;
|
||||||
|
|
||||||
|
self.stream_response(
|
||||||
|
answer_stream_port,
|
||||||
|
answer_stream_buffer,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
question_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stream_response(
|
||||||
|
&self,
|
||||||
|
answer_stream_port: i64,
|
||||||
|
answer_stream_buffer: Arc<Mutex<StringBuffer>>,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: String,
|
||||||
|
question_id: i64,
|
||||||
|
) {
|
||||||
let stop_stream = self.stop_stream.clone();
|
let stop_stream = self.stop_stream.clone();
|
||||||
let chat_id = self.chat_id.clone();
|
let chat_id = self.chat_id.clone();
|
||||||
let question_id = question.message_id;
|
|
||||||
let cloud_service = self.chat_service.clone();
|
let cloud_service = self.chat_service.clone();
|
||||||
let user_service = self.user_service.clone();
|
let user_service = self.user_service.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@ -217,9 +271,6 @@ impl Chat {
|
|||||||
save_and_notify_message(uid, &chat_id, &user_service, answer)?;
|
save_and_notify_message(uid, &chat_id, &user_service, answer)?;
|
||||||
Ok::<(), FlowyError>(())
|
Ok::<(), FlowyError>(())
|
||||||
});
|
});
|
||||||
|
|
||||||
let question_pb = ChatMessagePB::from(question);
|
|
||||||
Ok(question_pb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load chat messages for a given `chat_id`.
|
/// Load chat messages for a given `chat_id`.
|
||||||
@ -393,6 +444,30 @@ impl Chat {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_question_id_from_answer_id(
|
||||||
|
&self,
|
||||||
|
answer_message_id: i64,
|
||||||
|
) -> Result<i64, FlowyError> {
|
||||||
|
let conn = self.user_service.sqlite_connection(self.uid)?;
|
||||||
|
|
||||||
|
let local_result = select_message_where_match_reply_message_id(conn, answer_message_id)?
|
||||||
|
.map(|message| message.message_id);
|
||||||
|
|
||||||
|
if let Some(message_id) = local_result {
|
||||||
|
return Ok(message_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let workspace_id = self.user_service.workspace_id()?;
|
||||||
|
let chat_id = self.chat_id.clone();
|
||||||
|
let cloud_service = self.chat_service.clone();
|
||||||
|
|
||||||
|
let question = cloud_service
|
||||||
|
.get_question_from_answer_id(&workspace_id, &chat_id, answer_message_id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(question.message_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_related_question(
|
pub async fn get_related_question(
|
||||||
&self,
|
&self,
|
||||||
message_id: i64,
|
message_id: i64,
|
||||||
|
|||||||
@ -71,6 +71,19 @@ pub struct StreamChatPayloadPB {
|
|||||||
pub metadata: Vec<ChatMessageMetaPB>,
|
pub metadata: Vec<ChatMessageMetaPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, ProtoBuf, Validate, Clone, Debug)]
|
||||||
|
pub struct RegenerateResponsePB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
#[validate(custom(function = "required_not_empty_str"))]
|
||||||
|
pub chat_id: String,
|
||||||
|
|
||||||
|
#[pb(index = 2)]
|
||||||
|
pub answer_message_id: i64,
|
||||||
|
|
||||||
|
#[pb(index = 3)]
|
||||||
|
pub answer_stream_port: i64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, ProtoBuf, Validate, Clone, Debug)]
|
#[derive(Default, ProtoBuf, Validate, Clone, Debug)]
|
||||||
pub struct ChatMessageMetaPB {
|
pub struct ChatMessageMetaPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
|
|||||||
@ -77,6 +77,24 @@ pub(crate) async fn stream_chat_message_handler(
|
|||||||
data_result_ok(result)
|
data_result_ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
|
pub(crate) async fn regenerate_response_handler(
|
||||||
|
data: AFPluginData<RegenerateResponsePB>,
|
||||||
|
ai_manager: AFPluginState<Weak<AIManager>>,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
let data = data.try_into_inner()?;
|
||||||
|
|
||||||
|
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||||
|
ai_manager
|
||||||
|
.stream_regenerate_response(
|
||||||
|
&data.chat_id,
|
||||||
|
data.answer_message_id,
|
||||||
|
data.answer_stream_port,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
pub(crate) async fn load_prev_message_handler(
|
pub(crate) async fn load_prev_message_handler(
|
||||||
data: AFPluginData<LoadPrevChatMessagePB>,
|
data: AFPluginData<LoadPrevChatMessagePB>,
|
||||||
|
|||||||
@ -59,6 +59,7 @@ pub fn init(ai_manager: Weak<AIManager>) -> AFPlugin {
|
|||||||
.event(AIEvent::GetChatInfo, create_chat_context_handler)
|
.event(AIEvent::GetChatInfo, create_chat_context_handler)
|
||||||
.event(AIEvent::GetChatSettings, get_chat_settings_handler)
|
.event(AIEvent::GetChatSettings, get_chat_settings_handler)
|
||||||
.event(AIEvent::UpdateChatSettings, update_chat_settings_handler)
|
.event(AIEvent::UpdateChatSettings, update_chat_settings_handler)
|
||||||
|
.event(AIEvent::RegenerateResponse, regenerate_response_handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
||||||
@ -150,4 +151,7 @@ pub enum AIEvent {
|
|||||||
|
|
||||||
#[event(input = "UpdateChatSettingsPB")]
|
#[event(input = "UpdateChatSettingsPB")]
|
||||||
UpdateChatSettings = 26,
|
UpdateChatSettings = 26,
|
||||||
|
|
||||||
|
#[event(input = "RegenerateResponsePB")]
|
||||||
|
RegenerateResponse = 27,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -224,6 +224,18 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_question_from_answer_id(
|
||||||
|
&self,
|
||||||
|
workspace_id: &str,
|
||||||
|
chat_id: &str,
|
||||||
|
answer_id: i64,
|
||||||
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
|
self
|
||||||
|
.cloud_service
|
||||||
|
.get_question_from_answer_id(workspace_id, chat_id, answer_id)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_related_message(
|
async fn get_related_message(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
|
|||||||
@ -82,3 +82,13 @@ pub fn select_single_message(
|
|||||||
.optional()?;
|
.optional()?;
|
||||||
Ok(message)
|
Ok(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_message_where_match_reply_message_id(
|
||||||
|
mut conn: DBConnection,
|
||||||
|
answer_message_id_val: i64,
|
||||||
|
) -> QueryResult<Option<ChatMessageTable>> {
|
||||||
|
dsl::chat_message_table
|
||||||
|
.filter(chat_message_table::reply_message_id.eq(answer_message_id_val))
|
||||||
|
.first::<ChatMessageTable>(&mut *conn)
|
||||||
|
.optional()
|
||||||
|
}
|
||||||
|
|||||||
@ -56,6 +56,15 @@ impl AIQueryService for ChatQueryServiceImpl {
|
|||||||
|
|
||||||
Ok(ids)
|
Ok(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn sync_rag_documents(&self, rag_ids: Vec<String>) -> Result<(), FlowyError> {
|
||||||
|
for rag_id in rag_ids.iter() {
|
||||||
|
if let Some(_query_collab) = self.folder_query.get_collab(rag_id).await {
|
||||||
|
// TODO(nathan): sync
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChatUserServiceImpl(Weak<AuthenticateUser>);
|
struct ChatUserServiceImpl(Weak<AuthenticateUser>);
|
||||||
|
|||||||
@ -34,7 +34,7 @@ use tokio::sync::RwLock;
|
|||||||
use crate::integrate::server::ServerProvider;
|
use crate::integrate::server::ServerProvider;
|
||||||
|
|
||||||
use collab_plugins::local_storage::kv::KVTransactionDB;
|
use collab_plugins::local_storage::kv::KVTransactionDB;
|
||||||
use flowy_folder_pub::query::FolderQueryService;
|
use flowy_folder_pub::query::{FolderQueryService, QueryCollab};
|
||||||
use lib_infra::async_trait::async_trait;
|
use lib_infra::async_trait::async_trait;
|
||||||
|
|
||||||
pub struct FolderDepsResolver();
|
pub struct FolderDepsResolver();
|
||||||
@ -707,4 +707,9 @@ impl FolderQueryService for FolderQueryServiceImpl {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_collab(&self, _object_id: &str) -> Option<QueryCollab> {
|
||||||
|
// TODO(nathan): return query collab
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -715,6 +715,19 @@ impl ChatCloudService for ServerProvider {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_question_from_answer_id(
|
||||||
|
&self,
|
||||||
|
workspace_id: &str,
|
||||||
|
chat_id: &str,
|
||||||
|
answer_message_id: i64,
|
||||||
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
|
self
|
||||||
|
.get_server()?
|
||||||
|
.chat_service()
|
||||||
|
.get_question_from_answer_id(workspace_id, chat_id, answer_message_id)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_related_message(
|
async fn get_related_message(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
|
|||||||
@ -356,6 +356,9 @@ pub enum ErrorCode {
|
|||||||
|
|
||||||
#[error("Requested namespace has one or more invalid characters")]
|
#[error("Requested namespace has one or more invalid characters")]
|
||||||
CustomNamespaceInvalidCharacter = 122,
|
CustomNamespaceInvalidCharacter = 122,
|
||||||
|
|
||||||
|
#[error("Requested namespace has one or more invalid characters")]
|
||||||
|
AIServiceUnavailable = 123,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorCode {
|
impl ErrorCode {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use client_api::error::{AppResponseError, ErrorCode as AppErrorCode};
|
|
||||||
|
|
||||||
use crate::{ErrorCode, FlowyError};
|
use crate::{ErrorCode, FlowyError};
|
||||||
|
use client_api::error::{AppResponseError, ErrorCode as AppErrorCode};
|
||||||
|
|
||||||
impl From<AppResponseError> for FlowyError {
|
impl From<AppResponseError> for FlowyError {
|
||||||
fn from(error: AppResponseError) -> Self {
|
fn from(error: AppResponseError) -> Self {
|
||||||
@ -37,6 +36,7 @@ impl From<AppResponseError> for FlowyError {
|
|||||||
AppErrorCode::PublishNameInvalidCharacter => ErrorCode::PublishNameInvalidCharacter,
|
AppErrorCode::PublishNameInvalidCharacter => ErrorCode::PublishNameInvalidCharacter,
|
||||||
AppErrorCode::PublishNameTooLong => ErrorCode::PublishNameTooLong,
|
AppErrorCode::PublishNameTooLong => ErrorCode::PublishNameTooLong,
|
||||||
AppErrorCode::CustomNamespaceInvalidCharacter => ErrorCode::CustomNamespaceInvalidCharacter,
|
AppErrorCode::CustomNamespaceInvalidCharacter => ErrorCode::CustomNamespaceInvalidCharacter,
|
||||||
|
AppErrorCode::AIServiceUnavailable => ErrorCode::AIServiceUnavailable,
|
||||||
_ => ErrorCode::Internal,
|
_ => ErrorCode::Internal,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,14 @@
|
|||||||
|
use collab::entity::EncodedCollab;
|
||||||
|
use collab_entity::CollabType;
|
||||||
use collab_folder::ViewLayout;
|
use collab_folder::ViewLayout;
|
||||||
use lib_infra::async_trait::async_trait;
|
use lib_infra::async_trait::async_trait;
|
||||||
|
|
||||||
|
pub struct QueryCollab {
|
||||||
|
pub name: String,
|
||||||
|
pub collab_type: CollabType,
|
||||||
|
pub encoded_collab: EncodedCollab,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait FolderQueryService: Send + Sync + 'static {
|
pub trait FolderQueryService: Send + Sync + 'static {
|
||||||
async fn get_sibling_ids_with_view_layout(
|
async fn get_sibling_ids_with_view_layout(
|
||||||
@ -8,4 +16,6 @@ pub trait FolderQueryService: Send + Sync + 'static {
|
|||||||
parent_view_id: &str,
|
parent_view_id: &str,
|
||||||
view_layout: ViewLayout,
|
view_layout: ViewLayout,
|
||||||
) -> Vec<String>;
|
) -> Vec<String>;
|
||||||
|
|
||||||
|
async fn get_collab(&self, object_id: &str) -> Option<QueryCollab>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,11 +99,11 @@ where
|
|||||||
message_id: i64,
|
message_id: i64,
|
||||||
) -> Result<StreamAnswer, FlowyError> {
|
) -> Result<StreamAnswer, FlowyError> {
|
||||||
let try_get_client = self.inner.try_get_client();
|
let try_get_client = self.inner.try_get_client();
|
||||||
let stream = try_get_client?
|
let result = try_get_client?
|
||||||
.stream_answer_v2(workspace_id, chat_id, message_id)
|
.stream_answer_v2(workspace_id, chat_id, message_id)
|
||||||
.await
|
.await;
|
||||||
.map_err(FlowyError::from)?
|
|
||||||
.map_err(FlowyError::from);
|
let stream = result.map_err(FlowyError::from)?.map_err(FlowyError::from);
|
||||||
Ok(stream.boxed())
|
Ok(stream.boxed())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +137,22 @@ where
|
|||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_question_from_answer_id(
|
||||||
|
&self,
|
||||||
|
workspace_id: &str,
|
||||||
|
chat_id: &str,
|
||||||
|
answer_message_id: i64,
|
||||||
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
|
let try_get_client = self.inner.try_get_client()?;
|
||||||
|
let resp = try_get_client
|
||||||
|
.get_question_message_from_answer_id(workspace_id, chat_id, answer_message_id)
|
||||||
|
.await
|
||||||
|
.map_err(FlowyError::from)?
|
||||||
|
.ok_or_else(FlowyError::record_not_found)?;
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_related_message(
|
async fn get_related_message(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
|
|||||||
@ -64,6 +64,15 @@ impl ChatCloudService for DefaultChatCloudServiceImpl {
|
|||||||
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
|
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_question_from_answer_id(
|
||||||
|
&self,
|
||||||
|
_workspace_id: &str,
|
||||||
|
_chat_id: &str,
|
||||||
|
_answer_id: i64,
|
||||||
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
|
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_related_message(
|
async fn get_related_message(
|
||||||
&self,
|
&self,
|
||||||
_workspace_id: &str,
|
_workspace_id: &str,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user