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),
|
||||
splashColor: Colors.transparent,
|
||||
child: Container(
|
||||
height: 52,
|
||||
height: 44,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
|
||||
@ -30,7 +30,8 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
super(ChatState.initial()) {
|
||||
_startListening();
|
||||
_dispatch();
|
||||
_init();
|
||||
_loadMessages();
|
||||
_loadSetting();
|
||||
}
|
||||
|
||||
final String chatId;
|
||||
@ -59,16 +60,6 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
AnswerStream? answerStream;
|
||||
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
|
||||
Future<void> close() async {
|
||||
await answerStream?.dispose();
|
||||
@ -86,14 +77,23 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
await chatController.insert(message, index: 0);
|
||||
}
|
||||
|
||||
if (initialFetchCounter < 2) {
|
||||
initialFetchCounter++;
|
||||
}
|
||||
|
||||
if (state.loadingState.isLoading && initialFetchCounter >= 2) {
|
||||
emit(
|
||||
state.copyWith(loadingState: const ChatLoadingState.finish()),
|
||||
);
|
||||
switch (state.loadingState) {
|
||||
case LoadChatMessageStatus.loading
|
||||
when chatController.messages.isEmpty:
|
||||
emit(
|
||||
state.copyWith(
|
||||
loadingState: LoadChatMessageStatus.loadingRemote,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case LoadChatMessageStatus.loading:
|
||||
case LoadChatMessageStatus.loadingRemote:
|
||||
emit(
|
||||
state.copyWith(loadingState: LoadChatMessageStatus.ready),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
loadPreviousMessages: () {
|
||||
@ -165,18 +165,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
) {
|
||||
numSendMessage += 1;
|
||||
|
||||
final relatedQuestionMessages = chatController.messages
|
||||
.where(
|
||||
(message) =>
|
||||
onetimeMessageTypeFromMeta(message.metadata) ==
|
||||
OnetimeShotType.relatedQuestion,
|
||||
)
|
||||
.toList();
|
||||
|
||||
for (final message in relatedQuestionMessages) {
|
||||
chatController.remove(message);
|
||||
}
|
||||
|
||||
_clearRelatedQuestions();
|
||||
_startStreamingMessage(message, metadata);
|
||||
lastSentMessage = null;
|
||||
|
||||
@ -229,7 +218,6 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
answerStreamMessageId = '';
|
||||
},
|
||||
startAnswerStreaming: (Message message) {
|
||||
chatController.insert(message);
|
||||
emit(
|
||||
state.copyWith(
|
||||
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) {
|
||||
emit(
|
||||
state.copyWith(selectedSourceIds: settings.ragIds),
|
||||
@ -348,10 +347,10 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
);
|
||||
}
|
||||
|
||||
void _init() async {
|
||||
void _loadSetting() async {
|
||||
final getChatSettingsPayload =
|
||||
AIEventGetChatSettings(ChatId(value: chatId));
|
||||
final getChatSettingsFuture = getChatSettingsPayload.send().fold(
|
||||
await getChatSettingsPayload.send().fold(
|
||||
(settings) {
|
||||
if (!isClosed) {
|
||||
add(ChatEvent.didReceiveChatSettings(settings: settings));
|
||||
@ -359,13 +358,14 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
},
|
||||
Log.error,
|
||||
);
|
||||
}
|
||||
|
||||
void _loadMessages() async {
|
||||
final loadMessagesPayload = LoadNextChatMessagePB(
|
||||
chatId: chatId,
|
||||
limit: Int64(10),
|
||||
);
|
||||
final loadMessagesFuture =
|
||||
AIEventLoadNextMessage(loadMessagesPayload).send().fold(
|
||||
await AIEventLoadNextMessage(loadMessagesPayload).send().fold(
|
||||
(list) {
|
||||
if (!isClosed) {
|
||||
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"),
|
||||
);
|
||||
|
||||
await Future.wait([getChatSettingsFuture, loadMessagesFuture]);
|
||||
}
|
||||
|
||||
bool _isOneTimeMessage(Message message) {
|
||||
@ -433,6 +431,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
);
|
||||
|
||||
add(ChatEvent.finishSending(question));
|
||||
add(ChatEvent.receiveMessage(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(
|
||||
AnswerStream stream,
|
||||
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
|
||||
@ -539,6 +583,9 @@ class ChatEvent with _$ChatEvent {
|
||||
_FinishSendMessage;
|
||||
const factory ChatEvent.failedSending() = _FailSendMessage;
|
||||
|
||||
// regenerate
|
||||
const factory ChatEvent.regenerateAnswer(String id) = _RegenerateAnswer;
|
||||
|
||||
// streaming answer
|
||||
const factory ChatEvent.startAnswerStreaming(Message message) =
|
||||
_StartAnswerStreaming;
|
||||
@ -567,13 +614,13 @@ class ChatEvent with _$ChatEvent {
|
||||
class ChatState with _$ChatState {
|
||||
const factory ChatState({
|
||||
required List<String> selectedSourceIds,
|
||||
required ChatLoadingState loadingState,
|
||||
required LoadChatMessageStatus loadingState,
|
||||
required PromptResponseState promptResponseState,
|
||||
}) = _ChatState;
|
||||
|
||||
factory ChatState.initial() => const ChatState(
|
||||
selectedSourceIds: [],
|
||||
loadingState: ChatLoadingState.loading(),
|
||||
loadingState: LoadChatMessageStatus.loading,
|
||||
promptResponseState: PromptResponseState.ready,
|
||||
);
|
||||
}
|
||||
|
||||
@ -132,3 +132,9 @@ const onetimeShotType = "OnetimeShotType";
|
||||
OnetimeShotType? onetimeMessageTypeFromMeta(Map<String, dynamic>? metadata) {
|
||||
return metadata?[onetimeShotType];
|
||||
}
|
||||
|
||||
enum LoadChatMessageStatus {
|
||||
loading,
|
||||
loadingRemote,
|
||||
ready,
|
||||
}
|
||||
|
||||
@ -120,20 +120,13 @@ class _ChatContentPage extends StatelessWidget {
|
||||
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||
child: BlocBuilder<ChatBloc, ChatState>(
|
||||
builder: (context, state) {
|
||||
return state.loadingState.when(
|
||||
loading: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
},
|
||||
finish: (_) {
|
||||
final chatController =
|
||||
context.read<ChatBloc>().chatController;
|
||||
return Column(
|
||||
return switch (state.loadingState) {
|
||||
LoadChatMessageStatus.ready => Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Chat(
|
||||
chatController: chatController,
|
||||
chatController:
|
||||
context.read<ChatBloc>().chatController,
|
||||
user: User(id: userProfile.id.toString()),
|
||||
darkTheme: ChatTheme.fromThemeData(Theme.of(context)),
|
||||
theme: ChatTheme.fromThemeData(Theme.of(context)),
|
||||
@ -148,9 +141,9 @@ class _ChatContentPage extends StatelessWidget {
|
||||
),
|
||||
_buildInput(context),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
),
|
||||
_ => const Center(child: CircularProgressIndicator.adaptive()),
|
||||
};
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -223,6 +216,8 @@ class _ChatContentPage extends StatelessWidget {
|
||||
isLastMessage: isLastMessage,
|
||||
onSelectedMetadata: (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.showActions,
|
||||
this.isLastMessage = false,
|
||||
this.onRegenerate,
|
||||
});
|
||||
|
||||
final Message message;
|
||||
final Widget child;
|
||||
final bool showActions;
|
||||
final bool isLastMessage;
|
||||
final void Function(String)? onRegenerate;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -60,6 +62,7 @@ class ChatAIMessageBubble extends StatelessWidget {
|
||||
Widget _wrapBottomActions(Widget child) {
|
||||
return ChatAIBottomInlineActions(
|
||||
message: message,
|
||||
onRegenerate: onRegenerate,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
@ -67,6 +70,7 @@ class ChatAIMessageBubble extends StatelessWidget {
|
||||
Widget _wrapHover(Widget child) {
|
||||
return ChatAIMessageHover(
|
||||
message: message,
|
||||
onRegenerate: onRegenerate,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
@ -74,6 +78,7 @@ class ChatAIMessageBubble extends StatelessWidget {
|
||||
Widget _wrapPopMenu(Widget child) {
|
||||
return ChatAIMessagePopup(
|
||||
message: message,
|
||||
onRegenerate: onRegenerate,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
@ -84,10 +89,12 @@ class ChatAIBottomInlineActions extends StatelessWidget {
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.message,
|
||||
this.onRegenerate,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final Message message;
|
||||
final void Function(String)? onRegenerate;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -107,6 +114,9 @@ class ChatAIBottomInlineActions extends StatelessWidget {
|
||||
CopyButton(
|
||||
textMessage: message as TextMessage,
|
||||
),
|
||||
RegenerateButton(
|
||||
onTap: () => onRegenerate?.call(message.id),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -121,10 +131,12 @@ class ChatAIMessageHover extends StatefulWidget {
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.message,
|
||||
this.onRegenerate,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final Message message;
|
||||
final void Function(String)? onRegenerate;
|
||||
|
||||
@override
|
||||
State<ChatAIMessageHover> createState() => _ChatAIMessageHoverState();
|
||||
@ -206,6 +218,10 @@ class _ChatAIMessageHoverState extends State<ChatAIMessageHover> {
|
||||
CopyButton(
|
||||
textMessage: widget.message as TextMessage,
|
||||
),
|
||||
RegenerateButton(
|
||||
onTap: () =>
|
||||
widget.onRegenerate?.call(widget.message.id),
|
||||
),
|
||||
],
|
||||
)
|
||||
: 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 {
|
||||
const ChatAIMessagePopup({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.message,
|
||||
this.onRegenerate,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final Message message;
|
||||
final void Function(String)? onRegenerate;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -393,33 +438,11 @@ class ChatAIMessagePopup extends StatelessWidget {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
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(),
|
||||
),
|
||||
const VSpace(16.0),
|
||||
_copyButton(context, bottomSheetContext),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
_regenerateButton(context),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -428,4 +451,46 @@ class ChatAIMessagePopup extends StatelessWidget {
|
||||
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.refSourceJsonString,
|
||||
this.onSelectedMetadata,
|
||||
this.onRegenerate,
|
||||
this.isLastMessage = false,
|
||||
});
|
||||
|
||||
@ -45,6 +46,7 @@ class ChatAIMessageWidget extends StatelessWidget {
|
||||
final String chatId;
|
||||
final String? refSourceJsonString;
|
||||
final void Function(ChatMessageRefSource metadata)? onSelectedMetadata;
|
||||
final void Function(String messageId)? onRegenerate;
|
||||
final bool isLastMessage;
|
||||
|
||||
@override
|
||||
@ -80,6 +82,7 @@ class ChatAIMessageWidget extends StatelessWidget {
|
||||
message: message,
|
||||
isLastMessage: isLastMessage,
|
||||
showActions: stream == null && state.text.isNotEmpty,
|
||||
onRegenerate: onRegenerate,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -101,11 +104,6 @@ class ChatAIMessageWidget extends StatelessWidget {
|
||||
onError: (error) {
|
||||
return ChatErrorMessageWidget(
|
||||
errorMessage: LocaleKeys.chat_aiServerUnavailable.tr(),
|
||||
onRetry: () {
|
||||
context
|
||||
.read<ChatAIMessageBloc>()
|
||||
.add(const ChatAIMessageEvent.retry());
|
||||
},
|
||||
);
|
||||
},
|
||||
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]]
|
||||
name = "app-error"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -192,7 +192,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "appflowy-ai-client"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -888,7 +888,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
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 = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -944,7 +944,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
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 = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -957,7 +957,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
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 = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -1257,7 +1257,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1282,7 +1282,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1679,7 +1679,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3268,7 +3268,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -3285,7 +3285,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3840,7 +3840,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -6576,7 +6576,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
||||
@ -59,7 +59,7 @@ collab-importer = { version = "0.1" }
|
||||
# Run the script:
|
||||
# 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]
|
||||
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]]
|
||||
name = "app-error"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -183,7 +183,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "appflowy-ai-client"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -877,7 +877,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
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 = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -933,7 +933,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
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 = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -946,7 +946,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
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 = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -1255,7 +1255,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1280,7 +1280,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1684,7 +1684,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3350,7 +3350,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -3367,7 +3367,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3927,7 +3927,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -6656,7 +6656,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
||||
@ -58,7 +58,7 @@ collab-importer = { version = "0.1" }
|
||||
# Run the script:
|
||||
# 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]
|
||||
serde_json.workspace = true
|
||||
|
||||
@ -180,8 +180,8 @@
|
||||
"inputLocalAIMessageHint": "Ask @:appName Local AI",
|
||||
"unsupportedCloudPrompt": "This feature is only available when using @:appName Cloud",
|
||||
"relatedQuestion": "Suggested",
|
||||
"serverUnavailable": "Service Temporarily Unavailable. Please try again later.",
|
||||
"aiServerUnavailable": "Connection lost. Please check your internet and",
|
||||
"serverUnavailable": "Connection lost. Please check your internet and",
|
||||
"aiServerUnavailable": "The AI service is temporarily unavailable. Please try again later.",
|
||||
"retry": "Retry",
|
||||
"clickToRetry": "Click to retry",
|
||||
"regenerateAnswer": "Regenerate",
|
||||
@ -205,7 +205,8 @@
|
||||
"questionDetail": "Hi {}! How can I help you today?",
|
||||
"indexingFile": "Indexing {}",
|
||||
"generatingResponse": "Generating response",
|
||||
"selectSources": "Select Sources"
|
||||
"selectSources": "Select Sources",
|
||||
"regenerate": "Try again"
|
||||
},
|
||||
"trash": {
|
||||
"text": "Trash",
|
||||
|
||||
42
frontend/rust-lib/Cargo.lock
generated
42
frontend/rust-lib/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -183,7 +183,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "appflowy-ai-client"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -780,7 +780,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
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 = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -836,7 +836,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
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 = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -849,7 +849,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
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 = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -1118,7 +1118,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1143,7 +1143,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1389,7 +1389,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.11.2",
|
||||
"phf 0.8.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -1538,7 +1538,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2982,7 +2982,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2999,7 +2999,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3484,7 +3484,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -4499,7 +4499,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros 0.8.0",
|
||||
"phf_macros",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
@ -4519,7 +4519,6 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -4587,19 +4586,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -5855,7 +5841,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
||||
@ -107,8 +107,8 @@ dashmap = "6.0.1"
|
||||
# Run the script.add_workspace_members:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "abf827f2a6bebc70ef8829909dcf6acb3ce24715" }
|
||||
client-api-entity = { 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 = "9f57fa63d5ee039f50b0d27b860679deb14fee2e" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
|
||||
@ -70,6 +70,13 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
||||
limit: u64,
|
||||
) -> 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(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
|
||||
@ -37,6 +37,8 @@ pub trait AIQueryService: Send + Sync + 'static {
|
||||
parent_view_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<Vec<String>, FlowyError>;
|
||||
|
||||
async fn sync_rag_documents(&self, rag_ids: Vec<String>) -> Result<(), FlowyError>;
|
||||
}
|
||||
|
||||
pub struct AIManager {
|
||||
@ -102,6 +104,7 @@ impl AIManager {
|
||||
if self.local_ai_controller.is_running() {
|
||||
self.local_ai_controller.open_chat(chat_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -193,6 +196,22 @@ impl AIManager {
|
||||
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> {
|
||||
let chat = self.chats.get(chat_id).as_deref().cloned();
|
||||
match chat {
|
||||
|
||||
@ -4,7 +4,10 @@ use crate::entities::{
|
||||
};
|
||||
use crate::middleware::chat_service_mw::AICloudServiceMiddleware;
|
||||
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 allo_isolate::Isolate;
|
||||
use flowy_ai_pub::cloud::{
|
||||
@ -138,9 +141,60 @@ impl Chat {
|
||||
// Save message to disk
|
||||
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 chat_id = self.chat_id.clone();
|
||||
let question_id = question.message_id;
|
||||
let cloud_service = self.chat_service.clone();
|
||||
let user_service = self.user_service.clone();
|
||||
tokio::spawn(async move {
|
||||
@ -217,9 +271,6 @@ impl Chat {
|
||||
save_and_notify_message(uid, &chat_id, &user_service, answer)?;
|
||||
Ok::<(), FlowyError>(())
|
||||
});
|
||||
|
||||
let question_pb = ChatMessagePB::from(question);
|
||||
Ok(question_pb)
|
||||
}
|
||||
|
||||
/// Load chat messages for a given `chat_id`.
|
||||
@ -393,6 +444,30 @@ impl Chat {
|
||||
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(
|
||||
&self,
|
||||
message_id: i64,
|
||||
|
||||
@ -71,6 +71,19 @@ pub struct StreamChatPayloadPB {
|
||||
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)]
|
||||
pub struct ChatMessageMetaPB {
|
||||
#[pb(index = 1)]
|
||||
|
||||
@ -77,6 +77,24 @@ pub(crate) async fn stream_chat_message_handler(
|
||||
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)]
|
||||
pub(crate) async fn load_prev_message_handler(
|
||||
data: AFPluginData<LoadPrevChatMessagePB>,
|
||||
|
||||
@ -59,6 +59,7 @@ pub fn init(ai_manager: Weak<AIManager>) -> AFPlugin {
|
||||
.event(AIEvent::GetChatInfo, create_chat_context_handler)
|
||||
.event(AIEvent::GetChatSettings, get_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)]
|
||||
@ -150,4 +151,7 @@ pub enum AIEvent {
|
||||
|
||||
#[event(input = "UpdateChatSettingsPB")]
|
||||
UpdateChatSettings = 26,
|
||||
|
||||
#[event(input = "RegenerateResponsePB")]
|
||||
RegenerateResponse = 27,
|
||||
}
|
||||
|
||||
@ -224,6 +224,18 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
||||
.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(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
|
||||
@ -82,3 +82,13 @@ pub fn select_single_message(
|
||||
.optional()?;
|
||||
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)
|
||||
}
|
||||
|
||||
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>);
|
||||
|
||||
@ -34,7 +34,7 @@ use tokio::sync::RwLock;
|
||||
use crate::integrate::server::ServerProvider;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
|
||||
@ -356,6 +356,9 @@ pub enum ErrorCode {
|
||||
|
||||
#[error("Requested namespace has one or more invalid characters")]
|
||||
CustomNamespaceInvalidCharacter = 122,
|
||||
|
||||
#[error("Requested namespace has one or more invalid characters")]
|
||||
AIServiceUnavailable = 123,
|
||||
}
|
||||
|
||||
impl ErrorCode {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use client_api::error::{AppResponseError, ErrorCode as AppErrorCode};
|
||||
|
||||
use crate::{ErrorCode, FlowyError};
|
||||
use client_api::error::{AppResponseError, ErrorCode as AppErrorCode};
|
||||
|
||||
impl From<AppResponseError> for FlowyError {
|
||||
fn from(error: AppResponseError) -> Self {
|
||||
@ -37,6 +36,7 @@ impl From<AppResponseError> for FlowyError {
|
||||
AppErrorCode::PublishNameInvalidCharacter => ErrorCode::PublishNameInvalidCharacter,
|
||||
AppErrorCode::PublishNameTooLong => ErrorCode::PublishNameTooLong,
|
||||
AppErrorCode::CustomNamespaceInvalidCharacter => ErrorCode::CustomNamespaceInvalidCharacter,
|
||||
AppErrorCode::AIServiceUnavailable => ErrorCode::AIServiceUnavailable,
|
||||
_ => ErrorCode::Internal,
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,14 @@
|
||||
use collab::entity::EncodedCollab;
|
||||
use collab_entity::CollabType;
|
||||
use collab_folder::ViewLayout;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
|
||||
pub struct QueryCollab {
|
||||
pub name: String,
|
||||
pub collab_type: CollabType,
|
||||
pub encoded_collab: EncodedCollab,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait FolderQueryService: Send + Sync + 'static {
|
||||
async fn get_sibling_ids_with_view_layout(
|
||||
@ -8,4 +16,6 @@ pub trait FolderQueryService: Send + Sync + 'static {
|
||||
parent_view_id: &str,
|
||||
view_layout: ViewLayout,
|
||||
) -> Vec<String>;
|
||||
|
||||
async fn get_collab(&self, object_id: &str) -> Option<QueryCollab>;
|
||||
}
|
||||
|
||||
@ -99,11 +99,11 @@ where
|
||||
message_id: i64,
|
||||
) -> Result<StreamAnswer, FlowyError> {
|
||||
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)
|
||||
.await
|
||||
.map_err(FlowyError::from)?
|
||||
.map_err(FlowyError::from);
|
||||
.await;
|
||||
|
||||
let stream = result.map_err(FlowyError::from)?.map_err(FlowyError::from);
|
||||
Ok(stream.boxed())
|
||||
}
|
||||
|
||||
@ -137,6 +137,22 @@ where
|
||||
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(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
|
||||
@ -64,6 +64,15 @@ impl ChatCloudService for DefaultChatCloudServiceImpl {
|
||||
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(
|
||||
&self,
|
||||
_workspace_id: &str,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user