chore: select message

This commit is contained in:
Nathan 2025-04-17 14:07:01 +08:00
parent 98b835227e
commit af91a72187
19 changed files with 276 additions and 102 deletions

View File

@ -2564,6 +2564,7 @@ version = "0.1.0"
dependencies = [
"client-api",
"flowy-error",
"flowy-sqlite",
"futures",
"lib-infra",
"serde",

View File

@ -12,4 +12,5 @@ client-api = { workspace = true }
futures.workspace = true
serde_json.workspace = true
serde.workspace = true
uuid.workspace = true
uuid.workspace = true
flowy-sqlite = { workspace = true }

View File

@ -85,6 +85,8 @@ pub trait ChatCloudService: Send + Sync + 'static {
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
name: &str,
metadata: serde_json::Value,
) -> Result<(), FlowyError>;
async fn create_question(

View File

@ -1 +1,2 @@
pub mod cloud;
pub mod persistence;

View File

@ -1,3 +1,4 @@
use crate::cloud::MessageCursor;
use flowy_error::{FlowyError, FlowyResult};
use flowy_sqlite::upsert::excluded;
use flowy_sqlite::{
@ -51,19 +52,25 @@ pub fn select_chat_messages(
mut conn: DBConnection,
chat_id_val: &str,
limit_val: i64,
after_message_id: Option<i64>,
before_message_id: Option<i64>,
offset: MessageCursor,
) -> QueryResult<Vec<ChatMessageTable>> {
let mut query = dsl::chat_message_table
.filter(chat_message_table::chat_id.eq(chat_id_val))
.into_boxed();
if let Some(after_message_id) = after_message_id {
query = query.filter(chat_message_table::message_id.gt(after_message_id));
match offset {
MessageCursor::AfterMessageId(after_message_id) => {
query = query.filter(chat_message_table::message_id.gt(after_message_id));
},
MessageCursor::BeforeMessageId(before_message_id) => {
query = query.filter(chat_message_table::message_id.lt(before_message_id));
},
MessageCursor::Offset(offset_val) => {
query = query.offset(offset_val as i64);
},
MessageCursor::NextBack => {},
}
if let Some(before_message_id) = before_message_id {
query = query.filter(chat_message_table::message_id.lt(before_message_id));
}
query = query
.order((
chat_message_table::created_at.desc(),
@ -75,7 +82,7 @@ pub fn select_chat_messages(
Ok(messages)
}
pub fn select_single_message(
pub fn select_message(
mut conn: DBConnection,
message_id_val: i64,
) -> QueryResult<Option<ChatMessageTable>> {
@ -86,6 +93,18 @@ pub fn select_single_message(
Ok(message)
}
pub fn select_message_content(
mut conn: DBConnection,
message_id_val: i64,
) -> QueryResult<Option<String>> {
let message = dsl::chat_message_table
.filter(chat_message_table::message_id.eq(message_id_val))
.select(chat_message_table::content)
.first::<String>(&mut *conn)
.optional()?;
Ok(message)
}
pub fn select_message_where_match_reply_message_id(
mut conn: DBConnection,
answer_message_id_val: i64,

View File

@ -16,10 +16,8 @@ pub struct ChatTable {
pub chat_id: String,
pub created_at: i64,
pub name: String,
pub local_files: String,
pub metadata: String,
pub local_enabled: bool,
pub sync_to_cloud: bool,
pub rag_ids: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
@ -49,22 +47,57 @@ pub struct ChatTableFile {
pub struct ChatTableChangeset {
pub chat_id: String,
pub name: Option<String>,
pub local_files: Option<String>,
pub metadata: Option<String>,
pub local_enabled: Option<bool>,
pub sync_to_cloud: Option<bool>,
pub rag_ids: Option<String>,
}
impl ChatTableChangeset {
pub fn from_metadata(metadata: ChatTableMetadata) -> Self {
ChatTableChangeset {
chat_id: Default::default(),
metadata: serde_json::to_string(&metadata).ok(),
..Default::default()
name: None,
rag_ids: None,
}
}
pub fn from_rag_ids(rag_ids: Vec<String>) -> Self {
ChatTableChangeset {
chat_id: Default::default(),
// Serialize the Vec<String> to a JSON array string
rag_ids: Some(serde_json::to_string(&rag_ids).unwrap_or_default()),
name: None,
metadata: None,
}
}
}
pub fn insert_chat(mut conn: DBConnection, new_chat: &ChatTable) -> QueryResult<usize> {
pub fn serialize_rag_ids(rag_ids: &[String]) -> String {
serde_json::to_string(rag_ids).unwrap_or_default()
}
pub fn deserialize_rag_ids(rag_ids_str: &Option<String>) -> Vec<String> {
match rag_ids_str {
Some(str) => serde_json::from_str(str).unwrap_or_default(),
None => Vec::new(),
}
}
pub fn deserialize_chat_metadata<T>(metadata: &str) -> T
where
T: serde::de::DeserializeOwned + Default,
{
serde_json::from_str(metadata).unwrap_or_default()
}
pub fn serialize_chat_metadata<T>(metadata: &T) -> String
where
T: Serialize,
{
serde_json::to_string(metadata).unwrap_or_default()
}
pub fn upsert_chat(mut conn: DBConnection, new_chat: &ChatTable) -> QueryResult<usize> {
diesel::insert_into(chat_table::table)
.values(new_chat)
.on_conflict(chat_table::chat_id)
@ -72,6 +105,8 @@ pub fn insert_chat(mut conn: DBConnection, new_chat: &ChatTable) -> QueryResult<
.set((
chat_table::created_at.eq(excluded(chat_table::created_at)),
chat_table::name.eq(excluded(chat_table::name)),
chat_table::metadata.eq(excluded(chat_table::metadata)),
chat_table::rag_ids.eq(excluded(chat_table::rag_ids)),
))
.execute(&mut *conn)
}
@ -85,7 +120,6 @@ pub fn update_chat(
Ok(affected_row)
}
#[allow(dead_code)]
pub fn read_chat(mut conn: DBConnection, chat_id_val: &str) -> QueryResult<ChatTable> {
let row = dsl::chat_table
.filter(chat_table::chat_id.eq(chat_id_val))
@ -93,7 +127,17 @@ pub fn read_chat(mut conn: DBConnection, chat_id_val: &str) -> QueryResult<ChatT
Ok(row)
}
#[allow(dead_code)]
pub fn read_chat_rag_ids(
conn: &mut SqliteConnection,
chat_id_val: &str,
) -> FlowyResult<Vec<String>> {
let chat = dsl::chat_table
.filter(chat_table::chat_id.eq(chat_id_val))
.first::<ChatTable>(conn)?;
Ok(deserialize_rag_ids(&chat.rag_ids))
}
pub fn read_chat_metadata(
conn: &mut SqliteConnection,
chat_id_val: &str,
@ -102,8 +146,7 @@ pub fn read_chat_metadata(
.select(chat_table::metadata)
.filter(chat_table::chat_id.eq(chat_id_val))
.first::<String>(&mut *conn)?;
let value = serde_json::from_str(&metadata_str).unwrap_or_default();
Ok(value)
Ok(deserialize_chat_metadata(&metadata_str))
}
#[allow(dead_code)]

View File

@ -5,7 +5,9 @@ use crate::entities::{
};
use crate::local_ai::controller::{LocalAIController, LocalAISetting};
use crate::middleware::chat_service_mw::AICloudServiceMiddleware;
use crate::persistence::{insert_chat, read_chat_metadata, ChatTable};
use flowy_ai_pub::persistence::{
read_chat_metadata, serialize_chat_metadata, serialize_rag_ids, upsert_chat, ChatTable,
};
use std::collections::HashMap;
use dashmap::DashMap;
@ -25,6 +27,7 @@ use flowy_ai_pub::cloud::ai_dto::AvailableModel;
use flowy_storage_pub::storage::StorageService;
use lib_infra::async_trait::async_trait;
use lib_infra::util::timestamp;
use serde_json::json;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Weak};
@ -221,11 +224,17 @@ impl AIManager {
.unwrap_or_default();
info!("[Chat] create chat with rag_ids: {:?}", rag_ids);
save_chat(
self.user_service.sqlite_connection(*uid)?,
chat_id,
"",
rag_ids.iter().map(|v| v.to_string()).collect(),
json!({}),
)?;
self
.cloud_service_wm
.create_chat(uid, &workspace_id, chat_id, rag_ids)
.create_chat(uid, &workspace_id, chat_id, rag_ids, "", json!({}))
.await?;
save_chat(self.user_service.sqlite_connection(*uid)?, chat_id)?;
let chat = Arc::new(Chat::new(
self.user_service.user_id()?,
@ -705,18 +714,22 @@ async fn sync_chat_documents(
Ok(())
}
fn save_chat(conn: DBConnection, chat_id: &Uuid) -> FlowyResult<()> {
fn save_chat(
conn: DBConnection,
chat_id: &Uuid,
name: &str,
rag_ids: Vec<String>,
metadata: serde_json::Value,
) -> FlowyResult<()> {
let row = ChatTable {
chat_id: chat_id.to_string(),
created_at: timestamp(),
name: "".to_string(),
local_files: "".to_string(),
metadata: "".to_string(),
local_enabled: false,
sync_to_cloud: false,
name: name.to_string(),
metadata: serialize_chat_metadata(&metadata),
rag_ids: Some(serialize_rag_ids(&rag_ids)),
};
insert_chat(conn, &row)?;
upsert_chat(conn, &row)?;
Ok(())
}

View File

@ -5,15 +5,15 @@ use crate::entities::{
};
use crate::middleware::chat_service_mw::AICloudServiceMiddleware;
use crate::notification::{chat_notification_builder, ChatNotification};
use crate::persistence::{
insert_chat_messages, select_chat_messages, select_message_where_match_reply_message_id,
ChatMessageTable,
};
use crate::stream_message::StreamMessage;
use allo_isolate::Isolate;
use flowy_ai_pub::cloud::{
AIModel, ChatCloudService, ChatMessage, MessageCursor, QuestionStreamValue, ResponseFormat,
};
use flowy_ai_pub::persistence::{
insert_chat_messages, select_chat_messages, select_message_where_match_reply_message_id,
ChatMessageTable,
};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_sqlite::DBConnection;
use futures::{SinkExt, StreamExt};
@ -349,9 +349,9 @@ impl Chat {
limit,
before_message_id
);
let messages = self
.load_local_chat_messages(limit, None, before_message_id)
.await?;
let offset = before_message_id.map_or(MessageCursor::NextBack, MessageCursor::BeforeMessageId);
let messages = self.load_local_chat_messages(limit, offset).await?;
// If the number of messages equals the limit, then no need to load more messages from remote
if messages.len() == limit as usize {
@ -397,9 +397,8 @@ impl Chat {
limit,
after_message_id,
);
let messages = self
.load_local_chat_messages(limit, after_message_id, None)
.await?;
let offset = after_message_id.map_or(MessageCursor::NextBack, MessageCursor::AfterMessageId);
let messages = self.load_local_chat_messages(limit, offset).await?;
trace!(
"[Chat] Loaded local chat messages: chat_id={}, messages={}",
@ -562,17 +561,10 @@ impl Chat {
async fn load_local_chat_messages(
&self,
limit: i64,
after_message_id: Option<i64>,
before_message_id: Option<i64>,
offset: MessageCursor,
) -> Result<Vec<ChatMessagePB>, FlowyError> {
let conn = self.user_service.sqlite_connection(self.uid)?;
let records = select_chat_messages(
conn,
&self.chat_id.to_string(),
limit,
after_message_id,
before_message_id,
)?;
let records = select_chat_messages(conn, &self.chat_id.to_string(), limit, offset)?;
let messages = records
.into_iter()
.map(|record| ChatMessagePB {

View File

@ -12,7 +12,6 @@ pub mod local_ai;
mod middleware;
pub mod notification;
mod persistence;
mod protobuf;
mod stream_message;
mod util;

View File

@ -4,8 +4,8 @@ use crate::local_ai::controller::LocalAIController;
use crate::notification::{
chat_notification_builder, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY,
};
use crate::persistence::{select_single_message, ChatMessageTable};
use af_plugin::error::PluginError;
use flowy_ai_pub::persistence::{select_message, select_message_content, ChatMessageTable};
use std::collections::HashMap;
use flowy_ai_pub::cloud::{
@ -78,14 +78,13 @@ impl AICloudServiceMiddleware {
Ok(())
}
fn get_message_record(&self, message_id: i64) -> FlowyResult<ChatMessageTable> {
fn get_message_content(&self, message_id: i64) -> FlowyResult<String> {
let uid = self.user_service.user_id()?;
let conn = self.user_service.sqlite_connection(uid)?;
let row = select_single_message(conn, message_id)?.ok_or_else(|| {
let content = select_message_content(conn, message_id)?.ok_or_else(|| {
FlowyError::record_not_found().with_context(format!("Message not found: {}", message_id))
})?;
Ok(row)
Ok(content)
}
fn handle_plugin_error(&self, err: PluginError) {
@ -114,10 +113,12 @@ impl ChatCloudService for AICloudServiceMiddleware {
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
name: &str,
metadata: serde_json::Value,
) -> Result<(), FlowyError> {
self
.cloud_service
.create_chat(uid, workspace_id, chat_id, rag_ids)
.create_chat(uid, workspace_id, chat_id, rag_ids, name, metadata)
.await
}
@ -165,12 +166,12 @@ impl ChatCloudService for AICloudServiceMiddleware {
info!("stream_answer use model: {:?}", ai_model);
if use_local_ai {
if self.local_ai.is_running() {
let row = self.get_message_record(message_id)?;
let content = self.get_message_content(message_id)?;
match self
.local_ai
.stream_question(
&chat_id.to_string(),
&row.content,
&content,
Some(json!(format)),
json!({}),
)
@ -202,7 +203,7 @@ impl ChatCloudService for AICloudServiceMiddleware {
question_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
if self.local_ai.is_running() {
let content = self.get_message_record(question_message_id)?.content;
let content = self.get_message_content(question_message_id)?;
match self
.local_ai
.ask_question(&chat_id.to_string(), &content)

View File

@ -667,11 +667,13 @@ impl ChatCloudService for ServerProvider {
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
name: &str,
metadata: serde_json::Value,
) -> Result<(), FlowyError> {
let server = self.get_server();
server?
.chat_service()
.create_chat(uid, workspace_id, chat_id, rag_ids)
.create_chat(uid, workspace_id, chat_id, rag_ids, name, metadata)
.await
}

View File

@ -128,7 +128,7 @@ impl ServerProvider {
let server = match server_type {
Server::Local => {
let server = Arc::new(LocalServer::new(self.user.clone()));
let server = Arc::new(LocalServer::new(self.user.clone(), self.local_ai.clone()));
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)
},
Server::AppFlowyCloud => {

View File

@ -35,12 +35,14 @@ where
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
name: &str,
metadata: serde_json::Value,
) -> Result<(), FlowyError> {
let chat_id = chat_id.to_string();
let try_get_client = self.inner.try_get_client();
let params = CreateChatParams {
chat_id,
name: "".to_string(),
name: name.to_string(),
rag_ids,
};
try_get_client?

View File

@ -1,14 +1,22 @@
use crate::af_cloud::define::ServerUser;
use client_api::entity::ai_dto::RepeatedRelatedQuestion;
use flowy_ai::local_ai::controller::LocalAIController;
use flowy_ai::local_ai::stream_util::QuestionStream;
use flowy_ai_pub::cloud::{
AIModel, ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings,
CompleteTextParams, MessageCursor, ModelList, RepeatedChatMessage, ResponseFormat, StreamAnswer,
StreamComplete, SubscriptionPlan, UpdateChatParams,
};
use flowy_error::FlowyError;
use flowy_ai_pub::persistence::{
deserialize_chat_metadata, deserialize_rag_ids, read_chat, select_message_content,
serialize_chat_metadata, serialize_rag_ids, update_chat, upsert_chat, ChatTable,
ChatTableChangeset,
};
use flowy_error::{FlowyError, FlowyResult};
use futures_util::{stream, FutureExt, StreamExt};
use lib_infra::async_trait::async_trait;
use lib_infra::util::timestamp;
use serde_json::Value;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
@ -16,6 +24,18 @@ use uuid::Uuid;
pub struct LocalServerChatServiceImpl {
pub user: Arc<dyn ServerUser>,
pub local_ai: Arc<LocalAIController>,
}
impl LocalServerChatServiceImpl {
fn get_message_content(&self, message_id: i64) -> FlowyResult<String> {
let uid = self.user.user_id()?;
let db = self.user.get_sqlite_db(uid)?;
let content = select_message_content(db, message_id)?.ok_or_else(|| {
FlowyError::record_not_found().with_context(format!("Message not found: {}", message_id))
})?;
Ok(content)
}
}
#[async_trait]
@ -24,9 +44,28 @@ impl ChatCloudService for LocalServerChatServiceImpl {
&self,
_uid: &i64,
_workspace_id: &Uuid,
_chat_id: &Uuid,
_rag_ids: Vec<Uuid>,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
name: &str,
metadata: Value,
) -> Result<(), FlowyError> {
let uid = self.user.user_id()?;
let db = self.user.get_sqlite_db(uid)?;
let rag_ids = rag_ids
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>();
let row = ChatTable {
chat_id: chat_id.to_string(),
created_at: timestamp(),
name: name.to_string(),
metadata: serialize_chat_metadata(&metadata),
rag_ids: Some(serialize_rag_ids(&rag_ids)),
};
upsert_chat(db, &row)?;
Ok(())
}
@ -66,21 +105,52 @@ impl ChatCloudService for LocalServerChatServiceImpl {
async fn stream_answer(
&self,
_workspace_id: &Uuid,
_chat_id: &Uuid,
_message_id: i64,
_format: ResponseFormat,
chat_id: &Uuid,
message_id: i64,
format: ResponseFormat,
_ai_model: Option<AIModel>,
) -> Result<StreamAnswer, FlowyError> {
if self.local_ai.is_running() {
let content = self.get_message_content(message_id)?;
match self
.local_ai
.stream_question(
&chat_id.to_string(),
&content,
Some(json!(format)),
json!({}),
)
.await
{
Ok(stream) => Ok(QuestionStream::new(stream).boxed()),
Err(err) => Ok(
stream::once(async { Err(FlowyError::local_ai_unavailable().with_context(err)) }).boxed(),
),
}
} else {
Err(FlowyError::local_ai_not_ready())
}
}
async fn get_answer(
&self,
_workspace_id: &Uuid,
_chat_id: &Uuid,
_question_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn get_chat_messages(
&self,
_workspace_id: &Uuid,
_chat_id: &Uuid,
_offset: MessageCursor,
_limit: u64,
chat_id: &Uuid,
offset: MessageCursor,
limit: u64,
) -> Result<RepeatedChatMessage, FlowyError> {
let uid = self.user.user_id()?;
let db = self.user.get_sqlite_db(uid)?;
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
@ -106,15 +176,6 @@ impl ChatCloudService for LocalServerChatServiceImpl {
})
}
async fn get_answer(
&self,
_workspace_id: &Uuid,
_chat_id: &Uuid,
_question_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn stream_complete(
&self,
_workspace_id: &Uuid,
@ -137,18 +198,40 @@ impl ChatCloudService for LocalServerChatServiceImpl {
async fn get_chat_settings(
&self,
_workspace_id: &Uuid,
_chat_id: &Uuid,
chat_id: &Uuid,
) -> Result<ChatSettings, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
let chat_id = chat_id.to_string();
let uid = self.user.user_id()?;
let db = self.user.get_sqlite_db(uid)?;
let row = read_chat(db, &chat_id)?;
let rag_ids = deserialize_rag_ids(&row.rag_ids);
let metadata = deserialize_chat_metadata::<serde_json::Value>(&row.metadata);
let setting = ChatSettings {
name: row.name,
rag_ids,
metadata,
};
Ok(setting)
}
async fn update_chat_settings(
&self,
_workspace_id: &Uuid,
_id: &Uuid,
_s: UpdateChatParams,
id: &Uuid,
s: UpdateChatParams,
) -> Result<(), FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
let uid = self.user.user_id()?;
let mut db = self.user.get_sqlite_db(uid)?;
let changeset = ChatTableChangeset {
chat_id: id.to_string(),
name: s.name,
metadata: s.metadata.map(|s| serialize_chat_metadata(&s)),
rag_ids: s.rag_ids.map(|s| serialize_rag_ids(&s)),
};
update_chat(&mut db, changeset)?;
Ok(())
}
async fn get_available_models(&self, _workspace_id: &Uuid) -> Result<ModelList, FlowyError> {

View File

@ -8,6 +8,7 @@ use crate::local_server::impls::{
LocalServerUserServiceImpl,
};
use crate::AppFlowyServer;
use flowy_ai::local_ai::controller::LocalAIController;
use flowy_ai_pub::cloud::ChatCloudService;
use flowy_database_pub::cloud::{DatabaseAIService, DatabaseCloudService};
use flowy_document_pub::cloud::DocumentCloudService;
@ -18,13 +19,15 @@ use tokio::sync::mpsc;
pub struct LocalServer {
user: Arc<dyn ServerUser>,
local_ai: Arc<LocalAIController>,
stop_tx: Option<mpsc::Sender<()>>,
}
impl LocalServer {
pub fn new(user: Arc<dyn ServerUser>) -> Self {
pub fn new(user: Arc<dyn ServerUser>, local_ai: Arc<LocalAIController>) -> Self {
Self {
user,
local_ai,
stop_tx: Default::default(),
}
}
@ -61,6 +64,7 @@ impl AppFlowyServer for LocalServer {
fn chat_service(&self) -> Arc<dyn ChatCloudService> {
Arc::new(LocalServerChatServiceImpl {
user: self.user.clone(),
local_ai: self.local_ai.clone(),
})
}

View File

@ -0,0 +1,9 @@
-- This file should undo anything in `up.sql`
ALTER TABLE chat_table
ADD COLUMN local_enabled INTEGER;
ALTER TABLE chat_table
ADD COLUMN sync_to_cloud INTEGER;
ALTER TABLE chat_table
ADD COLUMN local_files TEXT;
ALTER TABLE chat_table DROP COLUMN rag_ids;

View File

@ -0,0 +1,4 @@
ALTER TABLE chat_table DROP COLUMN local_enabled;
ALTER TABLE chat_table DROP COLUMN local_files;
ALTER TABLE chat_table DROP COLUMN sync_to_cloud;
ALTER TABLE chat_table ADD COLUMN rag_ids TEXT;

View File

@ -35,10 +35,8 @@ diesel::table! {
chat_id -> Text,
created_at -> BigInt,
name -> Text,
local_files -> Text,
metadata -> Text,
local_enabled -> Bool,
sync_to_cloud -> Bool,
rag_ids -> Nullable<Text>,
}
}
@ -128,15 +126,15 @@ diesel::table! {
}
diesel::allow_tables_to_appear_in_same_query!(
af_collab_metadata,
chat_local_setting_table,
chat_message_table,
chat_table,
collab_snapshot,
upload_file_part,
upload_file_table,
user_data_migration_records,
user_table,
user_workspace_table,
workspace_members_table,
af_collab_metadata,
chat_local_setting_table,
chat_message_table,
chat_table,
collab_snapshot,
upload_file_part,
upload_file_table,
user_data_migration_records,
user_table,
user_workspace_table,
workspace_members_table,
);