mirror of
https://github.com/aljazceru/goose.git
synced 2026-02-22 15:04:29 +01:00
feat: GUI can use structured output present in recipes (#3218)
This commit is contained in:
@@ -10,6 +10,7 @@ use goose::config::Config;
|
||||
use goose::config::PermissionManager;
|
||||
use goose::model::ModelConfig;
|
||||
use goose::providers::create;
|
||||
use goose::recipe::Response;
|
||||
use goose::{
|
||||
agents::{extension::ToolInfo, extension_manager::get_parameter_names},
|
||||
config::permission::PermissionLevel,
|
||||
@@ -62,6 +63,11 @@ struct UpdateProviderRequest {
|
||||
model: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SessionConfigRequest {
|
||||
response: Option<Response>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetToolsQuery {
|
||||
extension_name: Option<String>,
|
||||
@@ -262,6 +268,44 @@ async fn update_router_tool_selector(
|
||||
))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/agent/session_config",
|
||||
responses(
|
||||
(status = 200, description = "Session config updated successfully", body = String),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
async fn update_session_config(
|
||||
State(state): State<Arc<AppState>>,
|
||||
headers: HeaderMap,
|
||||
Json(payload): Json<SessionConfigRequest>,
|
||||
) -> Result<Json<String>, Json<ErrorResponse>> {
|
||||
verify_secret_key(&headers, &state).map_err(|_| {
|
||||
Json(ErrorResponse {
|
||||
error: "Unauthorized - Invalid or missing API key".to_string(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let agent = state.get_agent().await.map_err(|e| {
|
||||
tracing::error!("Failed to get agent: {}", e);
|
||||
Json(ErrorResponse {
|
||||
error: format!("Failed to get agent: {}", e),
|
||||
})
|
||||
})?;
|
||||
|
||||
if let Some(response) = payload.response {
|
||||
agent.add_final_output_tool(response).await;
|
||||
|
||||
tracing::info!("Added final output tool with response config");
|
||||
Ok(Json(
|
||||
"Session config updated with final output tool".to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(Json("Nothing provided to update.".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes(state: Arc<AppState>) -> Router {
|
||||
Router::new()
|
||||
.route("/agent/versions", get(get_versions))
|
||||
@@ -273,5 +317,6 @@ pub fn routes(state: Arc<AppState>) -> Router {
|
||||
"/agent/update_router_tool_selector",
|
||||
post(update_router_tool_selector),
|
||||
)
|
||||
.route("/agent/session_config", post(update_session_config))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -1321,7 +1321,9 @@ mod tests {
|
||||
agent.add_final_output_tool(response).await;
|
||||
|
||||
let tools = agent.list_tools(None).await;
|
||||
let final_output_tool = tools.iter().find(|tool| tool.name == "final_output");
|
||||
let final_output_tool = tools
|
||||
.iter()
|
||||
.find(|tool| tool.name == FINAL_OUTPUT_TOOL_NAME);
|
||||
|
||||
assert!(
|
||||
final_output_tool.is_some(),
|
||||
|
||||
@@ -7,7 +7,7 @@ use mcp_core::{
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
pub const FINAL_OUTPUT_TOOL_NAME: &str = "final_output";
|
||||
pub const FINAL_OUTPUT_TOOL_NAME: &str = "recipe__final_output";
|
||||
pub const FINAL_OUTPUT_CONTINUATION_MESSAGE: &str =
|
||||
"You MUST call the `final_output` tool with your final output for the user.";
|
||||
|
||||
|
||||
@@ -285,6 +285,9 @@ function ToolCallView({
|
||||
}
|
||||
break;
|
||||
|
||||
case 'final_output':
|
||||
return 'final output';
|
||||
|
||||
case 'computer_control':
|
||||
return 'poking around...';
|
||||
|
||||
|
||||
@@ -191,6 +191,8 @@ export const initializeSystem = async (
|
||||
// Get recipeConfig directly here
|
||||
const recipeConfig = window.appConfig?.get?.('recipeConfig');
|
||||
const botPrompt = (recipeConfig as { instructions?: string })?.instructions;
|
||||
const responseConfig = (recipeConfig as { response?: { json_schema?: unknown } })?.response;
|
||||
|
||||
// Extend the system prompt with desktop-specific information
|
||||
const response = await fetch(getApiUrl('/agent/prompt'), {
|
||||
method: 'POST',
|
||||
@@ -213,6 +215,25 @@ export const initializeSystem = async (
|
||||
}
|
||||
}
|
||||
|
||||
// Configure session with response config if present
|
||||
if (responseConfig?.json_schema) {
|
||||
const sessionConfigResponse = await fetch(getApiUrl('/agent/session_config'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Secret-Key': getSecretKey(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
response: responseConfig,
|
||||
}),
|
||||
});
|
||||
if (!sessionConfigResponse.ok) {
|
||||
console.warn(`Failed to configure session: ${sessionConfigResponse.statusText}`);
|
||||
} else {
|
||||
console.log('Configured session with response schema');
|
||||
}
|
||||
}
|
||||
|
||||
if (!options?.getExtensions || !options?.addExtension) {
|
||||
console.warn('Extension helpers not provided in alpha mode');
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user