Skip to content

Commit 459ae33

Browse files
authored
feat(ui): add session content search via API (#7050)
1 parent 99c25c4 commit 459ae33

7 files changed

Lines changed: 238 additions & 28 deletions

File tree

crates/goose-server/src/openapi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ derive_utoipa!(Icon as IconSchema);
377377
super::routes::action_required::confirm_tool_action,
378378
super::routes::reply::reply,
379379
super::routes::session::list_sessions,
380+
super::routes::session::search_sessions,
380381
super::routes::session::get_session,
381382
super::routes::session::get_session_insights,
382383
super::routes::session::update_session_name,

crates/goose-server/src/routes/session.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ async fn get_session_extensions(
490490
pub fn routes(state: Arc<AppState>) -> Router {
491491
Router::new()
492492
.route("/sessions", get(list_sessions))
493+
.route("/sessions/search", get(search_sessions))
493494
.route("/sessions/{session_id}", get(get_session))
494495
.route("/sessions/{session_id}", delete(delete_session))
495496
.route("/sessions/{session_id}/export", get(export_session))
@@ -510,3 +511,88 @@ pub fn routes(state: Arc<AppState>) -> Router {
510511
)
511512
.with_state(state)
512513
}
514+
#[derive(Deserialize, ToSchema)]
515+
#[serde(rename_all = "camelCase")]
516+
pub struct SearchSessionsQuery {
517+
/// Search query string (keywords separated by spaces)
518+
query: String,
519+
/// Maximum number of results to return (default: 10, max: 50)
520+
#[serde(default = "default_limit")]
521+
limit: usize,
522+
/// Filter results to sessions after this date (ISO 8601 format)
523+
after_date: Option<String>,
524+
/// Filter results to sessions before this date (ISO 8601 format)
525+
before_date: Option<String>,
526+
}
527+
528+
fn default_limit() -> usize {
529+
10
530+
}
531+
532+
#[utoipa::path(
533+
get,
534+
path = "/sessions/search",
535+
params(
536+
("query" = String, Query, description = "Search query string"),
537+
("limit" = Option<usize>, Query, description = "Maximum results (default: 10, max: 50)"),
538+
("after_date" = Option<String>, Query, description = "Filter after date (ISO 8601)"),
539+
("before_date" = Option<String>, Query, description = "Filter before date (ISO 8601)")
540+
),
541+
responses(
542+
(status = 200, description = "Matching sessions", body = Vec<Session>),
543+
(status = 400, description = "Bad request - Invalid query"),
544+
(status = 401, description = "Unauthorized"),
545+
(status = 500, description = "Internal server error")
546+
),
547+
security(
548+
("api_key" = [])
549+
),
550+
tag = "Session Management"
551+
)]
552+
async fn search_sessions(
553+
State(state): State<Arc<AppState>>,
554+
axum::extract::Query(params): axum::extract::Query<SearchSessionsQuery>,
555+
) -> Result<Json<Vec<Session>>, StatusCode> {
556+
let query = params.query.trim();
557+
if query.is_empty() {
558+
return Err(StatusCode::BAD_REQUEST);
559+
}
560+
561+
let limit = params.limit.min(50);
562+
563+
let after_date = params
564+
.after_date
565+
.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok())
566+
.map(|dt| dt.with_timezone(&chrono::Utc));
567+
568+
let before_date = params
569+
.before_date
570+
.and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok())
571+
.map(|dt| dt.with_timezone(&chrono::Utc));
572+
573+
let search_results = state
574+
.session_manager()
575+
.search_chat_history(query, Some(limit), after_date, before_date, None)
576+
.await
577+
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
578+
579+
// Get full Session objects for matching session IDs
580+
let session_ids: Vec<String> = search_results
581+
.results
582+
.into_iter()
583+
.map(|r| r.session_id)
584+
.collect();
585+
586+
let all_sessions = state
587+
.session_manager()
588+
.list_sessions()
589+
.await
590+
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
591+
592+
let matching_sessions: Vec<Session> = all_sessions
593+
.into_iter()
594+
.filter(|s| session_ids.contains(&s.id))
595+
.collect();
596+
597+
Ok(Json(matching_sessions))
598+
}

ui/desktop/openapi.json

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,6 +2745,85 @@
27452745
]
27462746
}
27472747
},
2748+
"/sessions/search": {
2749+
"get": {
2750+
"tags": [
2751+
"Session Management"
2752+
],
2753+
"operationId": "search_sessions",
2754+
"parameters": [
2755+
{
2756+
"name": "query",
2757+
"in": "query",
2758+
"description": "Search query string",
2759+
"required": true,
2760+
"schema": {
2761+
"type": "string"
2762+
}
2763+
},
2764+
{
2765+
"name": "limit",
2766+
"in": "query",
2767+
"description": "Maximum results (default: 10, max: 50)",
2768+
"required": false,
2769+
"schema": {
2770+
"type": "integer",
2771+
"nullable": true,
2772+
"minimum": 0
2773+
}
2774+
},
2775+
{
2776+
"name": "after_date",
2777+
"in": "query",
2778+
"description": "Filter after date (ISO 8601)",
2779+
"required": false,
2780+
"schema": {
2781+
"type": "string",
2782+
"nullable": true
2783+
}
2784+
},
2785+
{
2786+
"name": "before_date",
2787+
"in": "query",
2788+
"description": "Filter before date (ISO 8601)",
2789+
"required": false,
2790+
"schema": {
2791+
"type": "string",
2792+
"nullable": true
2793+
}
2794+
}
2795+
],
2796+
"responses": {
2797+
"200": {
2798+
"description": "Matching sessions",
2799+
"content": {
2800+
"application/json": {
2801+
"schema": {
2802+
"type": "array",
2803+
"items": {
2804+
"$ref": "#/components/schemas/Session"
2805+
}
2806+
}
2807+
}
2808+
}
2809+
},
2810+
"400": {
2811+
"description": "Bad request - Invalid query"
2812+
},
2813+
"401": {
2814+
"description": "Unauthorized"
2815+
},
2816+
"500": {
2817+
"description": "Internal server error"
2818+
}
2819+
},
2820+
"security": [
2821+
{
2822+
"api_key": []
2823+
}
2824+
]
2825+
}
2826+
},
27482827
"/sessions/{session_id}": {
27492828
"get": {
27502829
"tags": [

ui/desktop/src/api/index.ts

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

ui/desktop/src/api/sdk.gen.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import type { Client, Options as Options2, TDataShape } from './client';
44
import { client } from './client.gen';
5-
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CheckProviderData, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
5+
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CheckProviderData, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
66

77
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<TData, ThrowOnError> & {
88
/**
@@ -458,6 +458,8 @@ export const importSession = <ThrowOnError extends boolean = false>(options: Opt
458458

459459
export const getSessionInsights = <ThrowOnError extends boolean = false>(options?: Options<GetSessionInsightsData, ThrowOnError>) => (options?.client ?? client).get<GetSessionInsightsResponses, GetSessionInsightsErrors, ThrowOnError>({ url: '/sessions/insights', ...options });
460460

461+
export const searchSessions = <ThrowOnError extends boolean = false>(options: Options<SearchSessionsData, ThrowOnError>) => (options.client ?? client).get<SearchSessionsResponses, SearchSessionsErrors, ThrowOnError>({ url: '/sessions/search', ...options });
462+
461463
export const deleteSession = <ThrowOnError extends boolean = false>(options: Options<DeleteSessionData, ThrowOnError>) => (options.client ?? client).delete<DeleteSessionResponses, DeleteSessionErrors, ThrowOnError>({ url: '/sessions/{session_id}', ...options });
462464

463465
export const getSession = <ThrowOnError extends boolean = false>(options: Options<GetSessionData, ThrowOnError>) => (options.client ?? client).get<GetSessionResponses, GetSessionErrors, ThrowOnError>({ url: '/sessions/{session_id}', ...options });

ui/desktop/src/api/types.gen.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3530,6 +3530,54 @@ export type GetSessionInsightsResponses = {
35303530

35313531
export type GetSessionInsightsResponse = GetSessionInsightsResponses[keyof GetSessionInsightsResponses];
35323532

3533+
export type SearchSessionsData = {
3534+
body?: never;
3535+
path?: never;
3536+
query: {
3537+
/**
3538+
* Search query string
3539+
*/
3540+
query: string;
3541+
/**
3542+
* Maximum results (default: 10, max: 50)
3543+
*/
3544+
limit?: number | null;
3545+
/**
3546+
* Filter after date (ISO 8601)
3547+
*/
3548+
after_date?: string | null;
3549+
/**
3550+
* Filter before date (ISO 8601)
3551+
*/
3552+
before_date?: string | null;
3553+
};
3554+
url: '/sessions/search';
3555+
};
3556+
3557+
export type SearchSessionsErrors = {
3558+
/**
3559+
* Bad request - Invalid query
3560+
*/
3561+
400: unknown;
3562+
/**
3563+
* Unauthorized
3564+
*/
3565+
401: unknown;
3566+
/**
3567+
* Internal server error
3568+
*/
3569+
500: unknown;
3570+
};
3571+
3572+
export type SearchSessionsResponses = {
3573+
/**
3574+
* Matching sessions
3575+
*/
3576+
200: Array<Session>;
3577+
};
3578+
3579+
export type SearchSessionsResponse = SearchSessionsResponses[keyof SearchSessionsResponses];
3580+
35333581
export type DeleteSessionData = {
35343582
body?: never;
35353583
path: {

0 commit comments

Comments
 (0)