mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-17 22:24:21 +01:00
cleanup
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -3535,8 +3535,10 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"config 0.13.4",
|
||||
"dashmap 6.1.0",
|
||||
"dirs 5.0.1",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"goose",
|
||||
|
||||
@@ -23,6 +23,9 @@ futures-util = "0.3"
|
||||
# For session IDs
|
||||
uuid = { version = "1", features = ["serde", "v4"] }
|
||||
dashmap = "6"
|
||||
# For session listing
|
||||
dirs = "5"
|
||||
chrono = "0.4"
|
||||
# Add dynamic-library for extension loading
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -53,6 +53,39 @@ pub struct EndSessionRequest {
|
||||
pub session_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct GetSessionRequest {
|
||||
pub session_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SessionInfo {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub modified: String,
|
||||
pub message_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ListSessionsResponse {
|
||||
pub sessions: Vec<SessionInfo>,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SessionMessage {
|
||||
pub role: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct GetSessionResponse {
|
||||
pub session_id: String,
|
||||
pub name: String,
|
||||
pub messages: Vec<SessionMessage>,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SummarizeSessionRequest {
|
||||
pub session_id: Uuid,
|
||||
@@ -585,6 +618,158 @@ pub async fn metrics_handler() -> Result<impl warp::Reply, Rejection> {
|
||||
Ok(warp::reply::json(&resp))
|
||||
}
|
||||
|
||||
pub async fn list_sessions_handler(
|
||||
_api_key: String,
|
||||
) -> Result<impl warp::Reply, Rejection> {
|
||||
info!("Listing all sessions");
|
||||
|
||||
// Get all session files from the goose session directory
|
||||
let data_dir = dirs::data_local_dir()
|
||||
.ok_or_else(|| custom(AnyhowRejection(anyhow::anyhow!("Failed to get data directory"))))?;
|
||||
let sessions_dir = data_dir.join("goose").join("sessions");
|
||||
|
||||
let mut sessions = Vec::new();
|
||||
|
||||
if sessions_dir.exists() {
|
||||
match std::fs::read_dir(&sessions_dir) {
|
||||
Ok(entries) => {
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.extension().and_then(|ext| ext.to_str()) == Some("jsonl") {
|
||||
// Read the session file to get metadata
|
||||
match session::read_metadata(&path) {
|
||||
Ok(metadata) => {
|
||||
if let Ok(file_metadata) = entry.metadata() {
|
||||
if let Ok(modified) = file_metadata.modified() {
|
||||
let modified_str = chrono::DateTime::<chrono::Utc>::from(modified)
|
||||
.format("%Y-%m-%d %H:%M:%S UTC")
|
||||
.to_string();
|
||||
|
||||
let session_id = path.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
|
||||
sessions.push(SessionInfo {
|
||||
id: session_id,
|
||||
name: metadata.description,
|
||||
modified: modified_str,
|
||||
message_count: metadata.message_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("Failed to read metadata for {:?}: {}", path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to read sessions directory: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by modified date (newest first)
|
||||
sessions.sort_by(|a, b| b.modified.cmp(&a.modified));
|
||||
|
||||
let response = ListSessionsResponse {
|
||||
sessions,
|
||||
status: "success".to_string(),
|
||||
};
|
||||
|
||||
Ok(warp::reply::with_status(
|
||||
warp::reply::json(&response),
|
||||
warp::http::StatusCode::OK,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_session_handler(
|
||||
req: GetSessionRequest,
|
||||
_api_key: String,
|
||||
) -> Result<impl warp::Reply, Rejection> {
|
||||
info!("Getting session: {}", req.session_id);
|
||||
|
||||
let session_name = req.session_id.to_string();
|
||||
let session_path = session::get_path(Identifier::Name(session_name.clone()))
|
||||
.map_err(|e| custom(AnyhowRejection(anyhow::anyhow!("Failed to get session path: {}", e))))?;
|
||||
|
||||
// Check if session file exists
|
||||
if !session_path.exists() {
|
||||
let response = GetSessionResponse {
|
||||
session_id: req.session_id.to_string(),
|
||||
name: String::new(),
|
||||
messages: Vec::new(),
|
||||
status: "error".to_string(),
|
||||
};
|
||||
return Ok(warp::reply::with_status(
|
||||
warp::reply::json(&response),
|
||||
warp::http::StatusCode::NOT_FOUND,
|
||||
));
|
||||
}
|
||||
|
||||
// Read session metadata and messages
|
||||
let metadata = session::read_metadata(&session_path)
|
||||
.map_err(|e| custom(AnyhowRejection(anyhow::anyhow!("Failed to read session metadata: {}", e))))?;
|
||||
|
||||
let messages = session::read_messages(&session_path)
|
||||
.map_err(|e| custom(AnyhowRejection(anyhow::anyhow!("Failed to read session messages: {}", e))))?;
|
||||
|
||||
// Convert messages to API format
|
||||
let api_messages: Vec<SessionMessage> = messages.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, msg)| {
|
||||
// Messages typically alternate between user and assistant
|
||||
// First message is usually user, then assistant, and so on
|
||||
// We can also serialize the message and check the role field
|
||||
let role = if let Ok(serialized) = serde_json::to_value(msg) {
|
||||
if let Some(role_value) = serialized.get("role") {
|
||||
if let Some(role_str) = role_value.as_str() {
|
||||
role_str.to_lowercase()
|
||||
} else {
|
||||
// Fallback to alternating pattern
|
||||
if idx % 2 == 0 { "user" } else { "assistant" }.to_string()
|
||||
}
|
||||
} else {
|
||||
// Fallback to alternating pattern
|
||||
if idx % 2 == 0 { "user" } else { "assistant" }.to_string()
|
||||
}
|
||||
} else {
|
||||
// Fallback to alternating pattern
|
||||
if idx % 2 == 0 { "user" } else { "assistant" }.to_string()
|
||||
};
|
||||
|
||||
// Extract text content, filtering out thinking tags
|
||||
let content = msg.content
|
||||
.iter()
|
||||
.filter_map(|c| match c {
|
||||
MessageContent::Text(text) => Some(text.text.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
SessionMessage { role, content }
|
||||
})
|
||||
.collect();
|
||||
|
||||
let response = GetSessionResponse {
|
||||
session_id: req.session_id.to_string(),
|
||||
name: metadata.description,
|
||||
messages: api_messages,
|
||||
status: "success".to_string(),
|
||||
};
|
||||
|
||||
Ok(warp::reply::with_status(
|
||||
warp::reply::json(&response),
|
||||
warp::http::StatusCode::OK,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn handle_rejection(err: Rejection) -> Result<impl warp::Reply, Rejection> {
|
||||
if let Some(e) = err.find::<AnyhowRejection>() {
|
||||
let message = e.0.to_string();
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::handlers::{
|
||||
end_session_handler, get_provider_config_handler, handle_rejection,
|
||||
list_extensions_handler, metrics_handler, reply_session_handler,
|
||||
start_session_handler, summarize_session_handler, with_api_key,
|
||||
list_sessions_handler, get_session_handler,
|
||||
};
|
||||
use crate::config::{
|
||||
load_provider_config, load_configuration,
|
||||
@@ -54,6 +55,19 @@ pub fn build_routes(api_key: String) -> impl Filter<Extract = impl warp::Reply,
|
||||
.and(warp::get())
|
||||
.and_then(metrics_handler);
|
||||
|
||||
let list_sessions = warp::path("sessions")
|
||||
.and(warp::path("list"))
|
||||
.and(warp::get())
|
||||
.and(with_api_key(api_key.clone()))
|
||||
.and_then(list_sessions_handler);
|
||||
|
||||
let get_session = warp::path("session")
|
||||
.and(warp::path("get"))
|
||||
.and(warp::post())
|
||||
.and(warp::body::json())
|
||||
.and(with_api_key(api_key.clone()))
|
||||
.and_then(get_session_handler);
|
||||
|
||||
start_session
|
||||
.or(reply_session)
|
||||
.or(summarize_session)
|
||||
@@ -61,6 +75,8 @@ pub fn build_routes(api_key: String) -> impl Filter<Extract = impl warp::Reply,
|
||||
.or(list_extensions)
|
||||
.or(get_provider_config)
|
||||
.or(metrics)
|
||||
.or(list_sessions)
|
||||
.or(get_session)
|
||||
.recover(handle_rejection)
|
||||
}
|
||||
|
||||
|
||||
@@ -1324,7 +1324,17 @@ pub async fn generate_description_with_schedule_id(
|
||||
anyhow::anyhow!("Failed to generate session description")
|
||||
})?;
|
||||
|
||||
let description = result.0.as_concat_text();
|
||||
// Extract only non-thinking text content for the description
|
||||
let description = result.0.content
|
||||
.iter()
|
||||
.filter_map(|c| match c {
|
||||
crate::message::MessageContent::Text(text) => Some(text.text.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
// Validate description length for security
|
||||
let sanitized_description = if description.chars().count() > 100 {
|
||||
|
||||
Reference in New Issue
Block a user