mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support mention a document as context on local ai (#5913)
* feat: support mention a document as context on local ai * chore: rename * chore: fix test * chore: fix test
This commit is contained in:
parent
f84473c857
commit
758c304a74
@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_message_stream.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:ffi';
|
|
||||||
import 'dart:isolate';
|
|
||||||
|
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_message_stream.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/code.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/code.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
@ -18,18 +16,12 @@ import 'package:flutter_chat_types/flutter_chat_types.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:nanoid/nanoid.dart';
|
import 'package:nanoid/nanoid.dart';
|
||||||
|
|
||||||
|
import 'chat_entity.dart';
|
||||||
import 'chat_message_listener.dart';
|
import 'chat_message_listener.dart';
|
||||||
import 'chat_message_service.dart';
|
import 'chat_message_service.dart';
|
||||||
|
|
||||||
part 'chat_bloc.g.dart';
|
|
||||||
part 'chat_bloc.freezed.dart';
|
part 'chat_bloc.freezed.dart';
|
||||||
|
|
||||||
const sendMessageErrorKey = "sendMessageError";
|
|
||||||
const systemUserId = "system";
|
|
||||||
const aiResponseUserId = "0";
|
|
||||||
const messageMetadataKey = "metadata";
|
|
||||||
const messageQuestionIdKey = "question";
|
|
||||||
|
|
||||||
class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||||
ChatBloc({
|
ChatBloc({
|
||||||
required ViewPB view,
|
required ViewPB view,
|
||||||
@ -100,7 +92,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
_loadPrevMessage(beforeMessageId);
|
_loadPrevMessage(beforeMessageId);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
loadingPreviousStatus: const LoadingState.loading(),
|
loadingPreviousStatus: const ChatLoadingState.loading(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -116,7 +108,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
messages: uniqueMessages,
|
messages: uniqueMessages,
|
||||||
loadingPreviousStatus: const LoadingState.finish(),
|
loadingPreviousStatus: const ChatLoadingState.finish(),
|
||||||
hasMorePrevMessage: hasMore,
|
hasMorePrevMessage: hasMore,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -130,7 +122,7 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
messages: uniqueMessages,
|
messages: uniqueMessages,
|
||||||
initialLoadingStatus: const LoadingState.finish(),
|
initialLoadingStatus: const ChatLoadingState.finish(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -200,6 +192,14 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
sendMessage: (String message, Map<String, dynamic>? metadata) async {
|
sendMessage: (String message, Map<String, dynamic>? metadata) async {
|
||||||
unawaited(_startStreamingMessage(message, metadata, emit));
|
unawaited(_startStreamingMessage(message, metadata, emit));
|
||||||
final allMessages = _perminentMessages();
|
final allMessages = _perminentMessages();
|
||||||
|
// allMessages.insert(
|
||||||
|
// 0,
|
||||||
|
// CustomMessage(
|
||||||
|
// metadata: OnetimeShotType.sendingMessage.toMap(),
|
||||||
|
// author: User(id: state.userProfile.id.toString()),
|
||||||
|
// id: state.userProfile.id.toString(),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
lastSentMessage: null,
|
lastSentMessage: null,
|
||||||
@ -406,7 +406,6 @@ class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
|||||||
|
|
||||||
Message _createStreamMessage(AnswerStream stream, Int64 questionMessageId) {
|
Message _createStreamMessage(AnswerStream stream, Int64 questionMessageId) {
|
||||||
final streamMessageId = (questionMessageId + 1).toString();
|
final streamMessageId = (questionMessageId + 1).toString();
|
||||||
|
|
||||||
lastStreamMessageId = streamMessageId;
|
lastStreamMessageId = streamMessageId;
|
||||||
|
|
||||||
return TextMessage(
|
return TextMessage(
|
||||||
@ -488,10 +487,10 @@ class ChatState with _$ChatState {
|
|||||||
required UserProfilePB userProfile,
|
required UserProfilePB userProfile,
|
||||||
// When opening the chat, the initial loading status will be set as loading.
|
// When opening the chat, the initial loading status will be set as loading.
|
||||||
//After the initial loading is done, the status will be set as finished.
|
//After the initial loading is done, the status will be set as finished.
|
||||||
required LoadingState initialLoadingStatus,
|
required ChatLoadingState initialLoadingStatus,
|
||||||
// When loading previous messages, the status will be set as loading.
|
// When loading previous messages, the status will be set as loading.
|
||||||
// After the loading is done, the status will be set as finished.
|
// After the loading is done, the status will be set as finished.
|
||||||
required LoadingState loadingPreviousStatus,
|
required ChatLoadingState loadingPreviousStatus,
|
||||||
// When sending a user message, the status will be set as loading.
|
// When sending a user message, the status will be set as loading.
|
||||||
// After the message is sent, the status will be set as finished.
|
// After the message is sent, the status will be set as finished.
|
||||||
required StreamingState streamingState,
|
required StreamingState streamingState,
|
||||||
@ -511,8 +510,8 @@ class ChatState with _$ChatState {
|
|||||||
view: view,
|
view: view,
|
||||||
messages: [],
|
messages: [],
|
||||||
userProfile: userProfile,
|
userProfile: userProfile,
|
||||||
initialLoadingStatus: const LoadingState.finish(),
|
initialLoadingStatus: const ChatLoadingState.finish(),
|
||||||
loadingPreviousStatus: const LoadingState.finish(),
|
loadingPreviousStatus: const ChatLoadingState.finish(),
|
||||||
streamingState: const StreamingState.done(),
|
streamingState: const StreamingState.done(),
|
||||||
sendingState: const SendMessageState.done(),
|
sendingState: const SendMessageState.done(),
|
||||||
hasMorePrevMessage: true,
|
hasMorePrevMessage: true,
|
||||||
@ -525,169 +524,3 @@ bool isOtherUserMessage(Message message) {
|
|||||||
message.author.id != systemUserId &&
|
message.author.id != systemUserId &&
|
||||||
!message.author.id.startsWith("streamId:");
|
!message.author.id.startsWith("streamId:");
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
|
||||||
class LoadingState with _$LoadingState {
|
|
||||||
const factory LoadingState.loading() = _Loading;
|
|
||||||
const factory LoadingState.finish({FlowyError? error}) = _Finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum OnetimeShotType {
|
|
||||||
unknown,
|
|
||||||
sendingMessage,
|
|
||||||
relatedQuestion,
|
|
||||||
invalidSendMesssage,
|
|
||||||
}
|
|
||||||
|
|
||||||
const onetimeShotType = "OnetimeShotType";
|
|
||||||
|
|
||||||
extension OnetimeMessageTypeExtension on OnetimeShotType {
|
|
||||||
static OnetimeShotType fromString(String value) {
|
|
||||||
switch (value) {
|
|
||||||
case 'OnetimeShotType.relatedQuestion':
|
|
||||||
return OnetimeShotType.relatedQuestion;
|
|
||||||
case 'OnetimeShotType.invalidSendMesssage':
|
|
||||||
return OnetimeShotType.invalidSendMesssage;
|
|
||||||
default:
|
|
||||||
Log.error('Unknown OnetimeShotType: $value');
|
|
||||||
return OnetimeShotType.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return {
|
|
||||||
onetimeShotType: toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OnetimeShotType? onetimeMessageTypeFromMeta(Map<String, dynamic>? metadata) {
|
|
||||||
if (metadata == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final entry in metadata.entries) {
|
|
||||||
if (entry.key == onetimeShotType) {
|
|
||||||
return OnetimeMessageTypeExtension.fromString(entry.value as String);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnswerStream {
|
|
||||||
AnswerStream() {
|
|
||||||
_port.handler = _controller.add;
|
|
||||||
_subscription = _controller.stream.listen(
|
|
||||||
(event) {
|
|
||||||
if (event.startsWith("data:")) {
|
|
||||||
_hasStarted = true;
|
|
||||||
final newText = event.substring(5);
|
|
||||||
_text += newText;
|
|
||||||
if (_onData != null) {
|
|
||||||
_onData!(_text);
|
|
||||||
}
|
|
||||||
} else if (event.startsWith("error:")) {
|
|
||||||
_error = event.substring(5);
|
|
||||||
if (_onError != null) {
|
|
||||||
_onError!(_error!);
|
|
||||||
}
|
|
||||||
} else if (event.startsWith("metadata:")) {
|
|
||||||
if (_onMetadata != null) {
|
|
||||||
final s = event.substring(9);
|
|
||||||
_onMetadata!(messageRefSourceFromString(s));
|
|
||||||
}
|
|
||||||
} else if (event == "AI_RESPONSE_LIMIT") {
|
|
||||||
if (_onAIResponseLimit != null) {
|
|
||||||
_onAIResponseLimit!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDone: () {
|
|
||||||
if (_onEnd != null) {
|
|
||||||
_onEnd!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (error) {
|
|
||||||
if (_onError != null) {
|
|
||||||
_onError!(error.toString());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final RawReceivePort _port = RawReceivePort();
|
|
||||||
final StreamController<String> _controller = StreamController.broadcast();
|
|
||||||
late StreamSubscription<String> _subscription;
|
|
||||||
bool _hasStarted = false;
|
|
||||||
String? _error;
|
|
||||||
String _text = "";
|
|
||||||
|
|
||||||
// Callbacks
|
|
||||||
void Function(String text)? _onData;
|
|
||||||
void Function()? _onStart;
|
|
||||||
void Function()? _onEnd;
|
|
||||||
void Function(String error)? _onError;
|
|
||||||
void Function()? _onAIResponseLimit;
|
|
||||||
void Function(List<ChatMessageRefSource> metadata)? _onMetadata;
|
|
||||||
|
|
||||||
int get nativePort => _port.sendPort.nativePort;
|
|
||||||
bool get hasStarted => _hasStarted;
|
|
||||||
String? get error => _error;
|
|
||||||
String get text => _text;
|
|
||||||
|
|
||||||
Future<void> dispose() async {
|
|
||||||
await _controller.close();
|
|
||||||
await _subscription.cancel();
|
|
||||||
_port.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void listen({
|
|
||||||
void Function(String text)? onData,
|
|
||||||
void Function()? onStart,
|
|
||||||
void Function()? onEnd,
|
|
||||||
void Function(String error)? onError,
|
|
||||||
void Function()? onAIResponseLimit,
|
|
||||||
void Function(List<ChatMessageRefSource> metadata)? onMetadata,
|
|
||||||
}) {
|
|
||||||
_onData = onData;
|
|
||||||
_onStart = onStart;
|
|
||||||
_onEnd = onEnd;
|
|
||||||
_onError = onError;
|
|
||||||
_onAIResponseLimit = onAIResponseLimit;
|
|
||||||
_onMetadata = onMetadata;
|
|
||||||
|
|
||||||
if (_onStart != null) {
|
|
||||||
_onStart!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class ChatMessageRefSource {
|
|
||||||
ChatMessageRefSource({
|
|
||||||
required this.id,
|
|
||||||
required this.name,
|
|
||||||
required this.source,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory ChatMessageRefSource.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ChatMessageRefSourceFromJson(json);
|
|
||||||
|
|
||||||
final String id;
|
|
||||||
final String name;
|
|
||||||
final String source;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$ChatMessageRefSourceToJson(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class StreamingState with _$StreamingState {
|
|
||||||
const factory StreamingState.streaming() = _Streaming;
|
|
||||||
const factory StreamingState.done({FlowyError? error}) = _StreamDone;
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class SendMessageState with _$SendMessageState {
|
|
||||||
const factory SendMessageState.sending() = _Sending;
|
|
||||||
const factory SendMessageState.done({FlowyError? error}) = _SendDone;
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,176 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pbenum.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
part 'chat_entity.g.dart';
|
||||||
|
part 'chat_entity.freezed.dart';
|
||||||
|
|
||||||
|
const sendMessageErrorKey = "sendMessageError";
|
||||||
|
const systemUserId = "system";
|
||||||
|
const aiResponseUserId = "0";
|
||||||
|
const messageMetadataKey = "metadata";
|
||||||
|
const messageQuestionIdKey = "question";
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ChatMessageRefSource {
|
||||||
|
ChatMessageRefSource({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.source,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ChatMessageRefSource.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ChatMessageRefSourceFromJson(json);
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final String source;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$ChatMessageRefSourceToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class StreamingState with _$StreamingState {
|
||||||
|
const factory StreamingState.streaming() = _Streaming;
|
||||||
|
const factory StreamingState.done({FlowyError? error}) = _StreamDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SendMessageState with _$SendMessageState {
|
||||||
|
const factory SendMessageState.sending() = _Sending;
|
||||||
|
const factory SendMessageState.done({FlowyError? error}) = _SendDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatFile extends Equatable {
|
||||||
|
const ChatFile({
|
||||||
|
required this.filePath,
|
||||||
|
required this.fileName,
|
||||||
|
required this.fileType,
|
||||||
|
});
|
||||||
|
|
||||||
|
static ChatFile? fromFilePath(String filePath) {
|
||||||
|
final file = File(filePath);
|
||||||
|
if (!file.existsSync()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final fileName = path.basename(filePath);
|
||||||
|
final extension = path.extension(filePath).toLowerCase();
|
||||||
|
|
||||||
|
ChatMessageMetaTypePB fileType;
|
||||||
|
switch (extension) {
|
||||||
|
case '.pdf':
|
||||||
|
fileType = ChatMessageMetaTypePB.PDF;
|
||||||
|
break;
|
||||||
|
case '.txt':
|
||||||
|
fileType = ChatMessageMetaTypePB.Txt;
|
||||||
|
break;
|
||||||
|
case '.md':
|
||||||
|
fileType = ChatMessageMetaTypePB.Markdown;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fileType = ChatMessageMetaTypePB.UnknownMetaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChatFile(
|
||||||
|
filePath: filePath,
|
||||||
|
fileName: fileName,
|
||||||
|
fileType: fileType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String filePath;
|
||||||
|
final String fileName;
|
||||||
|
final ChatMessageMetaTypePB fileType;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [filePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ChatFileTypeExtension on ChatMessageMetaTypePB {
|
||||||
|
Widget get icon {
|
||||||
|
switch (this) {
|
||||||
|
case ChatMessageMetaTypePB.PDF:
|
||||||
|
return const FlowySvg(
|
||||||
|
FlowySvgs.file_pdf_s,
|
||||||
|
color: Color(0xff00BCF0),
|
||||||
|
);
|
||||||
|
case ChatMessageMetaTypePB.Txt:
|
||||||
|
return const FlowySvg(
|
||||||
|
FlowySvgs.file_txt_s,
|
||||||
|
color: Color(0xff00BCF0),
|
||||||
|
);
|
||||||
|
case ChatMessageMetaTypePB.Markdown:
|
||||||
|
return const FlowySvg(
|
||||||
|
FlowySvgs.file_md_s,
|
||||||
|
color: Color(0xff00BCF0),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return const FlowySvg(FlowySvgs.file_unknown_s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ChatInputFileMetadata = Map<String, ChatFile>;
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class ChatLoadingState with _$ChatLoadingState {
|
||||||
|
const factory ChatLoadingState.loading() = _Loading;
|
||||||
|
const factory ChatLoadingState.finish({FlowyError? error}) = _Finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ChatLoadingStateExtension on ChatLoadingState {
|
||||||
|
bool get isLoading => this is _Loading;
|
||||||
|
bool get isFinish => this is _Finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OnetimeShotType {
|
||||||
|
unknown,
|
||||||
|
sendingMessage,
|
||||||
|
relatedQuestion,
|
||||||
|
invalidSendMesssage,
|
||||||
|
}
|
||||||
|
|
||||||
|
const onetimeShotType = "OnetimeShotType";
|
||||||
|
|
||||||
|
extension OnetimeMessageTypeExtension on OnetimeShotType {
|
||||||
|
static OnetimeShotType fromString(String value) {
|
||||||
|
switch (value) {
|
||||||
|
case 'OnetimeShotType.sendingMessage':
|
||||||
|
return OnetimeShotType.sendingMessage;
|
||||||
|
case 'OnetimeShotType.relatedQuestion':
|
||||||
|
return OnetimeShotType.relatedQuestion;
|
||||||
|
case 'OnetimeShotType.invalidSendMesssage':
|
||||||
|
return OnetimeShotType.invalidSendMesssage;
|
||||||
|
default:
|
||||||
|
Log.error('Unknown OnetimeShotType: $value');
|
||||||
|
return OnetimeShotType.unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
onetimeShotType: toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnetimeShotType? onetimeMessageTypeFromMeta(Map<String, dynamic>? metadata) {
|
||||||
|
if (metadata == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final entry in metadata.entries) {
|
||||||
|
if (entry.key == onetimeShotType) {
|
||||||
|
return OnetimeMessageTypeExtension.fromString(entry.value as String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
@ -1,23 +1,17 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/ai/local_llm_listener.dart';
|
import 'package:appflowy/workspace/application/settings/ai/local_llm_listener.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:path/path.dart' as path;
|
|
||||||
|
|
||||||
import 'chat_input_bloc.dart';
|
import 'chat_input_bloc.dart';
|
||||||
|
|
||||||
part 'chat_file_bloc.freezed.dart';
|
part 'chat_file_bloc.freezed.dart';
|
||||||
|
|
||||||
typedef ChatInputFileMetadata = Map<String, ChatFile>;
|
|
||||||
|
|
||||||
class ChatFileBloc extends Bloc<ChatFileEvent, ChatFileState> {
|
class ChatFileBloc extends Bloc<ChatFileEvent, ChatFileState> {
|
||||||
ChatFileBloc()
|
ChatFileBloc()
|
||||||
: listener = LocalLLMListener(),
|
: listener = LocalLLMListener(),
|
||||||
@ -158,64 +152,3 @@ class ChatFileState with _$ChatFileState {
|
|||||||
@Default(AIType.appflowyAI()) AIType aiType,
|
@Default(AIType.appflowyAI()) AIType aiType,
|
||||||
}) = _ChatFileState;
|
}) = _ChatFileState;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatFile extends Equatable {
|
|
||||||
const ChatFile({
|
|
||||||
required this.filePath,
|
|
||||||
required this.fileName,
|
|
||||||
required this.fileType,
|
|
||||||
});
|
|
||||||
|
|
||||||
static ChatFile? fromFilePath(String filePath) {
|
|
||||||
final file = File(filePath);
|
|
||||||
if (!file.existsSync()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final fileName = path.basename(filePath);
|
|
||||||
final extension = path.extension(filePath).toLowerCase();
|
|
||||||
|
|
||||||
ChatMessageMetaTypePB fileType;
|
|
||||||
switch (extension) {
|
|
||||||
case '.pdf':
|
|
||||||
fileType = ChatMessageMetaTypePB.PDF;
|
|
||||||
break;
|
|
||||||
case '.txt':
|
|
||||||
fileType = ChatMessageMetaTypePB.Txt;
|
|
||||||
break;
|
|
||||||
case '.md':
|
|
||||||
fileType = ChatMessageMetaTypePB.Markdown;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fileType = ChatMessageMetaTypePB.UnknownMetaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChatFile(
|
|
||||||
filePath: filePath,
|
|
||||||
fileName: fileName,
|
|
||||||
fileType: fileType,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String filePath;
|
|
||||||
final String fileName;
|
|
||||||
final ChatMessageMetaTypePB fileType;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [filePath];
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ChatFileTypeExtension on ChatMessageMetaTypePB {
|
|
||||||
Widget get icon {
|
|
||||||
switch (this) {
|
|
||||||
case ChatMessageMetaTypePB.PDF:
|
|
||||||
return const FlowySvg(FlowySvgs.file_pdf_s);
|
|
||||||
case ChatMessageMetaTypePB.Txt:
|
|
||||||
return const FlowySvg(FlowySvgs.file_txt_s);
|
|
||||||
case ChatMessageMetaTypePB.Markdown:
|
|
||||||
return const FlowySvg(FlowySvgs.file_md_s);
|
|
||||||
default:
|
|
||||||
return const FlowySvg(FlowySvgs.file_unknown_s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
@ -10,8 +10,6 @@ import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:nanoid/nanoid.dart';
|
import 'package:nanoid/nanoid.dart';
|
||||||
|
|
||||||
import 'chat_file_bloc.dart';
|
|
||||||
|
|
||||||
List<ChatFile> fileListFromMessageMetadata(
|
List<ChatFile> fileListFromMessageMetadata(
|
||||||
Map<String, dynamic>? map,
|
Map<String, dynamic>? map,
|
||||||
) {
|
) {
|
||||||
@ -117,7 +115,7 @@ Future<List<ChatMessageMetaPB>> metadataPBFromMetadata(
|
|||||||
name: view.name,
|
name: view.name,
|
||||||
data: pb.text,
|
data: pb.text,
|
||||||
dataType: ChatMessageMetaTypePB.Txt,
|
dataType: ChatMessageMetaTypePB.Txt,
|
||||||
source: "appflowy document",
|
source: "appflowy",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}, (err) {
|
}, (err) {
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'dart:isolate';
|
||||||
|
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_message_service.dart';
|
||||||
|
|
||||||
|
class AnswerStream {
|
||||||
|
AnswerStream() {
|
||||||
|
_port.handler = _controller.add;
|
||||||
|
_subscription = _controller.stream.listen(
|
||||||
|
(event) {
|
||||||
|
if (event.startsWith("data:")) {
|
||||||
|
_hasStarted = true;
|
||||||
|
final newText = event.substring(5);
|
||||||
|
_text += newText;
|
||||||
|
if (_onData != null) {
|
||||||
|
_onData!(_text);
|
||||||
|
}
|
||||||
|
} else if (event.startsWith("error:")) {
|
||||||
|
_error = event.substring(5);
|
||||||
|
if (_onError != null) {
|
||||||
|
_onError!(_error!);
|
||||||
|
}
|
||||||
|
} else if (event.startsWith("metadata:")) {
|
||||||
|
if (_onMetadata != null) {
|
||||||
|
final s = event.substring(9);
|
||||||
|
_onMetadata!(messageRefSourceFromString(s));
|
||||||
|
}
|
||||||
|
} else if (event == "AI_RESPONSE_LIMIT") {
|
||||||
|
if (_onAIResponseLimit != null) {
|
||||||
|
_onAIResponseLimit!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
if (_onEnd != null) {
|
||||||
|
_onEnd!();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (error) {
|
||||||
|
if (_onError != null) {
|
||||||
|
_onError!(error.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final RawReceivePort _port = RawReceivePort();
|
||||||
|
final StreamController<String> _controller = StreamController.broadcast();
|
||||||
|
late StreamSubscription<String> _subscription;
|
||||||
|
bool _hasStarted = false;
|
||||||
|
String? _error;
|
||||||
|
String _text = "";
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
void Function(String text)? _onData;
|
||||||
|
void Function()? _onStart;
|
||||||
|
void Function()? _onEnd;
|
||||||
|
void Function(String error)? _onError;
|
||||||
|
void Function()? _onAIResponseLimit;
|
||||||
|
void Function(List<ChatMessageRefSource> metadata)? _onMetadata;
|
||||||
|
|
||||||
|
int get nativePort => _port.sendPort.nativePort;
|
||||||
|
bool get hasStarted => _hasStarted;
|
||||||
|
String? get error => _error;
|
||||||
|
String get text => _text;
|
||||||
|
|
||||||
|
Future<void> dispose() async {
|
||||||
|
await _controller.close();
|
||||||
|
await _subscription.cancel();
|
||||||
|
_port.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void listen({
|
||||||
|
void Function(String text)? onData,
|
||||||
|
void Function()? onStart,
|
||||||
|
void Function()? onEnd,
|
||||||
|
void Function(String error)? onError,
|
||||||
|
void Function()? onAIResponseLimit,
|
||||||
|
void Function(List<ChatMessageRefSource> metadata)? onMetadata,
|
||||||
|
}) {
|
||||||
|
_onData = onData;
|
||||||
|
_onStart = onStart;
|
||||||
|
_onEnd = onEnd;
|
||||||
|
_onError = onError;
|
||||||
|
_onAIResponseLimit = onAIResponseLimit;
|
||||||
|
_onMetadata = onMetadata;
|
||||||
|
|
||||||
|
if (_onStart != null) {
|
||||||
|
_onStart!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
import 'chat_file_bloc.dart';
|
|
||||||
import 'chat_message_service.dart';
|
import 'chat_message_service.dart';
|
||||||
|
|
||||||
part 'chat_user_message_bloc.freezed.dart';
|
part 'chat_user_message_bloc.freezed.dart';
|
||||||
@ -10,7 +10,7 @@ part 'chat_user_message_bloc.freezed.dart';
|
|||||||
class ChatUserMessageBloc
|
class ChatUserMessageBloc
|
||||||
extends Bloc<ChatUserMessageEvent, ChatUserMessageState> {
|
extends Bloc<ChatUserMessageEvent, ChatUserMessageState> {
|
||||||
ChatUserMessageBloc({
|
ChatUserMessageBloc({
|
||||||
required TextMessage message,
|
required Message message,
|
||||||
required String? metadata,
|
required String? metadata,
|
||||||
}) : super(
|
}) : super(
|
||||||
ChatUserMessageState.initial(
|
ChatUserMessageState.initial(
|
||||||
@ -36,12 +36,12 @@ class ChatUserMessageEvent with _$ChatUserMessageEvent {
|
|||||||
@freezed
|
@freezed
|
||||||
class ChatUserMessageState with _$ChatUserMessageState {
|
class ChatUserMessageState with _$ChatUserMessageState {
|
||||||
const factory ChatUserMessageState({
|
const factory ChatUserMessageState({
|
||||||
required TextMessage message,
|
required Message message,
|
||||||
required List<ChatFile> files,
|
required List<ChatFile> files,
|
||||||
}) = _ChatUserMessageState;
|
}) = _ChatUserMessageState;
|
||||||
|
|
||||||
factory ChatUserMessageState.initial(
|
factory ChatUserMessageState.initial(
|
||||||
TextMessage message,
|
Message message,
|
||||||
List<ChatFile> files,
|
List<ChatFile> files,
|
||||||
) =>
|
) =>
|
||||||
ChatUserMessageState(message: message, files: files);
|
ChatUserMessageState(message: message, files: files);
|
||||||
|
@ -2,8 +2,10 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_input_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_input_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_message_stream.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/chat_related_question.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/chat_related_question.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/message/ai_message_bubble.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/message/ai_message_bubble.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/message/other_user_message_bubble.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/message/other_user_message_bubble.dart';
|
||||||
@ -256,27 +258,26 @@ class _ChatContentPageState extends State<_ChatContentPage> {
|
|||||||
theme: buildTheme(context),
|
theme: buildTheme(context),
|
||||||
onEndReached: () async {
|
onEndReached: () async {
|
||||||
if (state.hasMorePrevMessage &&
|
if (state.hasMorePrevMessage &&
|
||||||
state.loadingPreviousStatus != const LoadingState.loading()) {
|
state.loadingPreviousStatus.isFinish) {
|
||||||
blocContext
|
blocContext
|
||||||
.read<ChatBloc>()
|
.read<ChatBloc>()
|
||||||
.add(const ChatEvent.startLoadingPrevMessage());
|
.add(const ChatEvent.startLoadingPrevMessage());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emptyState: BlocBuilder<ChatBloc, ChatState>(
|
emptyState: BlocBuilder<ChatBloc, ChatState>(
|
||||||
builder: (_, state) =>
|
builder: (_, state) => state.initialLoadingStatus.isFinish
|
||||||
state.initialLoadingStatus == const LoadingState.finish()
|
? Padding(
|
||||||
? Padding(
|
padding: AIChatUILayout.welcomePagePadding,
|
||||||
padding: AIChatUILayout.welcomePagePadding,
|
child: ChatWelcomePage(
|
||||||
child: ChatWelcomePage(
|
userProfile: widget.userProfile,
|
||||||
userProfile: widget.userProfile,
|
onSelectedQuestion: (question) => blocContext
|
||||||
onSelectedQuestion: (question) => blocContext
|
.read<ChatBloc>()
|
||||||
.read<ChatBloc>()
|
.add(ChatEvent.sendMessage(message: question)),
|
||||||
.add(ChatEvent.sendMessage(message: question)),
|
),
|
||||||
),
|
)
|
||||||
)
|
: const Center(
|
||||||
: const Center(
|
child: CircularProgressIndicator.adaptive(),
|
||||||
child: CircularProgressIndicator.adaptive(),
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
messageWidthRatio: AIChatUILayout.messageWidthRatio,
|
messageWidthRatio: AIChatUILayout.messageWidthRatio,
|
||||||
textMessageBuilder: (
|
textMessageBuilder: (
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart';
|
||||||
@ -115,7 +116,7 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: _inputFocusNode.hasFocus && !isMobile
|
color: _inputFocusNode.hasFocus && !isMobile
|
||||||
? Theme.of(context).colorScheme.primary.withOpacity(0.6)
|
? Theme.of(context).colorScheme.primary.withOpacity(0.6)
|
||||||
: Colors.grey.shade700,
|
: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
),
|
),
|
||||||
@ -160,9 +161,8 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
Expanded(child: _inputTextField(context, textPadding)),
|
Expanded(child: _inputTextField(context, textPadding)),
|
||||||
|
|
||||||
// at button
|
// at button
|
||||||
if (PlatformExtension.isDesktop &&
|
// TODO(lucas): support mobile
|
||||||
widget.aiType == const AIType.appflowyAI())
|
if (PlatformExtension.isDesktop) _atButton(buttonPadding),
|
||||||
_atButton(buttonPadding),
|
|
||||||
|
|
||||||
// send button
|
// send button
|
||||||
_sendButton(buttonPadding),
|
_sendButton(buttonPadding),
|
||||||
@ -267,10 +267,6 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleOnTextChange(BuildContext context, String text) async {
|
Future<void> _handleOnTextChange(BuildContext context, String text) async {
|
||||||
if (widget.aiType != const AIType.appflowyAI()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_inputActionControl.onTextChanged(text)) {
|
if (!_inputActionControl.onTextChanged(text)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_input_file_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_input_file_bloc.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
@ -67,17 +67,18 @@ class ChatFilePreview extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color:
|
||||||
Theme.of(context).colorScheme.surfaceContainerHighest,
|
Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8.0,
|
horizontal: 10.0,
|
||||||
vertical: 10,
|
vertical: 14,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
file.fileType.icon,
|
file.fileType.icon,
|
||||||
const HSpace(6),
|
const HSpace(6),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/chat_avatar.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/chat_avatar.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/chat_input/chat_input.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/chat_input/chat_input.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/chat_popmenu.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/chat_popmenu.dart';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_ai_message_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_ai_message_bloc.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/chat_loading.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/chat_loading.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/message/ai_markdown_text.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/message/ai_markdown_text.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_member_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_member_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_user_message_bloc.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/chat_avatar.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/chat_avatar.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
|
||||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
|
||||||
|
|
||||||
class ChatUserMessageBubble extends StatelessWidget {
|
class ChatUserMessageBubble extends StatelessWidget {
|
||||||
const ChatUserMessageBubble({
|
const ChatUserMessageBubble({
|
||||||
@ -33,141 +29,130 @@ class ChatUserMessageBubble extends StatelessWidget {
|
|||||||
.read<ChatMemberBloc>()
|
.read<ChatMemberBloc>()
|
||||||
.add(ChatMemberEvent.getMemberInfo(message.author.id));
|
.add(ChatMemberEvent.getMemberInfo(message.author.id));
|
||||||
}
|
}
|
||||||
|
final metadata = message.metadata?[messageMetadataKey] as String?;
|
||||||
|
|
||||||
return BlocConsumer<ChatMemberBloc, ChatMemberState>(
|
return BlocProvider(
|
||||||
listenWhen: (previous, current) {
|
create: (context) => ChatUserMessageBloc(
|
||||||
return previous.members[message.author.id] !=
|
message: message,
|
||||||
current.members[message.author.id];
|
metadata: metadata,
|
||||||
},
|
),
|
||||||
listener: (context, state) {},
|
child: BlocBuilder<ChatUserMessageBloc, ChatUserMessageState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final member = state.members[message.author.id];
|
return Column(
|
||||||
return Row(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.min,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
if (state.files.isNotEmpty) ...[
|
||||||
// _wrapHover(
|
Padding(
|
||||||
Flexible(
|
padding: const EdgeInsets.only(right: defaultAvatarSize + 32),
|
||||||
child: DecoratedBox(
|
child: _MessageFileList(files: state.files),
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: borderRadius,
|
|
||||||
color: backgroundColor,
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
const VSpace(6),
|
||||||
padding: const EdgeInsets.symmetric(
|
],
|
||||||
horizontal: 16,
|
Row(
|
||||||
vertical: 12,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
color: backgroundColor,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: child,
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: BlocConsumer<ChatMemberBloc, ChatMemberState>(
|
||||||
|
listenWhen: (previous, current) =>
|
||||||
|
previous.members[message.author.id] !=
|
||||||
|
current.members[message.author.id],
|
||||||
|
listener: (context, state) {},
|
||||||
|
builder: (context, state) {
|
||||||
|
final member = state.members[message.author.id];
|
||||||
|
return ChatUserAvatar(
|
||||||
|
iconUrl: member?.info.avatarUrl ?? "",
|
||||||
|
name: member?.info.name ?? "",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
// ),
|
);
|
||||||
Padding(
|
},
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
),
|
||||||
child: ChatUserAvatar(
|
);
|
||||||
iconUrl: member?.info.avatarUrl ?? "",
|
}
|
||||||
name: member?.info.name ?? "",
|
}
|
||||||
|
|
||||||
|
class _MessageFileList extends StatelessWidget {
|
||||||
|
const _MessageFileList({required this.files});
|
||||||
|
|
||||||
|
final List<ChatFile> files;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final List<Widget> children = files
|
||||||
|
.map(
|
||||||
|
(file) => _MessageFile(
|
||||||
|
file: file,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return Wrap(
|
||||||
|
direction: Axis.vertical,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.end,
|
||||||
|
spacing: 6,
|
||||||
|
runSpacing: 6,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MessageFile extends StatelessWidget {
|
||||||
|
const _MessageFile({required this.file});
|
||||||
|
|
||||||
|
final ChatFile file;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox.square(dimension: 16, child: file.fileType.icon),
|
||||||
|
const HSpace(6),
|
||||||
|
Flexible(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 400),
|
||||||
|
child: FlowyText(
|
||||||
|
file.fileName,
|
||||||
|
fontSize: 12,
|
||||||
|
maxLines: 6,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChatUserMessageHover extends StatefulWidget {
|
|
||||||
const ChatUserMessageHover({
|
|
||||||
super.key,
|
|
||||||
required this.child,
|
|
||||||
required this.message,
|
|
||||||
});
|
|
||||||
|
|
||||||
final Widget child;
|
|
||||||
final Message message;
|
|
||||||
final bool autoShowHover = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ChatUserMessageHover> createState() => _ChatUserMessageHoverState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChatUserMessageHoverState extends State<ChatUserMessageHover> {
|
|
||||||
bool _isHover = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_isHover = widget.autoShowHover ? false : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final List<Widget> children = [
|
|
||||||
DecoratedBox(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.transparent,
|
|
||||||
borderRadius: Corners.s6Border,
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 30),
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_isHover) {
|
|
||||||
if (widget.message is TextMessage) {
|
|
||||||
children.add(
|
|
||||||
EditButton(
|
|
||||||
textMessage: widget.message as TextMessage,
|
|
||||||
).positioned(right: 0, bottom: 0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.click,
|
|
||||||
opaque: false,
|
|
||||||
onEnter: (p) => setState(() {
|
|
||||||
if (widget.autoShowHover) {
|
|
||||||
_isHover = true;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
onExit: (p) => setState(() {
|
|
||||||
if (widget.autoShowHover) {
|
|
||||||
_isHover = false;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
child: Stack(
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EditButton extends StatelessWidget {
|
|
||||||
const EditButton({
|
|
||||||
super.key,
|
|
||||||
required this.textMessage,
|
|
||||||
});
|
|
||||||
final TextMessage textMessage;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FlowyTooltip(
|
|
||||||
message: LocaleKeys.settings_menu_clickToCopy.tr(),
|
|
||||||
child: FlowyIconButton(
|
|
||||||
width: 24,
|
|
||||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
|
||||||
fillColor: Theme.of(context).cardColor,
|
|
||||||
icon: FlowySvg(
|
|
||||||
FlowySvgs.ai_copy_s,
|
|
||||||
size: const Size.square(14),
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_user_message_bloc.dart';
|
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
||||||
|
|
||||||
class ChatUserTextMessageWidget extends StatelessWidget {
|
class ChatUserTextMessageWidget extends StatelessWidget {
|
||||||
@ -23,28 +19,8 @@ class ChatUserTextMessageWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return TextMessageText(
|
||||||
create: (context) => ChatUserMessageBloc(
|
text: message.text,
|
||||||
message: message,
|
|
||||||
metadata: metadata,
|
|
||||||
),
|
|
||||||
child: BlocBuilder<ChatUserMessageBloc, ChatUserMessageState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
if (state.files.isNotEmpty) ...[
|
|
||||||
_MessageFileList(files: state.files),
|
|
||||||
const VSpace(6),
|
|
||||||
],
|
|
||||||
TextMessageText(
|
|
||||||
text: message.text,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,59 +47,3 @@ class TextMessageText extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MessageFileList extends StatelessWidget {
|
|
||||||
const _MessageFileList({required this.files});
|
|
||||||
|
|
||||||
final List<ChatFile> files;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final List<Widget> children = files
|
|
||||||
.map(
|
|
||||||
(file) => _MessageFile(
|
|
||||||
file: file,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return Wrap(
|
|
||||||
spacing: 6,
|
|
||||||
runSpacing: 6,
|
|
||||||
children: children,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MessageFile extends StatelessWidget {
|
|
||||||
const _MessageFile({required this.file});
|
|
||||||
|
|
||||||
final ChatFile file;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
file.fileType.icon,
|
|
||||||
const HSpace(6),
|
|
||||||
Flexible(
|
|
||||||
child: FlowyText(
|
|
||||||
file.fileName,
|
|
||||||
fontSize: 12,
|
|
||||||
maxLines: 6,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart';
|
||||||
@ -52,12 +52,16 @@ class DownloadModelBloc extends Bloc<DownloadModelEvent, DownloadModelState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
downloadStream: downloadStream,
|
downloadStream: downloadStream,
|
||||||
loadingState: const LoadingState.finish(),
|
loadingState: const ChatLoadingState.finish(),
|
||||||
downloadError: null,
|
downloadError: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}, (err) {
|
}, (err) {
|
||||||
emit(state.copyWith(loadingState: LoadingState.finish(error: err)));
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
loadingState: ChatLoadingState.finish(error: err),
|
||||||
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updatePercent: (String object, double percent) {
|
updatePercent: (String object, double percent) {
|
||||||
@ -95,7 +99,7 @@ class DownloadModelState with _$DownloadModelState {
|
|||||||
@Default("") String object,
|
@Default("") String object,
|
||||||
@Default(0) double percent,
|
@Default(0) double percent,
|
||||||
@Default(false) bool isFinish,
|
@Default(false) bool isFinish,
|
||||||
@Default(LoadingState.loading()) LoadingState loadingState,
|
@Default(ChatLoadingState.loading()) ChatLoadingState loadingState,
|
||||||
}) = _DownloadModelState;
|
}) = _DownloadModelState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/ai/local_llm_listener.dart';
|
import 'package:appflowy/workspace/application/settings/ai/local_llm_listener.dart';
|
||||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
@ -72,14 +72,14 @@ class LocalAIChatSettingBloc
|
|||||||
llmResource,
|
llmResource,
|
||||||
llmModel,
|
llmModel,
|
||||||
),
|
),
|
||||||
selectLLMState: const LoadingState.finish(),
|
selectLLMState: const ChatLoadingState.finish(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
selectedLLMModel: llmModel,
|
selectedLLMModel: llmModel,
|
||||||
selectLLMState: const LoadingState.finish(),
|
selectLLMState: const ChatLoadingState.finish(),
|
||||||
progressIndicator: const LocalAIProgress.checkPluginState(),
|
progressIndicator: const LocalAIProgress.checkPluginState(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -88,7 +88,7 @@ class LocalAIChatSettingBloc
|
|||||||
(err) {
|
(err) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
selectLLMState: LoadingState.finish(error: err),
|
selectLLMState: ChatLoadingState.finish(error: err),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -117,7 +117,7 @@ class LocalAIChatSettingBloc
|
|||||||
state.copyWith(
|
state.copyWith(
|
||||||
progressIndicator:
|
progressIndicator:
|
||||||
LocalAIProgress.startDownloading(state.selectedLLMModel!),
|
LocalAIProgress.startDownloading(state.selectedLLMModel!),
|
||||||
selectLLMState: const LoadingState.finish(),
|
selectLLMState: const ChatLoadingState.finish(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -128,7 +128,7 @@ class LocalAIChatSettingBloc
|
|||||||
llmResource,
|
llmResource,
|
||||||
state.selectedLLMModel!,
|
state.selectedLLMModel!,
|
||||||
),
|
),
|
||||||
selectLLMState: const LoadingState.finish(),
|
selectLLMState: const ChatLoadingState.finish(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ class LocalAIChatSettingBloc
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
progressIndicator: LocalAIProgress.startDownloading(llmModel),
|
progressIndicator: LocalAIProgress.startDownloading(llmModel),
|
||||||
selectLLMState: const LoadingState.finish(),
|
selectLLMState: const ChatLoadingState.finish(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -257,7 +257,7 @@ class LocalAIChatSettingState with _$LocalAIChatSettingState {
|
|||||||
LLMModelPB? selectedLLMModel,
|
LLMModelPB? selectedLLMModel,
|
||||||
LocalAIProgress? progressIndicator,
|
LocalAIProgress? progressIndicator,
|
||||||
@Default(AIModelProgress.init()) AIModelProgress aiModelProgress,
|
@Default(AIModelProgress.init()) AIModelProgress aiModelProgress,
|
||||||
@Default(LoadingState.loading()) LoadingState selectLLMState,
|
@Default(ChatLoadingState.loading()) ChatLoadingState selectLLMState,
|
||||||
@Default([]) List<LLMModelPB> models,
|
@Default([]) List<LLMModelPB> models,
|
||||||
@Default(RunningStatePB.Connecting) RunningStatePB runningState,
|
@Default(RunningStatePB.Connecting) RunningStatePB runningState,
|
||||||
}) = _LocalAIChatSettingState;
|
}) = _LocalAIChatSettingState;
|
||||||
|
@ -118,6 +118,10 @@ extension ViewExtension on ViewPB {
|
|||||||
|
|
||||||
bool get isSpace {
|
bool get isSpace {
|
||||||
try {
|
try {
|
||||||
|
if (extra.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
final ext = jsonDecode(extra);
|
final ext = jsonDecode(extra);
|
||||||
final isSpace = ext[ViewExtKeys.isSpaceKey] ?? false;
|
final isSpace = ext[ViewExtKeys.isSpaceKey] ?? false;
|
||||||
return isSpace;
|
return isSpace;
|
||||||
@ -138,6 +142,10 @@ extension ViewExtension on ViewPB {
|
|||||||
|
|
||||||
FlowySvg? buildSpaceIconSvg(BuildContext context, {Size? size}) {
|
FlowySvg? buildSpaceIconSvg(BuildContext context, {Size? size}) {
|
||||||
try {
|
try {
|
||||||
|
if (extra.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final ext = jsonDecode(extra);
|
final ext = jsonDecode(extra);
|
||||||
final icon = ext[ViewExtKeys.spaceIconKey];
|
final icon = ext[ViewExtKeys.spaceIconKey];
|
||||||
final color = ext[ViewExtKeys.spaceIconColorKey];
|
final color = ext[ViewExtKeys.spaceIconColorKey];
|
||||||
@ -214,6 +222,11 @@ extension ViewExtension on ViewPB {
|
|||||||
if (layout != ViewLayoutPB.Document) {
|
if (layout != ViewLayoutPB.Document) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extra.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final ext = jsonDecode(extra);
|
final ext = jsonDecode(extra);
|
||||||
final cover = ext[ViewExtKeys.coverKey] ?? {};
|
final cover = ext[ViewExtKeys.coverKey] ?? {};
|
||||||
|
34
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
34
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -172,7 +172,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
|||||||
[[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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -207,7 +207,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-local-ai"
|
name = "appflowy-local-ai"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=7dd879a6b9b3246e5cd06f1647e620553db9b960#7dd879a6b9b3246e5cd06f1647e620553db9b960"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=65802795ad8778de11c45b5af65d05c973709613#65802795ad8778de11c45b5af65d05c973709613"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"appflowy-plugin",
|
"appflowy-plugin",
|
||||||
@ -226,7 +226,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-plugin"
|
name = "appflowy-plugin"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=7dd879a6b9b3246e5cd06f1647e620553db9b960#7dd879a6b9b3246e5cd06f1647e620553db9b960"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=65802795ad8778de11c45b5af65d05c973709613#65802795ad8778de11c45b5af65d05c973709613"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -315,9 +315,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.77"
|
version = "0.1.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -423,7 +423,7 @@ dependencies = [
|
|||||||
"bitflags 2.4.0",
|
"bitflags 2.4.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools 0.10.5",
|
"itertools 0.12.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lazycell",
|
"lazycell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -826,7 +826,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -876,7 +876,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -888,7 +888,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1132,7 +1132,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1157,7 +1157,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1532,7 +1532,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3051,7 +3051,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -3068,7 +3068,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3500,7 +3500,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -6098,7 +6098,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
@ -53,7 +53,7 @@ collab-user = { version = "0.2" }
|
|||||||
# 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 = "99410fb7662440e75493df110de2283f75ab2418" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "135a67dc79848c39e9c53b4a99b6d14f444686ef" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
@ -128,5 +128,5 @@ collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-
|
|||||||
# To update the commit ID, run:
|
# To update the commit ID, run:
|
||||||
# scripts/tool/update_local_ai_rev.sh new_rev_id
|
# scripts/tool/update_local_ai_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "7dd879a6b9b3246e5cd06f1647e620553db9b960" }
|
appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "65802795ad8778de11c45b5af65d05c973709613" }
|
||||||
appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "7dd879a6b9b3246e5cd06f1647e620553db9b960" }
|
appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "65802795ad8778de11c45b5af65d05c973709613" }
|
||||||
|
32
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
32
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
|||||||
[[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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -198,7 +198,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-local-ai"
|
name = "appflowy-local-ai"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=7dd879a6b9b3246e5cd06f1647e620553db9b960#7dd879a6b9b3246e5cd06f1647e620553db9b960"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=65802795ad8778de11c45b5af65d05c973709613#65802795ad8778de11c45b5af65d05c973709613"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"appflowy-plugin",
|
"appflowy-plugin",
|
||||||
@ -217,7 +217,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-plugin"
|
name = "appflowy-plugin"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=7dd879a6b9b3246e5cd06f1647e620553db9b960#7dd879a6b9b3246e5cd06f1647e620553db9b960"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=65802795ad8778de11c45b5af65d05c973709613#65802795ad8778de11c45b5af65d05c973709613"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -325,9 +325,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.79"
|
version = "0.1.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681"
|
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -800,7 +800,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -850,7 +850,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -862,7 +862,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1115,7 +1115,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1140,7 +1140,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1522,7 +1522,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3118,7 +3118,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -3135,7 +3135,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3572,7 +3572,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -6162,7 +6162,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
@ -52,7 +52,7 @@ collab-user = { version = "0.2" }
|
|||||||
# 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 = "99410fb7662440e75493df110de2283f75ab2418" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "135a67dc79848c39e9c53b4a99b6d14f444686ef" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
@ -128,6 +128,6 @@ collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-
|
|||||||
# To update the commit ID, run:
|
# To update the commit ID, run:
|
||||||
# scripts/tool/update_local_ai_rev.sh new_rev_id
|
# scripts/tool/update_local_ai_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "7dd879a6b9b3246e5cd06f1647e620553db9b960" }
|
appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "65802795ad8778de11c45b5af65d05c973709613" }
|
||||||
appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "7dd879a6b9b3246e5cd06f1647e620553db9b960" }
|
appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "65802795ad8778de11c45b5af65d05c973709613" }
|
||||||
|
|
||||||
|
50
frontend/rust-lib/Cargo.lock
generated
50
frontend/rust-lib/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
|||||||
[[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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -198,7 +198,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-local-ai"
|
name = "appflowy-local-ai"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=7dd879a6b9b3246e5cd06f1647e620553db9b960#7dd879a6b9b3246e5cd06f1647e620553db9b960"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=65802795ad8778de11c45b5af65d05c973709613#65802795ad8778de11c45b5af65d05c973709613"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"appflowy-plugin",
|
"appflowy-plugin",
|
||||||
@ -217,7 +217,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-plugin"
|
name = "appflowy-plugin"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=7dd879a6b9b3246e5cd06f1647e620553db9b960#7dd879a6b9b3246e5cd06f1647e620553db9b960"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=65802795ad8778de11c45b5af65d05c973709613#65802795ad8778de11c45b5af65d05c973709613"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -289,9 +289,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.77"
|
version = "0.1.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -718,7 +718,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -768,7 +768,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -780,7 +780,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -993,7 +993,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1018,7 +1018,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1256,7 +1256,7 @@ dependencies = [
|
|||||||
"cssparser-macros",
|
"cssparser-macros",
|
||||||
"dtoa-short",
|
"dtoa-short",
|
||||||
"itoa",
|
"itoa",
|
||||||
"phf 0.8.0",
|
"phf 0.11.2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1356,7 +1356,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2730,7 +2730,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2747,7 +2747,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3112,7 +3112,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -4068,7 +4068,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",
|
"phf_macros 0.8.0",
|
||||||
"phf_shared 0.8.0",
|
"phf_shared 0.8.0",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
]
|
]
|
||||||
@ -4088,6 +4088,7 @@ 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",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4155,6 +4156,19 @@ 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"
|
||||||
@ -5307,7 +5321,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=99410fb7662440e75493df110de2283f75ab2418#99410fb7662440e75493df110de2283f75ab2418"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=135a67dc79848c39e9c53b4a99b6d14f444686ef#135a67dc79848c39e9c53b4a99b6d14f444686ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
@ -80,7 +80,7 @@ parking_lot = "0.12"
|
|||||||
futures = "0.3.29"
|
futures = "0.3.29"
|
||||||
tokio = "1.38.0"
|
tokio = "1.38.0"
|
||||||
tokio-stream = "0.1.14"
|
tokio-stream = "0.1.14"
|
||||||
async-trait = "0.1.74"
|
async-trait = "0.1.81"
|
||||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||||
collab = { version = "0.2" }
|
collab = { version = "0.2" }
|
||||||
collab-entity = { version = "0.2" }
|
collab-entity = { version = "0.2" }
|
||||||
@ -99,8 +99,8 @@ zip = "2.1.3"
|
|||||||
# 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 = "99410fb7662440e75493df110de2283f75ab2418" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "135a67dc79848c39e9c53b4a99b6d14f444686ef" }
|
||||||
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "99410fb7662440e75493df110de2283f75ab2418" }
|
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "135a67dc79848c39e9c53b4a99b6d14f444686ef" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
@ -147,5 +147,5 @@ collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-
|
|||||||
# To update the commit ID, run:
|
# To update the commit ID, run:
|
||||||
# scripts/tool/update_local_ai_rev.sh new_rev_id
|
# scripts/tool/update_local_ai_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "7dd879a6b9b3246e5cd06f1647e620553db9b960" }
|
appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "65802795ad8778de11c45b5af65d05c973709613" }
|
||||||
appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "7dd879a6b9b3246e5cd06f1647e620553db9b960" }
|
appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "65802795ad8778de11c45b5af65d05c973709613" }
|
||||||
|
@ -25,7 +25,7 @@ async fn af_cloud_create_chat_message_test() {
|
|||||||
&chat_id,
|
&chat_id,
|
||||||
&format!("hello world {}", i),
|
&format!("hello world {}", i),
|
||||||
ChatMessageType::System,
|
ChatMessageType::System,
|
||||||
vec![],
|
&[],
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -81,7 +81,7 @@ async fn af_cloud_load_remote_system_message_test() {
|
|||||||
&chat_id,
|
&chat_id,
|
||||||
&format!("hello server {}", i),
|
&format!("hello server {}", i),
|
||||||
ChatMessageType::System,
|
ChatMessageType::System,
|
||||||
vec![],
|
&[],
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -26,14 +26,14 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
|||||||
chat_id: &str,
|
chat_id: &str,
|
||||||
) -> FutureResult<(), FlowyError>;
|
) -> FutureResult<(), FlowyError>;
|
||||||
|
|
||||||
fn create_question(
|
async fn create_question(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
chat_id: &str,
|
chat_id: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
message_type: ChatMessageType,
|
message_type: ChatMessageType,
|
||||||
metadata: Vec<ChatMessageMetadata>,
|
metadata: &[ChatMessageMetadata],
|
||||||
) -> FutureResult<ChatMessage, FlowyError>;
|
) -> Result<ChatMessage, FlowyError>;
|
||||||
|
|
||||||
fn create_answer(
|
fn create_answer(
|
||||||
&self,
|
&self,
|
||||||
|
@ -23,7 +23,7 @@ pub trait AIUserService: Send + Sync + 'static {
|
|||||||
fn device_id(&self) -> Result<String, FlowyError>;
|
fn device_id(&self) -> Result<String, FlowyError>;
|
||||||
fn workspace_id(&self) -> Result<String, FlowyError>;
|
fn workspace_id(&self) -> Result<String, FlowyError>;
|
||||||
fn sqlite_connection(&self, uid: i64) -> Result<DBConnection, FlowyError>;
|
fn sqlite_connection(&self, uid: i64) -> Result<DBConnection, FlowyError>;
|
||||||
fn data_root_dir(&self) -> Result<PathBuf, FlowyError>;
|
fn application_root_dir(&self) -> Result<PathBuf, FlowyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AIManager {
|
pub struct AIManager {
|
||||||
@ -91,10 +91,6 @@ impl AIManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_using_local_ai(&self) -> bool {
|
|
||||||
self.local_ai_controller.is_running()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
|
pub async fn delete_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
|
||||||
if let Some((_, chat)) = self.chats.remove(chat_id) {
|
if let Some((_, chat)) = self.chats.remove(chat_id) {
|
||||||
chat.close();
|
chat.close();
|
||||||
|
@ -104,7 +104,7 @@ impl Chat {
|
|||||||
&self.chat_id,
|
&self.chat_id,
|
||||||
message,
|
message,
|
||||||
message_type,
|
message_type,
|
||||||
metadata,
|
&metadata,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -112,6 +112,16 @@ impl Chat {
|
|||||||
FlowyError::server_error()
|
FlowyError::server_error()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if self.chat_service.is_local_ai_enabled() {
|
||||||
|
if let Err(err) = self
|
||||||
|
.chat_service
|
||||||
|
.index_message_metadata(&self.chat_id, &metadata)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("Failed to index file: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
save_chat_message(
|
save_chat_message(
|
||||||
self.user_service.sqlite_connection(uid)?,
|
self.user_service.sqlite_connection(uid)?,
|
||||||
&self.chat_id,
|
&self.chat_id,
|
||||||
|
@ -15,6 +15,7 @@ use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataRes
|
|||||||
use lib_infra::isolate_stream::IsolateSink;
|
use lib_infra::isolate_stream::IsolateSink;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
use tracing::trace;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
fn upgrade_ai_manager(ai_manager: AFPluginState<Weak<AIManager>>) -> FlowyResult<Arc<AIManager>> {
|
fn upgrade_ai_manager(ai_manager: AFPluginState<Weak<AIManager>>) -> FlowyResult<Arc<AIManager>> {
|
||||||
@ -29,7 +30,6 @@ pub(crate) async fn stream_chat_message_handler(
|
|||||||
data: AFPluginData<StreamChatPayloadPB>,
|
data: AFPluginData<StreamChatPayloadPB>,
|
||||||
ai_manager: AFPluginState<Weak<AIManager>>,
|
ai_manager: AFPluginState<Weak<AIManager>>,
|
||||||
) -> DataResult<ChatMessagePB, FlowyError> {
|
) -> DataResult<ChatMessagePB, FlowyError> {
|
||||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
|
||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
data.validate()?;
|
data.validate()?;
|
||||||
|
|
||||||
@ -37,28 +37,21 @@ pub(crate) async fn stream_chat_message_handler(
|
|||||||
ChatMessageTypePB::System => ChatMessageType::System,
|
ChatMessageTypePB::System => ChatMessageType::System,
|
||||||
ChatMessageTypePB::User => ChatMessageType::User,
|
ChatMessageTypePB::User => ChatMessageType::User,
|
||||||
};
|
};
|
||||||
let is_using_local_ai = ai_manager.is_using_local_ai();
|
|
||||||
|
|
||||||
let metadata = data
|
let metadata = data
|
||||||
.metadata
|
.metadata
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|metadata| {
|
.map(|metadata| {
|
||||||
let (content_type, content_len) = if is_using_local_ai {
|
let (content_type, content_len) = match metadata.data_type {
|
||||||
(ChatMetadataContentType::Unknown, 0)
|
ChatMessageMetaTypePB::Txt => (ChatMetadataContentType::Text, metadata.data.len()),
|
||||||
} else {
|
ChatMessageMetaTypePB::Markdown => (ChatMetadataContentType::Markdown, metadata.data.len()),
|
||||||
match metadata.data_type {
|
ChatMessageMetaTypePB::PDF => (ChatMetadataContentType::PDF, 0),
|
||||||
ChatMessageMetaTypePB::Txt => (ChatMetadataContentType::Text, metadata.data.len()),
|
ChatMessageMetaTypePB::UnknownMetaType => (ChatMetadataContentType::Unknown, 0),
|
||||||
ChatMessageMetaTypePB::Markdown => {
|
|
||||||
(ChatMetadataContentType::Markdown, metadata.data.len())
|
|
||||||
},
|
|
||||||
ChatMessageMetaTypePB::PDF => (ChatMetadataContentType::PDF, 0),
|
|
||||||
ChatMessageMetaTypePB::UnknownMetaType => (ChatMetadataContentType::Unknown, 0),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ChatMessageMetadata {
|
ChatMessageMetadata {
|
||||||
data: ChatMetadataData {
|
data: ChatMetadataData {
|
||||||
content: metadata.data,
|
content: metadata.data,
|
||||||
|
url: None,
|
||||||
content_type,
|
content_type,
|
||||||
size: content_len as i64,
|
size: content_len as i64,
|
||||||
},
|
},
|
||||||
@ -70,15 +63,23 @@ pub(crate) async fn stream_chat_message_handler(
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let question = ai_manager
|
trace!("Stream chat message with metadata: {:?}", metadata);
|
||||||
.stream_chat_message(
|
let (tx, rx) = oneshot::channel::<Result<ChatMessagePB, FlowyError>>();
|
||||||
&data.chat_id,
|
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||||
&data.message,
|
tokio::spawn(async move {
|
||||||
message_type,
|
let result = ai_manager
|
||||||
data.text_stream_port,
|
.stream_chat_message(
|
||||||
metadata,
|
&data.chat_id,
|
||||||
)
|
&data.message,
|
||||||
.await?;
|
message_type,
|
||||||
|
data.text_stream_port,
|
||||||
|
metadata,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let _ = tx.send(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
let question = rx.await??;
|
||||||
data_result_ok(question)
|
data_result_ok(question)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,15 +6,21 @@ use anyhow::Error;
|
|||||||
use appflowy_local_ai::chat_plugin::{AIPluginConfig, AppFlowyLocalAI};
|
use appflowy_local_ai::chat_plugin::{AIPluginConfig, AppFlowyLocalAI};
|
||||||
use appflowy_plugin::manager::PluginManager;
|
use appflowy_plugin::manager::PluginManager;
|
||||||
use appflowy_plugin::util::is_apple_silicon;
|
use appflowy_plugin::util::is_apple_silicon;
|
||||||
use flowy_ai_pub::cloud::{AppFlowyOfflineAI, ChatCloudService, LLMModel, LocalAIConfig};
|
use flowy_ai_pub::cloud::{
|
||||||
|
AppFlowyOfflineAI, ChatCloudService, ChatMessageMetadata, ChatMetadataContentType, LLMModel,
|
||||||
|
LocalAIConfig,
|
||||||
|
};
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use flowy_sqlite::kv::KVStorePreferences;
|
use flowy_sqlite::kv::KVStorePreferences;
|
||||||
use futures::Sink;
|
use futures::Sink;
|
||||||
use lib_infra::async_trait::async_trait;
|
use lib_infra::async_trait::async_trait;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::select;
|
use tokio::select;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
@ -173,7 +179,7 @@ impl LocalAIController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_chat(&self, chat_id: &str) {
|
pub fn open_chat(&self, chat_id: &str) {
|
||||||
if !self.is_running() {
|
if !self.is_enabled() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,6 +340,63 @@ impl LocalAIController {
|
|||||||
Ok(enabled)
|
Ok(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn index_message_metadata(
|
||||||
|
&self,
|
||||||
|
chat_id: &str,
|
||||||
|
metadata_list: &[ChatMessageMetadata],
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
for metadata in metadata_list {
|
||||||
|
let mut index_metadata = HashMap::new();
|
||||||
|
index_metadata.insert("name".to_string(), json!(&metadata.name));
|
||||||
|
index_metadata.insert("at_name".to_string(), json!(format!("@{}", &metadata.name)));
|
||||||
|
index_metadata.insert("source".to_string(), json!(&metadata.source));
|
||||||
|
|
||||||
|
match &metadata.data.url {
|
||||||
|
None => match &metadata.data.content_type {
|
||||||
|
ChatMetadataContentType::Text | ChatMetadataContentType::Markdown => {
|
||||||
|
if metadata.data.validate() {
|
||||||
|
if let Err(err) = self
|
||||||
|
.index_file(
|
||||||
|
chat_id,
|
||||||
|
None,
|
||||||
|
Some(metadata.data.content.clone()),
|
||||||
|
Some(index_metadata),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("[AI Plugin] failed to index file: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
error!(
|
||||||
|
"[AI Plugin] unsupported content type: {:?}",
|
||||||
|
metadata.data.content_type
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Some(url) => {
|
||||||
|
let file_path = Path::new(url);
|
||||||
|
if file_path.exists() {
|
||||||
|
if let Err(err) = self
|
||||||
|
.index_file(
|
||||||
|
chat_id,
|
||||||
|
Some(file_path.to_path_buf()),
|
||||||
|
None,
|
||||||
|
Some(index_metadata),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("[AI Plugin] failed to index file: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn enable_chat_plugin(&self, enabled: bool) -> FlowyResult<()> {
|
async fn enable_chat_plugin(&self, enabled: bool) -> FlowyResult<()> {
|
||||||
info!("[AI Plugin] enable chat plugin: {}", enabled);
|
info!("[AI Plugin] enable chat plugin: {}", enabled);
|
||||||
if enabled {
|
if enabled {
|
||||||
|
@ -514,7 +514,7 @@ impl LocalAIResourceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resource_dir(&self) -> FlowyResult<PathBuf> {
|
pub(crate) fn resource_dir(&self) -> FlowyResult<PathBuf> {
|
||||||
let user_data_dir = self.user_service.data_root_dir()?;
|
let user_data_dir = self.user_service.application_root_dir()?;
|
||||||
Ok(user_data_dir.join("ai"))
|
Ok(user_data_dir.join("ai"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::ai_manager::AIUserService;
|
|||||||
use crate::entities::{ChatStatePB, ModelTypePB};
|
use crate::entities::{ChatStatePB, ModelTypePB};
|
||||||
use crate::local_ai::local_llm_chat::LocalAIController;
|
use crate::local_ai::local_llm_chat::LocalAIController;
|
||||||
use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY};
|
use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY};
|
||||||
use crate::persistence::select_single_message;
|
use crate::persistence::{select_single_message, ChatMessageTable};
|
||||||
use appflowy_plugin::error::PluginError;
|
use appflowy_plugin::error::PluginError;
|
||||||
|
|
||||||
use flowy_ai_pub::cloud::{
|
use flowy_ai_pub::cloud::{
|
||||||
@ -16,8 +16,10 @@ use lib_infra::async_trait::async_trait;
|
|||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
use crate::local_ai::stream_util::LocalAIStreamAdaptor;
|
use crate::local_ai::stream_util::LocalAIStreamAdaptor;
|
||||||
|
use serde_json::json;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
pub struct AICloudServiceMiddleware {
|
pub struct AICloudServiceMiddleware {
|
||||||
cloud_service: Arc<dyn ChatCloudService>,
|
cloud_service: Arc<dyn ChatCloudService>,
|
||||||
@ -38,16 +40,30 @@ impl AICloudServiceMiddleware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_message_content(&self, message_id: i64) -> FlowyResult<String> {
|
pub fn is_local_ai_enabled(&self) -> bool {
|
||||||
|
self.local_llm_controller.is_enabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn index_message_metadata(
|
||||||
|
&self,
|
||||||
|
chat_id: &str,
|
||||||
|
metadata_list: &[ChatMessageMetadata],
|
||||||
|
) -> Result<(), FlowyError> {
|
||||||
|
self
|
||||||
|
.local_llm_controller
|
||||||
|
.index_message_metadata(chat_id, metadata_list)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_message_record(&self, message_id: i64) -> FlowyResult<ChatMessageTable> {
|
||||||
let uid = self.user_service.user_id()?;
|
let uid = self.user_service.user_id()?;
|
||||||
let conn = self.user_service.sqlite_connection(uid)?;
|
let conn = self.user_service.sqlite_connection(uid)?;
|
||||||
let content = select_single_message(conn, message_id)?
|
let row = select_single_message(conn, message_id)?.ok_or_else(|| {
|
||||||
.map(|data| data.content)
|
FlowyError::record_not_found().with_context(format!("Message not found: {}", message_id))
|
||||||
.ok_or_else(|| {
|
})?;
|
||||||
FlowyError::record_not_found().with_context(format!("Message not found: {}", message_id))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(content)
|
Ok(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_plugin_error(&self, err: PluginError) {
|
fn handle_plugin_error(&self, err: PluginError) {
|
||||||
@ -79,17 +95,18 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
self.cloud_service.create_chat(uid, workspace_id, chat_id)
|
self.cloud_service.create_chat(uid, workspace_id, chat_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_question(
|
async fn create_question(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
chat_id: &str,
|
chat_id: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
message_type: ChatMessageType,
|
message_type: ChatMessageType,
|
||||||
metadata: Vec<ChatMessageMetadata>,
|
metadata: &[ChatMessageMetadata],
|
||||||
) -> FutureResult<ChatMessage, FlowyError> {
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
self
|
self
|
||||||
.cloud_service
|
.cloud_service
|
||||||
.create_question(workspace_id, chat_id, message, message_type, metadata)
|
.create_question(workspace_id, chat_id, message, message_type, metadata)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_answer(
|
fn create_answer(
|
||||||
@ -109,13 +126,13 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
chat_id: &str,
|
chat_id: &str,
|
||||||
message_id: i64,
|
question_id: i64,
|
||||||
) -> Result<StreamAnswer, FlowyError> {
|
) -> Result<StreamAnswer, FlowyError> {
|
||||||
if self.local_llm_controller.is_running() {
|
if self.local_llm_controller.is_running() {
|
||||||
let content = self.get_message_content(message_id)?;
|
let row = self.get_message_record(question_id)?;
|
||||||
match self
|
match self
|
||||||
.local_llm_controller
|
.local_llm_controller
|
||||||
.stream_question(chat_id, &content)
|
.stream_question(chat_id, &row.content, json!([]))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(stream) => Ok(LocalAIStreamAdaptor::new(stream).boxed()),
|
Ok(stream) => Ok(LocalAIStreamAdaptor::new(stream).boxed()),
|
||||||
@ -127,7 +144,7 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
.cloud_service
|
.cloud_service
|
||||||
.stream_answer(workspace_id, chat_id, message_id)
|
.stream_answer(workspace_id, chat_id, question_id)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,7 +156,7 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
question_message_id: i64,
|
question_message_id: i64,
|
||||||
) -> Result<ChatMessage, FlowyError> {
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
if self.local_llm_controller.is_running() {
|
if self.local_llm_controller.is_running() {
|
||||||
let content = self.get_message_content(question_message_id)?;
|
let content = self.get_message_record(question_message_id)?.content;
|
||||||
match self
|
match self
|
||||||
.local_llm_controller
|
.local_llm_controller
|
||||||
.ask_question(chat_id, &content)
|
.ask_question(chat_id, &content)
|
||||||
@ -189,7 +206,10 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
.local_llm_controller
|
.local_llm_controller
|
||||||
.get_related_question(chat_id)
|
.get_related_question(chat_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| FlowyError::local_ai().with_context(err))?
|
.map_err(|err| FlowyError::local_ai().with_context(err))?;
|
||||||
|
trace!("LocalAI related questions: {:?}", questions);
|
||||||
|
|
||||||
|
let items = questions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|content| RelatedQuestion {
|
.map(|content| RelatedQuestion {
|
||||||
content,
|
content,
|
||||||
@ -197,10 +217,7 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(RepeatedRelatedQuestion {
|
Ok(RepeatedRelatedQuestion { message_id, items })
|
||||||
message_id,
|
|
||||||
items: questions,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
.cloud_service
|
.cloud_service
|
||||||
@ -248,7 +265,7 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||||||
if self.local_llm_controller.is_running() {
|
if self.local_llm_controller.is_running() {
|
||||||
self
|
self
|
||||||
.local_llm_controller
|
.local_llm_controller
|
||||||
.index_file(chat_id, file_path.to_path_buf())
|
.index_file(chat_id, Some(file_path.to_path_buf()), None, None)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| FlowyError::local_ai().with_context(err))?;
|
.map_err(|err| FlowyError::local_ai().with_context(err))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -52,7 +52,7 @@ impl AIUserService for ChatUserServiceImpl {
|
|||||||
self.upgrade_user()?.get_sqlite_connection(uid)
|
self.upgrade_user()?.get_sqlite_connection(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_root_dir(&self) -> Result<PathBuf, FlowyError> {
|
fn application_root_dir(&self) -> Result<PathBuf, FlowyError> {
|
||||||
Ok(PathBuf::from(
|
Ok(PathBuf::from(
|
||||||
self.upgrade_user()?.get_application_root_dir(),
|
self.upgrade_user()?.get_application_root_dir(),
|
||||||
))
|
))
|
||||||
|
@ -611,25 +611,22 @@ impl ChatCloudService for ServerProvider {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_question(
|
async fn create_question(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
chat_id: &str,
|
chat_id: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
message_type: ChatMessageType,
|
message_type: ChatMessageType,
|
||||||
metadata: Vec<ChatMessageMetadata>,
|
metadata: &[ChatMessageMetadata],
|
||||||
) -> FutureResult<ChatMessage, FlowyError> {
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
let workspace_id = workspace_id.to_string();
|
let workspace_id = workspace_id.to_string();
|
||||||
let chat_id = chat_id.to_string();
|
let chat_id = chat_id.to_string();
|
||||||
let message = message.to_string();
|
let message = message.to_string();
|
||||||
let server = self.get_server();
|
self
|
||||||
|
.get_server()?
|
||||||
FutureResult::new(async move {
|
.chat_service()
|
||||||
server?
|
.create_question(&workspace_id, &chat_id, &message, message_type, metadata)
|
||||||
.chat_service()
|
.await
|
||||||
.create_question(&workspace_id, &chat_id, &message, message_type, metadata)
|
|
||||||
.await
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_answer(
|
fn create_answer(
|
||||||
|
@ -52,14 +52,14 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_question(
|
async fn create_question(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
chat_id: &str,
|
chat_id: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
message_type: ChatMessageType,
|
message_type: ChatMessageType,
|
||||||
metadata: Vec<ChatMessageMetadata>,
|
metadata: &[ChatMessageMetadata],
|
||||||
) -> FutureResult<ChatMessage, FlowyError> {
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
let workspace_id = workspace_id.to_string();
|
let workspace_id = workspace_id.to_string();
|
||||||
let chat_id = chat_id.to_string();
|
let chat_id = chat_id.to_string();
|
||||||
let try_get_client = self.inner.try_get_client();
|
let try_get_client = self.inner.try_get_client();
|
||||||
@ -69,13 +69,11 @@ where
|
|||||||
metadata: Some(json!(metadata)),
|
metadata: Some(json!(metadata)),
|
||||||
};
|
};
|
||||||
|
|
||||||
FutureResult::new(async move {
|
let message = try_get_client?
|
||||||
let message = try_get_client?
|
.create_question(&workspace_id, &chat_id, params)
|
||||||
.create_question(&workspace_id, &chat_id, params)
|
.await
|
||||||
.await
|
.map_err(FlowyError::from)?;
|
||||||
.map_err(FlowyError::from)?;
|
Ok(message)
|
||||||
Ok(message)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_answer(
|
fn create_answer(
|
||||||
|
@ -23,17 +23,15 @@ impl ChatCloudService for DefaultChatCloudServiceImpl {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_question(
|
async fn create_question(
|
||||||
&self,
|
&self,
|
||||||
_workspace_id: &str,
|
_workspace_id: &str,
|
||||||
_chat_id: &str,
|
_chat_id: &str,
|
||||||
_message: &str,
|
_message: &str,
|
||||||
_message_type: ChatMessageType,
|
_message_type: ChatMessageType,
|
||||||
_metadata: Vec<ChatMessageMetadata>,
|
_metadata: &[ChatMessageMetadata],
|
||||||
) -> FutureResult<ChatMessage, FlowyError> {
|
) -> Result<ChatMessage, FlowyError> {
|
||||||
FutureResult::new(async move {
|
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."))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_answer(
|
fn create_answer(
|
||||||
|
Loading…
Reference in New Issue
Block a user