merge: agent-lifecycle-polish
# Conflicts: # TASKS.md
This commit is contained in:
+118
-2
@@ -12,8 +12,8 @@ use crate::{
|
||||
list_memberships, list_platform_api_keys, revoke_platform_api_key, update_membership,
|
||||
},
|
||||
agents::{
|
||||
create_agent, delete_agent, get_agent, get_agent_version, list_agents, publish_agent,
|
||||
save_agent_bindings, update_agent,
|
||||
archive_agent, create_agent, delete_agent, get_agent, get_agent_version, list_agents,
|
||||
publish_agent, save_agent_bindings, unpublish_agent, update_agent,
|
||||
},
|
||||
auth::{
|
||||
change_password, get_profile, get_session, login, logout, update_current_workspace,
|
||||
@@ -92,6 +92,8 @@ pub fn build_app(state: AppState) -> Router {
|
||||
)
|
||||
.route("/agents/{agent_id}/bindings", post(save_agent_bindings))
|
||||
.route("/agents/{agent_id}/publish", post(publish_agent))
|
||||
.route("/agents/{agent_id}/unpublish", post(unpublish_agent))
|
||||
.route("/agents/{agent_id}/archive", post(archive_agent))
|
||||
.route(
|
||||
"/auth-profiles",
|
||||
get(list_auth_profiles).post(create_auth_profile),
|
||||
@@ -669,6 +671,120 @@ mod tests {
|
||||
assert_eq!(missing.status(), reqwest::StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[serial]
|
||||
async fn unpublishes_and_archives_agent() {
|
||||
let registry = test_registry().await;
|
||||
let storage_root = test_storage_root("agent_statuses");
|
||||
let upstream_base_url = spawn_upstream_server().await;
|
||||
let base_url = spawn_admin_api(build_test_app(registry, storage_root)).await;
|
||||
let client = authorized_client(&base_url).await;
|
||||
|
||||
let operation = assert_success_json(
|
||||
client
|
||||
.post(format!("{base_url}/operations"))
|
||||
.json(&test_operation_payload(
|
||||
&upstream_base_url,
|
||||
"crm_create_lead_agent_status",
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
let operation_id = operation["operation_id"].as_str().unwrap().to_owned();
|
||||
|
||||
client
|
||||
.post(format!("{base_url}/operations/{operation_id}/publish"))
|
||||
.json(&json!({ "version": 1 }))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let created = assert_success_json(
|
||||
client
|
||||
.post(format!("{base_url}/agents"))
|
||||
.json(&json!({
|
||||
"slug": "sales-routing",
|
||||
"display_name": "Sales Routing",
|
||||
"description": "Routing agent",
|
||||
"instructions": {},
|
||||
"tool_selection_policy": {}
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
let agent_id = created["agent_id"].as_str().unwrap().to_owned();
|
||||
|
||||
client
|
||||
.post(format!("{base_url}/agents/{agent_id}/bindings"))
|
||||
.json(&json!([
|
||||
{
|
||||
"operation_id": operation_id,
|
||||
"operation_version": 1,
|
||||
"tool_name": "crm_create_lead_agent_status",
|
||||
"tool_title": "Create Lead",
|
||||
"tool_description_override": null,
|
||||
"enabled": true
|
||||
}
|
||||
]))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
client
|
||||
.post(format!("{base_url}/agents/{agent_id}/publish"))
|
||||
.json(&json!({ "version": 1 }))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let unpublished = assert_success_json(
|
||||
client
|
||||
.post(format!("{base_url}/agents/{agent_id}/unpublish"))
|
||||
.json(&json!({}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(unpublished["agent_id"], agent_id);
|
||||
|
||||
let draft_detail = assert_success_json(
|
||||
client
|
||||
.get(format!("{base_url}/agents/{agent_id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(draft_detail["status"], "draft");
|
||||
assert_eq!(draft_detail["latest_published_version"], 1);
|
||||
|
||||
let archived = assert_success_json(
|
||||
client
|
||||
.post(format!("{base_url}/agents/{agent_id}/archive"))
|
||||
.json(&json!({}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(archived["agent_id"], agent_id);
|
||||
|
||||
let archived_detail = assert_success_json(
|
||||
client
|
||||
.get(format!("{base_url}/agents/{agent_id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(archived_detail["status"], "archived");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[serial]
|
||||
async fn manages_platform_access_resources() {
|
||||
|
||||
@@ -142,3 +142,31 @@ pub async fn publish_agent(
|
||||
.await?;
|
||||
Ok(Json(json!(published)))
|
||||
}
|
||||
|
||||
pub async fn unpublish_agent(
|
||||
Path(path): Path<WorkspaceAgentPath>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
let updated = state
|
||||
.service
|
||||
.unpublish_agent(
|
||||
&path.workspace_id.as_str().into(),
|
||||
&path.agent_id.as_str().into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(Json(json!(updated)))
|
||||
}
|
||||
|
||||
pub async fn archive_agent(
|
||||
Path(path): Path<WorkspaceAgentPath>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
let updated = state
|
||||
.service
|
||||
.archive_agent(
|
||||
&path.workspace_id.as_str().into(),
|
||||
&path.agent_id.as_str().into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(Json(json!(updated)))
|
||||
}
|
||||
|
||||
@@ -2026,6 +2026,46 @@ impl AdminService {
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self), fields(workspace_id = %workspace_id.as_str(), agent_id = %agent_id.as_str()))]
|
||||
pub async fn unpublish_agent(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
agent_id: &AgentId,
|
||||
) -> Result<AgentMutationResult, ApiError> {
|
||||
self.ensure_workspace_exists(workspace_id).await?;
|
||||
let updated_at = now_string()?;
|
||||
self.registry
|
||||
.unpublish_agent(workspace_id, agent_id, &updated_at)
|
||||
.await?;
|
||||
info!(agent_id = %agent_id.as_str(), "agent moved to draft");
|
||||
|
||||
Ok(AgentMutationResult {
|
||||
agent_id: agent_id.as_str().to_owned(),
|
||||
workspace_id: workspace_id.as_str().to_owned(),
|
||||
updated_at,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self), fields(workspace_id = %workspace_id.as_str(), agent_id = %agent_id.as_str()))]
|
||||
pub async fn archive_agent(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
agent_id: &AgentId,
|
||||
) -> Result<AgentMutationResult, ApiError> {
|
||||
self.ensure_workspace_exists(workspace_id).await?;
|
||||
let updated_at = now_string()?;
|
||||
self.registry
|
||||
.archive_agent(workspace_id, agent_id, &updated_at)
|
||||
.await?;
|
||||
info!(agent_id = %agent_id.as_str(), "agent archived");
|
||||
|
||||
Ok(AgentMutationResult {
|
||||
agent_id: agent_id.as_str().to_owned(),
|
||||
workspace_id: workspace_id.as_str().to_owned(),
|
||||
updated_at,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self, payload), fields(operation_id = %operation_id.as_str(), source_name = source_name.unwrap_or("descriptor-set.bin")))]
|
||||
pub async fn upload_descriptor_set(
|
||||
&self,
|
||||
|
||||
Reference in New Issue
Block a user