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::config::PermissionManager;
|
||||||
use goose::model::ModelConfig;
|
use goose::model::ModelConfig;
|
||||||
use goose::providers::create;
|
use goose::providers::create;
|
||||||
|
use goose::recipe::Response;
|
||||||
use goose::{
|
use goose::{
|
||||||
agents::{extension::ToolInfo, extension_manager::get_parameter_names},
|
agents::{extension::ToolInfo, extension_manager::get_parameter_names},
|
||||||
config::permission::PermissionLevel,
|
config::permission::PermissionLevel,
|
||||||
@@ -62,6 +63,11 @@ struct UpdateProviderRequest {
|
|||||||
model: Option<String>,
|
model: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct SessionConfigRequest {
|
||||||
|
response: Option<Response>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GetToolsQuery {
|
pub struct GetToolsQuery {
|
||||||
extension_name: Option<String>,
|
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 {
|
pub fn routes(state: Arc<AppState>) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/agent/versions", get(get_versions))
|
.route("/agent/versions", get(get_versions))
|
||||||
@@ -273,5 +317,6 @@ pub fn routes(state: Arc<AppState>) -> Router {
|
|||||||
"/agent/update_router_tool_selector",
|
"/agent/update_router_tool_selector",
|
||||||
post(update_router_tool_selector),
|
post(update_router_tool_selector),
|
||||||
)
|
)
|
||||||
|
.route("/agent/session_config", post(update_session_config))
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1321,7 +1321,9 @@ mod tests {
|
|||||||
agent.add_final_output_tool(response).await;
|
agent.add_final_output_tool(response).await;
|
||||||
|
|
||||||
let tools = agent.list_tools(None).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!(
|
assert!(
|
||||||
final_output_tool.is_some(),
|
final_output_tool.is_some(),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use mcp_core::{
|
|||||||
};
|
};
|
||||||
use serde_json::Value;
|
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 =
|
pub const FINAL_OUTPUT_CONTINUATION_MESSAGE: &str =
|
||||||
"You MUST call the `final_output` tool with your final output for the user.";
|
"You MUST call the `final_output` tool with your final output for the user.";
|
||||||
|
|
||||||
|
|||||||
@@ -285,6 +285,9 @@ function ToolCallView({
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'final_output':
|
||||||
|
return 'final output';
|
||||||
|
|
||||||
case 'computer_control':
|
case 'computer_control':
|
||||||
return 'poking around...';
|
return 'poking around...';
|
||||||
|
|
||||||
|
|||||||
@@ -191,6 +191,8 @@ export const initializeSystem = async (
|
|||||||
// Get recipeConfig directly here
|
// Get recipeConfig directly here
|
||||||
const recipeConfig = window.appConfig?.get?.('recipeConfig');
|
const recipeConfig = window.appConfig?.get?.('recipeConfig');
|
||||||
const botPrompt = (recipeConfig as { instructions?: string })?.instructions;
|
const botPrompt = (recipeConfig as { instructions?: string })?.instructions;
|
||||||
|
const responseConfig = (recipeConfig as { response?: { json_schema?: unknown } })?.response;
|
||||||
|
|
||||||
// Extend the system prompt with desktop-specific information
|
// Extend the system prompt with desktop-specific information
|
||||||
const response = await fetch(getApiUrl('/agent/prompt'), {
|
const response = await fetch(getApiUrl('/agent/prompt'), {
|
||||||
method: 'POST',
|
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) {
|
if (!options?.getExtensions || !options?.addExtension) {
|
||||||
console.warn('Extension helpers not provided in alpha mode');
|
console.warn('Extension helpers not provided in alpha mode');
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user