diff --git a/crates/goose-server/src/routes/recipe.rs b/crates/goose-server/src/routes/recipe.rs index 899f3dfc666d..ad463aefed4d 100644 --- a/crates/goose-server/src/routes/recipe.rs +++ b/crates/goose-server/src/routes/recipe.rs @@ -83,14 +83,21 @@ async fn create_recipe( State(state): State>, Json(request): Json, ) -> Result, (StatusCode, Json)> { + tracing::info!( + "Recipe creation request received with {} messages", + request.messages.len() + ); + let error_response = CreateRecipeResponse { recipe: None, error: Some("Missing agent".to_string()), }; - let agent = state - .get_agent() - .await - .map_err(|_| (StatusCode::PRECONDITION_FAILED, Json(error_response)))?; + let agent = state.get_agent().await.map_err(|e| { + tracing::error!("Failed to get agent for recipe creation: {}", e); + (StatusCode::PRECONDITION_FAILED, Json(error_response)) + })?; + + tracing::debug!("Agent retrieved successfully, creating recipe from conversation"); // Create base recipe from agent state and messages let recipe_result = agent @@ -99,6 +106,8 @@ async fn create_recipe( match recipe_result { Ok(mut recipe) => { + tracing::info!("Recipe created successfully with title: '{}'", recipe.title); + // Update with user-provided metadata recipe.title = request.title; recipe.description = request.description; @@ -114,16 +123,23 @@ async fn create_recipe( }); } + tracing::debug!("Recipe metadata updated, returning success response"); + Ok(Json(CreateRecipeResponse { recipe: Some(recipe), error: None, })) } Err(e) => { + // Log the detailed error for debugging + tracing::error!("Recipe creation failed: {}", e); + tracing::error!("Error details: {:?}", e); + // Return 400 Bad Request with error message + let error_message = format!("Recipe creation failed: {}", e); let error_response = CreateRecipeResponse { recipe: None, - error: Some(e.to_string()), + error: Some(error_message), }; Err((StatusCode::BAD_REQUEST, Json(error_response))) } diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index 01c417984160..cb676d025ca4 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -1364,12 +1364,19 @@ impl Agent { } pub async fn create_recipe(&self, mut messages: Conversation) -> Result { + tracing::info!("Starting recipe creation with {} messages", messages.len()); + let extensions_info = self.extension_manager.get_extensions_info().await; + tracing::debug!("Retrieved {} extensions info", extensions_info.len()); // Get model name from provider - let provider = self.provider().await?; + let provider = self.provider().await.map_err(|e| { + tracing::error!("Failed to get provider for recipe creation: {}", e); + e + })?; let model_config = provider.get_model_config(); let model_name = &model_config.model_name; + tracing::debug!("Using model: {}", model_name); let prompt_manager = self.prompt_manager.lock().await; let system_prompt = prompt_manager.build_system_prompt( @@ -1381,22 +1388,51 @@ impl Agent { Some(model_name), false, ); + tracing::debug!( + "Built system prompt with {} characters", + system_prompt.len() + ); let recipe_prompt = prompt_manager.get_recipe_prompt().await; - let tools = self.extension_manager.get_prefixed_tools(None).await?; + let tools = self + .extension_manager + .get_prefixed_tools(None) + .await + .map_err(|e| { + tracing::error!("Failed to get tools for recipe creation: {}", e); + e + })?; + tracing::debug!("Retrieved {} tools for recipe creation", tools.len()); messages.push(Message::user().with_text(recipe_prompt)); + tracing::debug!( + "Added recipe prompt to messages, total messages: {}", + messages.len() + ); + tracing::info!("Calling provider to generate recipe content"); let (result, _usage) = self .provider .lock() .await .as_ref() - .unwrap() + .ok_or_else(|| { + let error = anyhow!("Provider not available during recipe creation"); + tracing::error!("{}", error); + error + })? .complete(&system_prompt, messages.messages(), &tools) - .await?; + .await + .map_err(|e| { + tracing::error!("Provider completion failed during recipe creation: {}", e); + e + })?; let content = result.as_concat_text(); + tracing::debug!( + "Provider returned content with {} characters", + content.len() + ); // the response may be contained in ```json ```, strip that before parsing json let re = Regex::new(r"(?s)```[^\n]*\n(.*?)\n```").unwrap(); @@ -1406,10 +1442,17 @@ impl Agent { .unwrap_or(&content) .trim() .to_string(); + tracing::debug!( + "Cleaned content for parsing: {}", + &clean_content[..std::cmp::min(200, clean_content.len())] + ); // try to parse json response from the LLM + tracing::debug!("Attempting to parse recipe content as JSON"); let (instructions, activities) = if let Ok(json_content) = serde_json::from_str::(&clean_content) { + tracing::debug!("Successfully parsed JSON content"); + let instructions = json_content .get("instructions") .ok_or_else(|| anyhow!("Missing 'instructions' in json response"))? @@ -1432,6 +1475,7 @@ impl Agent { (instructions, activities) } else { + tracing::warn!("Failed to parse JSON, falling back to string parsing"); // If we can't get valid JSON, try string parsing // Use split_once to get the content after "Instructions:". let after_instructions = content @@ -1491,6 +1535,12 @@ impl Agent { temperature: Some(model_config.temperature.unwrap_or(0.0)), }; + tracing::debug!( + "Building recipe with {} activities and {} extensions", + activities.len(), + extension_configs.len() + ); + let recipe = Recipe::builder() .title("Custom recipe from chat") .description("a custom recipe instance from this chat session") @@ -1500,8 +1550,12 @@ impl Agent { .settings(settings) .author(author) .build() - .expect("valid recipe"); + .map_err(|e| { + tracing::error!("Failed to build recipe: {}", e); + anyhow!("Recipe build failed: {}", e) + })?; + tracing::info!("Recipe creation completed successfully"); Ok(recipe) } }