fix(tkmind): restore provider and session recipe on compat reply paths
Unused Dependencies / machete (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
Add POST /agent/update_from_session to apply session recipe to the agent, and restore provider from session when session_reply finds no provider set. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -14,6 +15,7 @@ use serde_json::Value;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::error;
|
||||
|
||||
use crate::agents::Agent;
|
||||
use crate::agents::extension::ToolInfo;
|
||||
use crate::agents::extension_manager::get_parameter_names;
|
||||
use crate::agents::reply_parts::is_tool_visible_to_app;
|
||||
@@ -25,6 +27,8 @@ use crate::config::permission::PermissionLevel;
|
||||
use crate::config::resolve_extensions_for_new_session;
|
||||
use crate::config::{Config, GooseMode};
|
||||
use crate::providers::create;
|
||||
use crate::recipe::build_recipe::{build_recipe_from_template, RecipeError};
|
||||
use crate::recipe::local_recipes::get_recipe_library_dir;
|
||||
use crate::recipe::Recipe;
|
||||
use crate::session::session_manager::SessionType;
|
||||
use crate::session::{EnabledExtensionsState, ExtensionState, Session};
|
||||
@@ -71,6 +75,11 @@ pub struct UpdateWorkingDirRequest {
|
||||
working_dir: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UpdateFromSessionRequest {
|
||||
session_id: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RestartAgentRequest {
|
||||
session_id: String,
|
||||
@@ -658,6 +667,80 @@ async fn resume_agent(
|
||||
}))
|
||||
}
|
||||
|
||||
async fn build_recipe_with_parameter_values(
|
||||
original_recipe: &Recipe,
|
||||
user_recipe_values: HashMap<String, String>,
|
||||
) -> Result<Option<Recipe>, String> {
|
||||
let recipe_content = original_recipe
|
||||
.to_yaml()
|
||||
.map_err(|err| format!("Failed to serialize recipe: {err}"))?;
|
||||
let recipe_dir = get_recipe_library_dir(true);
|
||||
let params: Vec<(String, String)> = user_recipe_values.into_iter().collect();
|
||||
match build_recipe_from_template(
|
||||
recipe_content,
|
||||
&recipe_dir,
|
||||
params,
|
||||
None::<fn(&str, &str) -> Result<String, anyhow::Error>>,
|
||||
) {
|
||||
Ok(recipe) => Ok(Some(recipe)),
|
||||
Err(RecipeError::MissingParams { .. }) => Ok(None),
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn apply_recipe_to_agent(agent: &Arc<Agent>, recipe: &Recipe) {
|
||||
let _ = agent
|
||||
.apply_recipe_components(recipe.response.clone(), true)
|
||||
.await;
|
||||
if let Some(instructions) = recipe.instructions.as_ref() {
|
||||
agent
|
||||
.extend_system_prompt("recipe".to_string(), instructions.clone())
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn apply_session_recipe_to_agent(
|
||||
agent: &Arc<Agent>,
|
||||
session: &Session,
|
||||
) -> Result<(), String> {
|
||||
let Some(recipe) = session.recipe.as_ref() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if session.session_type == SessionType::Scheduled {
|
||||
apply_recipe_to_agent(agent, recipe).await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let user_values = session.user_recipe_values.clone().unwrap_or_default();
|
||||
match build_recipe_with_parameter_values(recipe, user_values).await? {
|
||||
Some(rendered) => apply_recipe_to_agent(agent, &rendered).await,
|
||||
None => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_from_session(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(payload): Json<UpdateFromSessionRequest>,
|
||||
) -> Result<StatusCode, (StatusCode, String)> {
|
||||
let agent = state
|
||||
.get_agent_for_route(payload.session_id.clone())
|
||||
.await
|
||||
.map_err(|status| (status, "No agent for session id".to_owned()))?;
|
||||
let session = state
|
||||
.session_manager()
|
||||
.get_session(&payload.session_id, false)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to get session: {err}")))?;
|
||||
|
||||
apply_session_recipe_to_agent(&agent, &session)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err))?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
pub fn routes(state: Arc<AppState>) -> Router {
|
||||
Router::new()
|
||||
.route("/agent/start", post(start_agent))
|
||||
@@ -665,6 +748,7 @@ pub fn routes(state: Arc<AppState>) -> Router {
|
||||
.route("/agent/call_tool", post(call_tool))
|
||||
.route("/agent/tools", get(get_tools))
|
||||
.route("/agent/update_provider", post(update_agent_provider))
|
||||
.route("/agent/update_from_session", post(update_from_session))
|
||||
.route("/agent/update_session", post(update_session))
|
||||
.route("/agent/add_extension", post(add_extension))
|
||||
.route("/agent/remove_extension", post(remove_extension))
|
||||
|
||||
@@ -347,6 +347,24 @@ pub async fn session_reply(
|
||||
}
|
||||
};
|
||||
|
||||
if agent.provider().await.is_err() {
|
||||
if let Err(error) = agent.restore_provider_from_session(&session).await {
|
||||
tracing::error!(
|
||||
"Failed to restore provider for session {}: {}",
|
||||
task_session_id,
|
||||
error
|
||||
);
|
||||
publish(
|
||||
Some(task_request_id.clone()),
|
||||
MessageEvent::Error {
|
||||
error: format!("Provider not set: {}", error),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let session_config = SessionConfig {
|
||||
id: task_session_id.clone(),
|
||||
schedule_id: session.schedule_id.clone(),
|
||||
|
||||
Reference in New Issue
Block a user