use sea_orm::ActiveValue::{NotSet, Set};
use uuid::Uuid;
use chat_engine_sdk::models::{
LifecycleState, Message, MessagePart, MessagePartType, MessageRole, Session, TenantId, UserId,
};
use crate::domain::reaction::{MessageReaction, ReactionType};
use crate::infra::db::entity::message as message_entity;
use crate::infra::db::entity::message_part as message_part_entity;
use crate::infra::db::entity::message_reaction as reaction_entity;
use crate::infra::db::entity::session as session_entity;
impl From<session_entity::Model> for Session {
fn from(model: session_entity::Model) -> Self {
let lifecycle_state = LifecycleState::from_str_value(&model.lifecycle_state)
.unwrap_or(LifecycleState::Active);
Session {
session_id: model.session_id,
tenant_id: model.tenant_id.into(),
user_id: model.user_id.into(),
client_id: model.client_id,
session_type_id: model.session_type_id,
enabled_capabilities: model.enabled_capabilities,
metadata: model.metadata,
lifecycle_state,
share_token: model.share_token.map(toolkit_utils::SecretString::new),
created_at: model.created_at,
updated_at: model.updated_at,
}
}
}
impl From<Session> for session_entity::ActiveModel {
fn from(s: Session) -> Self {
session_entity::ActiveModel {
session_id: Set(s.session_id),
tenant_id: Set(s.tenant_id.into_inner()),
user_id: Set(s.user_id.into_inner()),
client_id: Set(s.client_id),
session_type_id: Set(s.session_type_id),
enabled_capabilities: Set(s.enabled_capabilities),
metadata: Set(s.metadata),
lifecycle_state: Set(s.lifecycle_state.as_str().to_string()),
share_token: Set(s.share_token.map(toolkit_utils::SecretString::into_inner)),
deleted_at: NotSet,
scheduled_hard_delete_at: NotSet,
created_at: Set(s.created_at),
updated_at: Set(s.updated_at),
}
}
}
impl From<message_entity::Model> for Message {
fn from(m: message_entity::Model) -> Self {
let role = role_from_entity(&m.role);
let file_ids = m
.file_ids
.as_ref()
.and_then(|v| serde_json::from_value::<Vec<Uuid>>(v.clone()).ok())
.unwrap_or_default();
let variant_index = u32::try_from(m.variant_index).unwrap_or(0);
Message {
message_id: m.message_id,
session_id: m.session_id,
tenant_id: m.tenant_id.filter(|s| !s.is_empty()).map(TenantId::from),
user_id: m.user_id.filter(|s| !s.is_empty()).map(UserId::from),
parent_message_id: m.parent_message_id,
variant_index,
is_active: m.is_active,
role,
parts: Vec::new(),
file_ids,
metadata: m.metadata,
is_complete: m.is_complete,
is_hidden_from_user: m.is_hidden_from_user,
is_hidden_from_backend: m.is_hidden_from_backend,
created_at: m.created_at,
updated_at: m.created_at,
}
}
}
impl From<Message> for message_entity::ActiveModel {
fn from(m: Message) -> Self {
let file_ids_json = if m.file_ids.is_empty() {
None
} else {
serde_json::to_value(&m.file_ids).ok()
};
message_entity::ActiveModel {
message_id: Set(m.message_id),
session_id: Set(m.session_id),
tenant_id: Set(m.tenant_id.map(|t| t.as_str().to_owned())),
user_id: Set(m.user_id.map(|u| u.as_str().to_owned())),
parent_message_id: Set(m.parent_message_id),
role: Set(role_to_entity(&m.role)),
file_ids: Set(file_ids_json),
variant_index: Set(i32::try_from(m.variant_index).unwrap_or(i32::MAX)),
is_active: Set(m.is_active),
is_complete: Set(m.is_complete),
is_hidden_from_user: Set(m.is_hidden_from_user),
is_hidden_from_backend: Set(m.is_hidden_from_backend),
metadata: Set(m.metadata),
created_at: Set(m.created_at),
}
}
}
fn role_from_entity(role: &message_entity::MessageRole) -> MessageRole {
match role {
message_entity::MessageRole::User => MessageRole::User,
message_entity::MessageRole::Assistant => MessageRole::Assistant,
message_entity::MessageRole::System => MessageRole::System,
}
}
fn role_to_entity(role: &MessageRole) -> message_entity::MessageRole {
match role {
MessageRole::User => message_entity::MessageRole::User,
MessageRole::Assistant => message_entity::MessageRole::Assistant,
MessageRole::System => message_entity::MessageRole::System,
}
}
impl From<message_part_entity::Model> for MessagePart {
fn from(p: message_part_entity::Model) -> Self {
MessagePart {
id: p.id,
message_id: p.message_id,
part_type: part_type_from_entity(&p.r#type),
content: p.content,
number: u32::try_from(p.number).unwrap_or(0),
file_citations: Vec::new(),
link_citations: Vec::new(),
references: Vec::new(),
}
}
}
impl From<MessagePart> for message_part_entity::ActiveModel {
fn from(p: MessagePart) -> Self {
message_part_entity::ActiveModel {
id: Set(p.id),
message_id: Set(p.message_id),
r#type: Set(part_type_to_entity(&p.part_type)),
content: Set(p.content),
number: Set(i32::try_from(p.number).unwrap_or(i32::MAX)),
}
}
}
fn part_type_from_entity(t: &message_part_entity::MessagePartType) -> MessagePartType {
match t {
message_part_entity::MessagePartType::Text => MessagePartType::Text,
message_part_entity::MessagePartType::Code => MessagePartType::Code,
message_part_entity::MessagePartType::Images => MessagePartType::Images,
message_part_entity::MessagePartType::Videos => MessagePartType::Videos,
message_part_entity::MessagePartType::Links => MessagePartType::Links,
message_part_entity::MessagePartType::Statuses => MessagePartType::Statuses,
}
}
pub fn part_type_to_entity(t: &MessagePartType) -> message_part_entity::MessagePartType {
match t {
MessagePartType::Text => message_part_entity::MessagePartType::Text,
MessagePartType::Code => message_part_entity::MessagePartType::Code,
MessagePartType::Images => message_part_entity::MessagePartType::Images,
MessagePartType::Videos => message_part_entity::MessagePartType::Videos,
MessagePartType::Links => message_part_entity::MessagePartType::Links,
MessagePartType::Statuses => message_part_entity::MessagePartType::Statuses,
}
}
impl From<reaction_entity::Model> for MessageReaction {
fn from(m: reaction_entity::Model) -> Self {
let reaction_type =
ReactionType::from_str_value(&m.reaction_type).unwrap_or(ReactionType::None);
Self {
message_id: m.message_id,
user_id: m.user_id,
reaction_type,
created_at: m.created_at,
updated_at: m.updated_at,
}
}
}
#[cfg(test)]
#[path = "conversions_tests.rs"]
mod conversions_tests;