use crate::AppState;
use anyhow::{anyhow, Result};
use serde_json::{json, Value};
use trusty_common::memory_core::store::chat_sessions::ChatMessage;
use super::helpers::resolve_palace;
use super::palace_index::{resolve_palace_or_index, PalaceScope};
const VALID_ROLES: [&str; 3] = ["user", "assistant", "system"];
pub(crate) async fn handle_chat_session_create(state: &AppState, args: Value) -> Result<Value> {
let palace = resolve_palace(state, &args, "chat_session_create")?;
let store = state.session_store(&palace)?;
let title = args
.get("title")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let session_id = match args.get("session_id").and_then(|v| v.as_str()) {
Some(id) => {
if store.get_session(id)?.is_none() {
store.upsert_session(id, &[])?;
}
id.to_string()
}
None => store.create_session(title)?,
};
let session = store
.get_session(&session_id)?
.ok_or_else(|| anyhow!("chat_session_create: session vanished after write"))?;
Ok(json!({
"session_id": session.id,
"created_at": session.created_at,
"message_count": session.history.len(),
}))
}
pub(crate) async fn handle_chat_session_add_turn(state: &AppState, args: Value) -> Result<Value> {
let palace = resolve_palace(state, &args, "chat_session_add_turn")?;
let session_id = args
.get("session_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_session_add_turn: missing 'session_id'"))?;
let role = args
.get("role")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_session_add_turn: missing 'role'"))?;
if !VALID_ROLES.contains(&role) {
return Err(anyhow!(
"chat_session_add_turn: invalid role '{role}' (expected one of {VALID_ROLES:?})"
));
}
let content = args
.get("content")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_session_add_turn: missing 'content'"))?;
let store = state.session_store(&palace)?;
let session = store.append_message(
session_id,
ChatMessage {
role: role.to_string(),
content: content.to_string(),
attachments: serde_json::from_value(
args.get("attachments").cloned().unwrap_or(json!([])),
)?,
},
)?;
Ok(json!({
"message_count": session.history.len(),
"updated_at": session.updated_at,
"attachments_version": 1,
}))
}
pub(crate) async fn handle_chat_session_get(state: &AppState, args: Value) -> Result<Value> {
let palace = match resolve_palace_or_index(state, &args, "chat_session_get").await? {
PalaceScope::Palace(p) => p,
PalaceScope::Index(index) => return Ok(index),
};
let session_id = args
.get("session_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_session_get: missing 'session_id'"))?;
let store = state.session_store(&palace)?;
let session = store
.get_session(session_id)?
.ok_or_else(|| anyhow!("chat_session_get: session not found: {session_id}"))?;
Ok(serde_json::to_value(session)?)
}
pub(crate) async fn handle_chat_session_list(state: &AppState, args: Value) -> Result<Value> {
let palace = match resolve_palace_or_index(state, &args, "chat_session_list").await? {
PalaceScope::Palace(p) => p,
PalaceScope::Index(index) => return Ok(index),
};
let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(50) as usize;
let offset = args.get("offset").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
let store = state.session_store(&palace)?;
let metas = store.list_sessions()?;
let total_count = metas.len();
let page: Vec<_> = metas.into_iter().skip(offset).take(limit).collect();
Ok(json!({
"sessions": serde_json::to_value(page)?,
"total_count": total_count,
}))
}
pub(crate) async fn handle_chat_session_delete(state: &AppState, args: Value) -> Result<Value> {
let palace = resolve_palace(state, &args, "chat_session_delete")?;
let session_id = args
.get("session_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_session_delete: missing 'session_id'"))?;
let store = state.session_store(&palace)?;
store.delete_session(session_id)?;
Ok(json!({ "deleted": session_id }))
}
pub(crate) async fn handle_chat_session_recall(state: &AppState, args: Value) -> Result<Value> {
let palace = match resolve_palace_or_index(state, &args, "chat_session_recall").await? {
PalaceScope::Palace(p) => p,
PalaceScope::Index(index) => return Ok(index),
};
let session_id = args
.get("session_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_session_recall: missing 'session_id'"))?;
let store = state.session_store(&palace)?;
let session = store
.get_session(session_id)?
.ok_or_else(|| anyhow!("chat_session_recall: session not found: {session_id}"))?;
Ok(serde_json::to_value(session)?)
}
pub(crate) async fn handle_chat_turn_append(state: &AppState, args: Value) -> Result<Value> {
let palace = resolve_palace(state, &args, "chat_turn_append")?;
let session_id = args
.get("session_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_turn_append: missing 'session_id'"))?;
let prompt = args
.get("prompt")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_turn_append: missing 'prompt'"))?;
let response = args
.get("response")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("chat_turn_append: missing 'response'"))?;
let store = state.session_store(&palace)?;
let session = store.append_messages(
session_id,
vec![
ChatMessage {
role: "user".to_string(),
content: prompt.to_string(),
attachments: serde_json::from_value(
args.get("attachments").cloned().unwrap_or(json!([])),
)?,
},
ChatMessage {
role: "assistant".to_string(),
content: response.to_string(),
attachments: vec![],
},
],
)?;
Ok(json!({
"message_count": session.history.len(),
"updated_at": session.updated_at,
"attachments_version": 1,
}))
}