Add reply to the routes (#4963)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2025-10-03 13:32:42 -04:00
committed by GitHub
parent 73e35c15ab
commit 5b8efb5b9a
5 changed files with 117 additions and 45 deletions
+2
View File
@@ -346,6 +346,7 @@ derive_utoipa!(Icon as IconSchema);
super::routes::agent::update_router_tool_selector,
super::routes::agent::update_session_config,
super::routes::reply::confirm_permission,
super::routes::reply::reply,
super::routes::context::manage_context,
super::routes::session::list_sessions,
super::routes::session::get_session,
@@ -385,6 +386,7 @@ derive_utoipa!(Icon as IconSchema);
super::routes::config_management::UpsertPermissionsQuery,
super::routes::config_management::CreateCustomProviderRequest,
super::routes::reply::PermissionConfirmationRequest,
super::routes::reply::ChatRequest,
super::routes::context::ContextManageRequest,
super::routes::context::ContextManageResponse,
super::routes::session::SessionListResponse,
+16 -44
View File
@@ -10,16 +10,14 @@ use bytes::Bytes;
use futures::{stream::StreamExt, Stream};
use goose::conversation::message::{Message, MessageContent};
use goose::conversation::Conversation;
use goose::mcp_utils::ToolResult;
use goose::permission::{Permission, PermissionConfirmation};
use goose::session::SessionManager;
use goose::{
agents::{AgentEvent, SessionConfig},
permission::permission_confirmation::PrincipalType,
};
use rmcp::model::{Content, ServerNotification};
use rmcp::model::ServerNotification;
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;
use std::{
convert::Infallible,
@@ -81,8 +79,8 @@ fn track_tool_telemetry(content: &MessageContent, all_messages: &[Message]) {
}
}
#[derive(Debug, Deserialize, Serialize)]
struct ChatRequest {
#[derive(Debug, Deserialize, Serialize, utoipa::ToSchema)]
pub struct ChatRequest {
messages: Vec<Message>,
session_id: String,
recipe_name: Option<String>,
@@ -163,7 +161,18 @@ async fn stream_event(
}
}
async fn reply_handler(
#[allow(clippy::too_many_lines)]
#[utoipa::path(
post,
path = "/reply",
request_body = ChatRequest,
responses(
(status = 200, description = "Streaming response initiated", content_type = "text/event-stream"),
(status = 424, description = "Agent not initialized"),
(status = 500, description = "Internal server error")
)
)]
pub async fn reply(
State(state): State<Arc<AppState>>,
Json(request): Json<ChatRequest>,
) -> Result<SseResponse, StatusCode> {
@@ -446,50 +455,13 @@ pub async fn confirm_permission(
Ok(Json(Value::Object(serde_json::Map::new())))
}
#[derive(Debug, Deserialize)]
struct ToolResultRequest {
id: String,
result: ToolResult<Vec<Content>>,
session_id: String,
}
async fn submit_tool_result(
State(state): State<Arc<AppState>>,
raw: Json<Value>,
) -> Result<Json<Value>, StatusCode> {
tracing::info!(
"Received tool result request: {}",
serde_json::to_string_pretty(&raw.0).unwrap()
);
let payload: ToolResultRequest = match serde_json::from_value(raw.0.clone()) {
Ok(req) => req,
Err(e) => {
tracing::error!("Failed to parse tool result request: {}", e);
tracing::error!(
"Raw request was: {}",
serde_json::to_string_pretty(&raw.0).unwrap()
);
return Err(StatusCode::UNPROCESSABLE_ENTITY);
}
};
let agent = state.get_agent_for_route(payload.session_id).await?;
agent.handle_tool_result(payload.id, payload.result).await;
Ok(Json(json!({"status": "ok"})))
}
pub fn routes(state: Arc<AppState>) -> Router {
Router::new()
.route(
"/reply",
post(reply_handler).layer(DefaultBodyLimit::max(50 * 1024 * 1024)),
post(reply).layer(DefaultBodyLimit::max(50 * 1024 * 1024)),
)
.route("/confirm", post(confirm_permission))
.route(
"/tool_result",
post(submit_tool_result).layer(DefaultBodyLimit::max(10 * 1024 * 1024)),
)
.with_state(state)
}