Reroute routes (#4088)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Douwe Osinga
2025-08-14 14:23:02 -04:00
committed by GitHub
parent 290d29e961
commit d7e761c529
12 changed files with 466 additions and 523 deletions
+10
View File
@@ -369,6 +369,10 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
super::routes::config_management::upsert_permissions,
super::routes::agent::get_tools,
super::routes::agent::add_sub_recipes,
super::routes::agent::extend_prompt,
super::routes::agent::update_agent_provider,
super::routes::agent::update_router_tool_selector,
super::routes::agent::update_session_config,
super::routes::reply::confirm_permission,
super::routes::context::manage_context,
super::routes::session::list_sessions,
@@ -464,6 +468,12 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
goose::agents::types::SuccessCheck,
super::routes::agent::AddSubRecipesRequest,
super::routes::agent::AddSubRecipesResponse,
super::routes::agent::ExtendPromptRequest,
super::routes::agent::ExtendPromptResponse,
super::routes::agent::UpdateProviderRequest,
super::routes::agent::SessionConfigRequest,
super::routes::agent::GetToolsQuery,
super::routes::agent::ErrorResponse,
))
)]
pub struct ApiDoc;
+31 -77
View File
@@ -16,22 +16,15 @@ use goose::{
};
use goose::{config::Config, recipe::SubRecipe};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Serialize)]
struct VersionsResponse {
available_versions: Vec<String>,
default_version: String,
}
#[derive(Deserialize)]
struct ExtendPromptRequest {
#[derive(Deserialize, utoipa::ToSchema)]
pub struct ExtendPromptRequest {
extension: String,
}
#[derive(Serialize)]
struct ExtendPromptResponse {
#[derive(Serialize, utoipa::ToSchema)]
pub struct ExtendPromptResponse {
success: bool,
}
@@ -45,66 +38,35 @@ pub struct AddSubRecipesResponse {
success: bool,
}
#[derive(Deserialize)]
struct ProviderFile {
name: String,
description: String,
models: Vec<String>,
required_keys: Vec<String>,
}
#[derive(Serialize)]
struct ProviderDetails {
name: String,
description: String,
models: Vec<String>,
required_keys: Vec<String>,
}
#[derive(Serialize)]
struct ProviderList {
id: String,
details: ProviderDetails,
}
#[derive(Deserialize)]
struct UpdateProviderRequest {
#[derive(Deserialize, utoipa::ToSchema)]
pub struct UpdateProviderRequest {
provider: String,
model: Option<String>,
}
#[derive(Deserialize)]
struct SessionConfigRequest {
#[derive(Deserialize, utoipa::ToSchema)]
pub struct SessionConfigRequest {
response: Option<Response>,
}
#[derive(Deserialize)]
#[derive(Deserialize, utoipa::ToSchema)]
pub struct GetToolsQuery {
extension_name: Option<String>,
}
#[derive(Serialize)]
struct ErrorResponse {
#[derive(Serialize, utoipa::ToSchema)]
pub struct ErrorResponse {
error: String,
}
async fn get_versions() -> Json<VersionsResponse> {
let versions = ["goose".to_string()];
let default_version = "goose".to_string();
Json(VersionsResponse {
available_versions: versions.iter().map(|v| v.to_string()).collect(),
default_version,
})
}
#[utoipa::path(
post,
path = "/agent/add_sub_recipes",
request_body = AddSubRecipesRequest,
responses(
(status = 200, description = "added sub recipes to agent successfully", body = AddSubRecipesResponse),
(status = 200, description = "Added sub recipes to agent successfully", body = AddSubRecipesResponse),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
),
)]
async fn add_sub_recipes(
@@ -122,6 +84,16 @@ async fn add_sub_recipes(
Ok(Json(AddSubRecipesResponse { success: true }))
}
#[utoipa::path(
post,
path = "/agent/prompt",
request_body = ExtendPromptRequest,
responses(
(status = 200, description = "Extended system prompt successfully", body = ExtendPromptResponse),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
),
)]
async fn extend_prompt(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
@@ -137,29 +109,6 @@ async fn extend_prompt(
Ok(Json(ExtendPromptResponse { success: true }))
}
async fn list_providers() -> Json<Vec<ProviderList>> {
let contents = include_str!("providers_and_keys.json");
let providers: HashMap<String, ProviderFile> =
serde_json::from_str(contents).expect("Failed to parse providers_and_keys.json");
let response: Vec<ProviderList> = providers
.into_iter()
.map(|(id, provider)| ProviderList {
id,
details: ProviderDetails {
name: provider.name,
description: provider.description,
models: provider.models,
required_keys: provider.required_keys,
},
})
.collect();
// Return the response as JSON.
Json(response)
}
#[utoipa::path(
get,
path = "/agent/tools",
@@ -224,10 +173,12 @@ async fn get_tools(
#[utoipa::path(
post,
path = "/agent/update_provider",
request_body = UpdateProviderRequest,
responses(
(status = 200, description = "Update provider completed", body = String),
(status = 200, description = "Provider updated successfully"),
(status = 400, description = "Bad request - missing or invalid parameters"),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 500, description = "Internal server error")
)
)]
@@ -269,6 +220,8 @@ async fn update_agent_provider(
path = "/agent/update_router_tool_selector",
responses(
(status = 200, description = "Tool selection strategy updated successfully", body = String),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 500, description = "Internal server error")
)
)]
@@ -307,8 +260,11 @@ async fn update_router_tool_selector(
#[utoipa::path(
post,
path = "/agent/session_config",
request_body = SessionConfigRequest,
responses(
(status = 200, description = "Session config updated successfully", body = String),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 500, description = "Internal server error")
)
)]
@@ -344,8 +300,6 @@ async fn update_session_config(
pub fn routes(state: Arc<AppState>) -> Router {
Router::new()
.route("/agent/versions", get(get_versions))
.route("/agent/providers", get(list_providers))
.route("/agent/prompt", post(extend_prompt))
.route("/agent/tools", get(get_tools))
.route("/agent/update_provider", post(update_agent_provider))
@@ -58,9 +58,7 @@ pub struct ConfigResponse {
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ProviderDetails {
pub name: String,
pub metadata: ProviderMetadata,
pub is_configured: bool,
}
@@ -1,68 +0,0 @@
{
"openai": {
"name": "OpenAI",
"description": "Use GPT-4 and other OpenAI models",
"models": ["gpt-4o", "gpt-4-turbo","o1"],
"required_keys": ["OPENAI_API_KEY", "OPENAI_HOST", "OPENAI_BASE_PATH"]
},
"anthropic": {
"name": "Anthropic",
"description": "Use Claude and other Anthropic models",
"models": ["claude-3.5-sonnet-2"],
"required_keys": ["ANTHROPIC_API_KEY", "ANTHROPIC_HOST"]
},
"databricks": {
"name": "Databricks",
"description": "Connect to LLMs via Databricks",
"models": ["goose"],
"required_keys": ["DATABRICKS_HOST"]
},
"gcp_vertex_ai": {
"name": "GCP Vertex AI",
"description": "Use Vertex AI platform models",
"models": ["claude-3-5-haiku@20241022", "claude-3-5-sonnet@20240620", "claude-3-5-sonnet-v2@20241022", "claude-3-7-sonnet@20250219", "claude-sonnet-4@20250514", "claude-opus-4@20250514", "gemini-1.5-pro-002", "gemini-2.0-flash-001", "gemini-2.0-pro-exp-02-05", "gemini-2.5-pro-exp-03-25", "gemini-2.5-flash-preview-05-20", "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash", "gemini-2.5-pro"],
"required_keys": ["GCP_PROJECT_ID", "GCP_LOCATION"]
},
"google": {
"name": "Google",
"description": "Lorem ipsum",
"models": ["gemini-1.5-flash"],
"required_keys": ["GOOGLE_API_KEY"]
},
"groq": {
"name": "Groq",
"description": "Lorem ipsum",
"models": ["llama-3.3-70b-versatile"],
"required_keys": ["GROQ_API_KEY"]
},
"ollama": {
"name": "Ollama",
"description": "Lorem ipsum",
"models": ["qwen2.5"],
"required_keys": ["OLLAMA_HOST"]
},
"openrouter": {
"name": "OpenRouter",
"description": "Lorem ipsum",
"models": [],
"required_keys": ["OPENROUTER_API_KEY"]
},
"azure_openai": {
"name": "Azure OpenAI",
"description": "Connect to Azure OpenAI Service. If no API key is provided, Azure credential chain will be used.",
"models": ["gpt-4o", "gpt-4o-mini"],
"required_keys": ["AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_DEPLOYMENT_NAME"]
},
"aws_bedrock": {
"name": "AWS Bedrock",
"description": "Connect to LLMs via AWS Bedrock",
"models": ["us.anthropic.claude-3-7-sonnet-20250219-v1:0"],
"required_keys": ["AWS_PROFILE"]
},
"xai": {
"name": "Xai",
"description": "Lorem ipsum",
"models": ["grok-3"],
"required_keys": ["XAI_API_KEY"]
},
}
+1 -51
View File
@@ -1,5 +1,5 @@
use super::utils::verify_secret_key;
use chrono::{DateTime, Datelike};
use chrono::DateTime;
use std::collections::HashMap;
use std::sync::Arc;
@@ -260,55 +260,6 @@ async fn get_session_insights(
Ok(Json(insights))
}
#[utoipa::path(
get,
path = "/sessions/activity-heatmap",
responses(
(status = 200, description = "Activity heatmap data", body = [ActivityHeatmapCell]),
(status = 401, description = "Unauthorized - Invalid or missing API key"),
(status = 500, description = "Internal server error")
),
security(("api_key" = [])),
tag = "Session Management"
)]
async fn get_activity_heatmap(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> Result<Json<Vec<ActivityHeatmapCell>>, StatusCode> {
verify_secret_key(&headers, &state)?;
let sessions = get_valid_sorted_sessions(SortOrder::Descending)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// Only sessions with a description
let sessions: Vec<SessionInfo> = sessions
.into_iter()
.filter(|session| !session.metadata.description.is_empty())
.collect();
// Map: (week, day) -> count
let mut heatmap: std::collections::HashMap<(usize, usize), usize> =
std::collections::HashMap::new();
for session in &sessions {
if let Ok(date) =
chrono::NaiveDateTime::parse_from_str(&session.modified, "%Y-%m-%d %H:%M:%S UTC")
{
let date = date.date();
let week = date.iso_week().week() as usize - 1; // 0-based week
let day = date.weekday().num_days_from_sunday() as usize; // 0=Sun, 6=Sat
*heatmap.entry((week, day)).or_insert(0) += 1;
}
}
let mut result = Vec::new();
for ((week, day), count) in heatmap {
result.push(ActivityHeatmapCell { week, day, count });
}
Ok(Json(result))
}
#[utoipa::path(
put,
path = "/sessions/{session_id}/metadata",
@@ -365,7 +316,6 @@ pub fn routes(state: Arc<AppState>) -> Router {
.route("/sessions", get(list_sessions))
.route("/sessions/{session_id}", get(get_session_history))
.route("/sessions/insights", get(get_session_insights))
.route("/sessions/activity-heatmap", get(get_activity_heatmap))
.route(
"/sessions/{session_id}/metadata",
put(update_session_metadata),