diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.project b/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.project
index 77aded223a..4141b13cf1 100644
--- a/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.project
+++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.project
@@ -22,12 +22,12 @@
- 1626576261667
+ 1693395487121
30
org.eclipse.core.resources.regexFilterMatcher
- node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+ node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.settings/org.eclipse.buildship.core.prefs b/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.settings/org.eclipse.buildship.core.prefs
index e8895216fd..d76c0b7ac1 100644
--- a/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.settings/org.eclipse.buildship.core.prefs
+++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/android/.settings/org.eclipse.buildship.core.prefs
@@ -1,2 +1,13 @@
+arguments=--init-script /var/folders/th/tfqrqcp12kvgzs3c3z0xqxlc0000gn/T/d146c9752a26f79b52047fb6dc6ed385d064e120494f96f08ca63a317c41f94c.gradle --init-script /var/folders/th/tfqrqcp12kvgzs3c3z0xqxlc0000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
+auto.sync=false
+build.scans.enabled=false
+connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.4.2))
connection.project.dir=
eclipse.preferences.version=1
+gradle.user.home=
+java.home=/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
+jvm.arguments=
+offline.mode=false
+override.workspace.settings=true
+show.console.view=true
+show.executions.view=true
diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/example/android/app/.settings/org.eclipse.buildship.core.prefs b/frontend/appflowy_flutter/packages/flowy_infra_ui/example/android/app/.settings/org.eclipse.buildship.core.prefs
index b1886adb46..25e4212285 100644
--- a/frontend/appflowy_flutter/packages/flowy_infra_ui/example/android/app/.settings/org.eclipse.buildship.core.prefs
+++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/example/android/app/.settings/org.eclipse.buildship.core.prefs
@@ -1,2 +1,13 @@
-connection.project.dir=..
+arguments=
+auto.sync=false
+build.scans.enabled=false
+connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
+connection.project.dir=
eclipse.preferences.version=1
+gradle.user.home=
+java.home=/Library/Java/JavaVirtualMachines/jdk11.0.5-zulu.jdk/Contents/Home
+jvm.arguments=
+offline.mode=false
+override.workspace.settings=true
+show.console.view=true
+show.executions.view=true
diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock
index baf7aac402..4d4b640f12 100644
--- a/frontend/appflowy_tauri/src-tauri/Cargo.lock
+++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock
@@ -1569,6 +1569,23 @@ dependencies = [
"miniz_oxide 0.7.1",
]
+[[package]]
+name = "flowy-ai"
+version = "0.1.0"
+dependencies = [
+ "bytes",
+ "flowy-derive",
+ "flowy-error",
+ "flowy-notification",
+ "lib-dispatch",
+ "lib-infra",
+ "protobuf",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "strum_macros 0.21.1",
+]
+
[[package]]
name = "flowy-ast"
version = "0.1.0"
@@ -1627,6 +1644,7 @@ dependencies = [
"collab-integrate",
"collab-plugins",
"diesel",
+ "flowy-ai",
"flowy-config",
"flowy-database-deps",
"flowy-database2",
diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock
index c7d09a00cd..dcd4bd295a 100644
--- a/frontend/rust-lib/Cargo.lock
+++ b/frontend/rust-lib/Cargo.lock
@@ -1317,6 +1317,23 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "flowy-ai"
+version = "0.1.0"
+dependencies = [
+ "bytes",
+ "flowy-derive",
+ "flowy-error",
+ "flowy-notification",
+ "lib-dispatch",
+ "lib-infra",
+ "protobuf",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "strum_macros 0.21.1",
+]
+
[[package]]
name = "flowy-ast"
version = "0.1.0"
@@ -1376,6 +1393,7 @@ dependencies = [
"collab-plugins",
"console-subscriber",
"diesel",
+ "flowy-ai",
"flowy-config",
"flowy-database-deps",
"flowy-database2",
diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml
index cdfd1aeb67..c7c2a9b951 100644
--- a/frontend/rust-lib/Cargo.toml
+++ b/frontend/rust-lib/Cargo.toml
@@ -23,6 +23,7 @@ members = [
"flowy-encrypt",
"flowy-storage",
"collab-integrate",
+ "flowy-ai",
]
[workspace.dependencies]
@@ -48,6 +49,7 @@ flowy-config = { workspace = true, path = "flowy-config" }
flowy-encrypt = { workspace = true, path = "flowy-encrypt" }
flowy-storage = { workspace = true, path = "flowy-storage" }
collab-integrate = { workspace = true, path = "collab-integrate" }
+flowy-ai = { workspace = true, path = "flowy-ai" }
[profile.dev]
opt-level = 0
diff --git a/frontend/rust-lib/flowy-ai/Cargo.toml b/frontend/rust-lib/flowy-ai/Cargo.toml
new file mode 100644
index 0000000000..5d44a3fbdd
--- /dev/null
+++ b/frontend/rust-lib/flowy-ai/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "flowy-ai"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+flowy-derive = { path = "../../../shared-lib/flowy-derive" }
+flowy-notification = { path = "../flowy-notification" }
+flowy-error = { path = "../flowy-error", features = ["impl_from_serde", "impl_from_dispatch_error"] }
+lib-dispatch = { path = "../lib-dispatch" }
+lib-infra = { path = "../../../shared-lib/lib-infra" }
+
+protobuf = {version = "2.28.0"}
+bytes = { version = "1.4" }
+strum_macros = "0.21"
+reqwest = { version = "0.11", features = ["json"] }
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
diff --git a/frontend/rust-lib/flowy-ai/src/entities.rs b/frontend/rust-lib/flowy-ai/src/entities.rs
new file mode 100644
index 0000000000..bc42019650
--- /dev/null
+++ b/frontend/rust-lib/flowy-ai/src/entities.rs
@@ -0,0 +1,71 @@
+use flowy_error::ErrorCode;
+
+/*
+ model="text-davinci-003",
+ prompt="Write a tagline for an ice cream shop."
+*/
+#[derive(Default)]
+pub struct TextCompletionPayloadPB {
+ pub request_id: String,
+
+ // Model: Either text-davinci-003 or gpt-3.5-turbo
+ pub model: String,
+
+ // Prompt to query gpt
+ pub prompt: String,
+
+ // User open_ai_key for authentication
+ pub open_ai_key: String,
+}
+
+pub struct TextCompletionParams {
+ pub request_id: String,
+ pub model: String,
+ pub prompt: String,
+ pub open_ai_key: String,
+}
+
+impl TryInto for TextCompletionPayloadPB {
+ type Error = ErrorCode;
+ fn try_into(self) -> Result {
+ Ok(TextCompletionParams {
+ request_id: self.request_id,
+ model: self.model,
+ prompt: self.prompt,
+ open_ai_key: self.open_ai_key,
+ })
+ }
+}
+
+/*
+{
+ "id": "chatcmpl-123",
+ "object": "chat.completion",
+ "created": 1677652288,
+ "model": "gpt-3.5-turbo-0613",
+ "choices": [{
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "\n\nHello there, how may I assist you today?",
+ },
+ "finish_reason": "stop"
+ }],
+ "usage": {
+ "prompt_tokens": 9,
+ "completion_tokens": 12,
+ "total_tokens": 21
+ }
+}
+
+*/
+#[derive(Default)]
+pub struct TextCompletionDataPB {
+ pub request_id: String,
+
+ pub model: String,
+
+ pub index: i32,
+
+ pub content: String,
+}
diff --git a/frontend/rust-lib/flowy-ai/src/event_handler.rs b/frontend/rust-lib/flowy-ai/src/event_handler.rs
new file mode 100644
index 0000000000..ac2b83bcff
--- /dev/null
+++ b/frontend/rust-lib/flowy-ai/src/event_handler.rs
@@ -0,0 +1,71 @@
+use crate::entities::{TextCompletionDataPB, TextCompletionParams, TextCompletionPayloadPB};
+use flowy_error::FlowyError;
+use lib_dispatch::prelude::{data_result_ok, AFPluginData, DataResult};
+use reqwest;
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize)]
+struct Message {
+ role: String,
+ content: String,
+}
+
+#[derive(Serialize)]
+struct RequestBody {
+ model: String,
+ messages: Vec,
+}
+
+#[derive(Deserialize)]
+struct ResponseChoice {
+ index: i32,
+ message: Message,
+}
+
+#[derive(Deserialize)]
+struct ApiResponse {
+ choices: Vec,
+}
+
+pub(crate) async fn request_text_completion(
+ data: AFPluginData,
+) -> DataResult {
+ // Set up the request body
+ let body = RequestBody {
+ model: "gpt-3.5-turbo".to_string(),
+ messages: vec![
+ Message {
+ role: "system".to_string(),
+ content: "You are a helpful assistant.".to_string(),
+ },
+ Message {
+ role: "user".to_string(),
+ content: data.prompt.to_string(),
+ },
+ ],
+ };
+
+ // Make the API call
+ let client = reqwest::Client::new();
+ let response: ApiResponse = client
+ .post("https://api.openai.com/v1/chat/completions")
+ .header("Content-Type", "application/json")
+ .header("Authorization", format!("Bearer {}", data.open_ai_key))
+ .json(&body)
+ .send()
+ .await?
+ .json()
+ .await?;
+
+ // Extract index and content
+ let _choice = &response.choices[0];
+
+ let params: TextCompletionParams = data.into_inner().try_into()?;
+
+ data_result_ok(TextCompletionDataPB {
+ request_id: params.request_id,
+ model: params.model,
+ index: response.choices[0].index,
+ content: response.choices[0].message.content.to_string(),
+ })
+}
diff --git a/frontend/rust-lib/flowy-ai/src/event_map.rs b/frontend/rust-lib/flowy-ai/src/event_map.rs
new file mode 100644
index 0000000000..52d1f34df2
--- /dev/null
+++ b/frontend/rust-lib/flowy-ai/src/event_map.rs
@@ -0,0 +1,18 @@
+use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
+use lib_dispatch::prelude::AFPlugin;
+use strum_macros::Display;
+
+use crate::event_handler::request_text_completion;
+
+pub fn init() -> AFPlugin {
+ AFPlugin::new()
+ .name(env!("CARGO_PKG_NAME"))
+ .event(OpenAIEvent::RequestTextCompletion, request_text_completion)
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Display, ProtoBuf_Enum, Flowy_Event)]
+#[event_err = "FlowyError"]
+pub enum OpenAIEvent {
+ #[event(input = "TextCompletionPayloadPB", output = "TextCompletionDataPB")]
+ RequestTextCompletion = 0,
+}
diff --git a/frontend/rust-lib/flowy-ai/src/lib.rs b/frontend/rust-lib/flowy-ai/src/lib.rs
new file mode 100644
index 0000000000..7446a897b9
--- /dev/null
+++ b/frontend/rust-lib/flowy-ai/src/lib.rs
@@ -0,0 +1,4 @@
+// pub mod entities;
+// pub mod event_handler;
+// pub mod event_map;
+// pub mod notification;
diff --git a/frontend/rust-lib/flowy-ai/src/notification.rs b/frontend/rust-lib/flowy-ai/src/notification.rs
new file mode 100644
index 0000000000..48bf159846
--- /dev/null
+++ b/frontend/rust-lib/flowy-ai/src/notification.rs
@@ -0,0 +1,21 @@
+use flowy_derive::ProtoBuf_Enum;
+use flowy_notification::NotificationBuilder;
+
+const OPEN_AI_NOTIFICATION: &str = "OpenAI";
+
+#[derive(ProtoBuf_Enum, Debug, Default)]
+pub(crate) enum OpenAINotification {
+ #[default]
+ Unknown = 0,
+}
+
+impl std::convert::From for i32 {
+ fn from(notification: OpenAINotification) -> Self {
+ notification as i32
+ }
+}
+
+#[allow(dead_code)]
+pub(crate) fn send_notification(id: &str, ty: OpenAINotification) -> NotificationBuilder {
+ NotificationBuilder::new(id, ty, OPEN_AI_NOTIFICATION)
+}
diff --git a/frontend/rust-lib/flowy-core/Cargo.toml b/frontend/rust-lib/flowy-core/Cargo.toml
index 3fb78e45c2..e9f7951540 100644
--- a/frontend/rust-lib/flowy-core/Cargo.toml
+++ b/frontend/rust-lib/flowy-core/Cargo.toml
@@ -23,6 +23,7 @@ flowy-server = { workspace = true }
flowy-server-config = { workspace = true }
flowy-config = { workspace = true }
collab-integrate = { workspace = true, features = ["supabase_integrate", "appflowy_cloud_integrate", "snapshot_plugin"] }
+flowy-ai = { workspace = true }
collab-define = { version = "0.1.0" }
collab-plugins = { version = "0.1.0", features = ["sync_plugin"] }
collab = { version = "0.1.0" }
diff --git a/frontend/rust-lib/flowy-user/src/migrations/sync_new_user.rs b/frontend/rust-lib/flowy-user/src/migrations/sync_new_user.rs
index 5dc69cf94d..3c1092dd2f 100644
--- a/frontend/rust-lib/flowy-user/src/migrations/sync_new_user.rs
+++ b/frontend/rust-lib/flowy-user/src/migrations/sync_new_user.rs
@@ -70,6 +70,7 @@ pub async fn sync_user_data_to_cloud(
Ok(())
}
+#[allow(clippy::too_many_arguments)]
fn sync_views(
uid: i64,
folder: Arc,