From 2f083b827a6e87c1c43b1f85f2a521d1c048c747 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Tue, 20 Jan 2026 17:15:52 -0500 Subject: [PATCH] User configurable templates (#6420) Co-authored-by: Douwe Osinga --- crates/goose-server/src/openapi.rs | 8 + crates/goose-server/src/routes/agent.rs | 6 +- crates/goose-server/src/routes/mod.rs | 2 + crates/goose-server/src/routes/prompts.rs | 133 ++++++ .../goose-server/src/routes/recipe_utils.rs | 4 +- crates/goose/src/agents/extension_manager.rs | 2 +- crates/goose/src/agents/prompt_manager.rs | 6 +- crates/goose/src/agents/subagent_handler.rs | 4 +- crates/goose/src/context_mgmt/mod.rs | 4 +- .../goose/src/permission/permission_judge.rs | 4 +- crates/goose/src/prompt_template.rs | 408 +++++++++--------- .../{summarize_oneshot.md => compaction.md} | 0 crates/goose/src/prompts/mock.md | 3 - crates/goose/src/prompts/system_gpt_4.1.md | 63 --- ui/desktop/openapi.json | 210 +++++++++ ui/desktop/src/api/index.ts | 4 +- ui/desktop/src/api/sdk.gen.ts | 17 +- ui/desktop/src/api/types.gen.ts | 134 ++++++ ui/desktop/src/components/BaseChat.tsx | 3 +- .../settings/PromptsSettingsSection.tsx | 297 +++++++++++++ .../src/components/settings/SettingsView.tsx | 19 +- .../models/subcomponents/SwitchModelModal.tsx | 22 +- ui/desktop/src/hooks/useChatStream.ts | 6 +- 23 files changed, 1052 insertions(+), 307 deletions(-) create mode 100644 crates/goose-server/src/routes/prompts.rs rename crates/goose/src/prompts/{summarize_oneshot.md => compaction.md} (100%) delete mode 100644 crates/goose/src/prompts/mock.md delete mode 100644 crates/goose/src/prompts/system_gpt_4.1.md create mode 100644 ui/desktop/src/components/settings/PromptsSettingsSection.tsx diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index 7935a1acf4..be4634b17d 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -353,6 +353,10 @@ derive_utoipa!(Icon as IconSchema); super::routes::config_management::check_provider, super::routes::config_management::set_config_provider, super::routes::config_management::get_pricing, + super::routes::prompts::get_prompts, + super::routes::prompts::get_prompt, + super::routes::prompts::save_prompt, + super::routes::prompts::reset_prompt, super::routes::agent::start_agent, super::routes::agent::resume_agent, super::routes::agent::stop_agent, @@ -427,6 +431,10 @@ derive_utoipa!(Icon as IconSchema); super::routes::config_management::PricingQuery, super::routes::config_management::PricingResponse, super::routes::config_management::PricingData, + super::routes::prompts::PromptsListResponse, + super::routes::prompts::PromptContentResponse, + super::routes::prompts::SavePromptRequest, + goose::prompt_template::Template, super::routes::action_required::ConfirmToolActionRequest, super::routes::reply::ChatRequest, super::routes::session::ImportSessionRequest, diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index 7e3503c0f6..fda525fa80 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -18,7 +18,7 @@ use goose::agents::ExtensionConfig; use goose::config::resolve_extensions_for_new_session; use goose::config::{Config, GooseMode}; use goose::model::ModelConfig; -use goose::prompt_template::render_global_file; +use goose::prompt_template::render_template; use goose::providers::create; use goose::recipe::Recipe; use goose::recipe_deeplink; @@ -418,7 +418,7 @@ async fn update_from_session( })?; let context: HashMap<&str, Value> = HashMap::new(); let desktop_prompt = - render_global_file("desktop_prompt.md", &context).expect("Prompt should render"); + render_template("desktop_prompt.md", &context).expect("Prompt should render"); let mut update_prompt = desktop_prompt; if let Some(recipe) = session.recipe { match build_recipe_with_parameter_values( @@ -691,7 +691,7 @@ async fn restart_agent_internal( let context: HashMap<&str, Value> = HashMap::new(); let desktop_prompt = - render_global_file("desktop_prompt.md", &context).expect("Prompt should render"); + render_template("desktop_prompt.md", &context).expect("Prompt should render"); let mut update_prompt = desktop_prompt; if let Some(ref recipe) = session.recipe { diff --git a/crates/goose-server/src/routes/mod.rs b/crates/goose-server/src/routes/mod.rs index aa9392b92f..d65241cc0d 100644 --- a/crates/goose-server/src/routes/mod.rs +++ b/crates/goose-server/src/routes/mod.rs @@ -5,6 +5,7 @@ pub mod config_management; pub mod errors; pub mod mcp_app_proxy; pub mod mcp_ui_proxy; +pub mod prompts; pub mod recipe; pub mod recipe_utils; pub mod reply; @@ -29,6 +30,7 @@ pub fn configure(state: Arc, secret_key: String) -> Rout .merge(agent::routes(state.clone())) .merge(audio::routes(state.clone())) .merge(config_management::routes(state.clone())) + .merge(prompts::routes()) .merge(recipe::routes(state.clone())) .merge(session::routes(state.clone())) .merge(schedule::routes(state.clone())) diff --git a/crates/goose-server/src/routes/prompts.rs b/crates/goose-server/src/routes/prompts.rs new file mode 100644 index 0000000000..ed62c1e427 --- /dev/null +++ b/crates/goose-server/src/routes/prompts.rs @@ -0,0 +1,133 @@ +use axum::{ + extract::Path, + routing::{delete, get, put}, + Json, Router, +}; +use goose::prompt_template::{ + get_template, list_templates, reset_template, save_template, Template, +}; +use http::StatusCode; +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +#[derive(Serialize, ToSchema)] +pub struct PromptsListResponse { + pub prompts: Vec