feat(ui): add session content search via API (#7050)
This commit is contained in:
@@ -377,6 +377,7 @@ derive_utoipa!(Icon as IconSchema);
|
||||
super::routes::action_required::confirm_tool_action,
|
||||
super::routes::reply::reply,
|
||||
super::routes::session::list_sessions,
|
||||
super::routes::session::search_sessions,
|
||||
super::routes::session::get_session,
|
||||
super::routes::session::get_session_insights,
|
||||
super::routes::session::update_session_name,
|
||||
|
||||
@@ -490,6 +490,7 @@ async fn get_session_extensions(
|
||||
pub fn routes(state: Arc<AppState>) -> Router {
|
||||
Router::new()
|
||||
.route("/sessions", get(list_sessions))
|
||||
.route("/sessions/search", get(search_sessions))
|
||||
.route("/sessions/{session_id}", get(get_session))
|
||||
.route("/sessions/{session_id}", delete(delete_session))
|
||||
.route("/sessions/{session_id}/export", get(export_session))
|
||||
@@ -510,3 +511,88 @@ pub fn routes(state: Arc<AppState>) -> Router {
|
||||
)
|
||||
.with_state(state)
|
||||
}
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SearchSessionsQuery {
|
||||
/// Search query string (keywords separated by spaces)
|
||||
query: String,
|
||||
/// Maximum number of results to return (default: 10, max: 50)
|
||||
#[serde(default = "default_limit")]
|
||||
limit: usize,
|
||||
/// Filter results to sessions after this date (ISO 8601 format)
|
||||
after_date: Option<String>,
|
||||
/// Filter results to sessions before this date (ISO 8601 format)
|
||||
before_date: Option<String>,
|
||||
}
|
||||
|
||||
fn default_limit() -> usize {
|
||||
10
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/sessions/search",
|
||||
params(
|
||||
("query" = String, Query, description = "Search query string"),
|
||||
("limit" = Option<usize>, Query, description = "Maximum results (default: 10, max: 50)"),
|
||||
("after_date" = Option<String>, Query, description = "Filter after date (ISO 8601)"),
|
||||
("before_date" = Option<String>, Query, description = "Filter before date (ISO 8601)")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Matching sessions", body = Vec<Session>),
|
||||
(status = 400, description = "Bad request - Invalid query"),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Internal server error")
|
||||
),
|
||||
security(
|
||||
("api_key" = [])
|
||||
),
|
||||
tag = "Session Management"
|
||||
)]
|
||||
async fn search_sessions(
|
||||
State(state): State<Arc<AppState>>,
|
||||
axum::extract::Query(params): axum::extract::Query<SearchSessionsQuery>,
|
||||
) -> Result<Json<Vec<Session>>, StatusCode> {
|
||||
let query = params.query.trim();
|
||||
if query.is_empty() {
|
||||
return Err(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
let limit = params.limit.min(50);
|
||||
|
||||
let after_date = params
|
||||
.after_date
|
||||
.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok())
|
||||
.map(|dt| dt.with_timezone(&chrono::Utc));
|
||||
|
||||
let before_date = params
|
||||
.before_date
|
||||
.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok())
|
||||
.map(|dt| dt.with_timezone(&chrono::Utc));
|
||||
|
||||
let search_results = state
|
||||
.session_manager()
|
||||
.search_chat_history(query, Some(limit), after_date, before_date, None)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
// Get full Session objects for matching session IDs
|
||||
let session_ids: Vec<String> = search_results
|
||||
.results
|
||||
.into_iter()
|
||||
.map(|r| r.session_id)
|
||||
.collect();
|
||||
|
||||
let all_sessions = state
|
||||
.session_manager()
|
||||
.list_sessions()
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
let matching_sessions: Vec<Session> = all_sessions
|
||||
.into_iter()
|
||||
.filter(|s| session_ids.contains(&s.id))
|
||||
.collect();
|
||||
|
||||
Ok(Json(matching_sessions))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user