systemprompt_api/routes/agent/
tasks.rs1use axum::extract::{Path, Query, State};
2use axum::http::StatusCode;
3use axum::response::IntoResponse;
4use axum::{Extension, Json};
5use serde::Deserialize;
6use systemprompt_identifiers::{ContextId, TaskId, UserId};
7use systemprompt_models::api::ApiError;
8
9use systemprompt_agent::models::a2a::TaskState;
10use systemprompt_agent::repository::task::TaskRepository;
11use systemprompt_models::RequestContext;
12use systemprompt_runtime::AppContext;
13
14#[derive(Debug, Deserialize)]
15pub struct TaskFilterParams {
16 status: Option<String>,
17 limit: Option<u32>,
18}
19
20pub async fn list_tasks_by_context(
21 Extension(_req_ctx): Extension<RequestContext>,
22 State(app_context): State<AppContext>,
23 Path(context_id): Path<String>,
24) -> Result<impl IntoResponse, ApiError> {
25 tracing::debug!(context_id = %context_id, "Listing tasks");
26
27 let task_repo = TaskRepository::new(app_context.db_pool().clone());
28
29 let context_id_typed = ContextId::new(&context_id);
30 let tasks = task_repo
31 .list_tasks_by_context(&context_id_typed)
32 .await
33 .map_err(|e| {
34 tracing::error!(error = %e, "Failed to list tasks");
35 ApiError::internal_error("Failed to retrieve tasks")
36 })?;
37
38 tracing::debug!(context_id = %context_id, count = %tasks.len(), "Tasks listed");
39 Ok((StatusCode::OK, Json(tasks)))
40}
41
42pub async fn get_task(
43 Extension(_req_ctx): Extension<RequestContext>,
44 State(app_context): State<AppContext>,
45 Path(task_id): Path<String>,
46) -> Result<impl IntoResponse, ApiError> {
47 tracing::debug!(task_id = %task_id, "Retrieving task");
48
49 let task_repo = TaskRepository::new(app_context.db_pool().clone());
50
51 let task_id_typed = TaskId::new(&task_id);
52 match task_repo.get_task(&task_id_typed).await {
53 Ok(Some(task)) => {
54 tracing::debug!("Task retrieved successfully");
55 Ok((StatusCode::OK, Json(task)).into_response())
56 },
57 Ok(None) => {
58 tracing::debug!("Task not found");
59 Err(ApiError::not_found(format!("Task '{}' not found", task_id)))
60 },
61 Err(e) => {
62 tracing::error!(error = %e, "Failed to retrieve task");
63 Err(ApiError::internal_error("Failed to retrieve task"))
64 },
65 }
66}
67
68pub async fn list_tasks_by_user(
69 Extension(req_ctx): Extension<RequestContext>,
70 State(app_context): State<AppContext>,
71 Query(params): Query<TaskFilterParams>,
72) -> Result<impl IntoResponse, ApiError> {
73 let user_id = req_ctx.auth.user_id.as_str();
74
75 tracing::debug!(user_id = %user_id, "Listing tasks");
76
77 let task_repo = TaskRepository::new(app_context.db_pool().clone());
78
79 let task_state = params.status.as_ref().and_then(|s| match s.as_str() {
80 "submitted" => Some(TaskState::Submitted),
81 "working" => Some(TaskState::Working),
82 "input-required" => Some(TaskState::InputRequired),
83 "completed" => Some(TaskState::Completed),
84 "canceled" | "cancelled" => Some(TaskState::Canceled),
85 "failed" => Some(TaskState::Failed),
86 "rejected" => Some(TaskState::Rejected),
87 "auth-required" => Some(TaskState::AuthRequired),
88 _ => None,
89 });
90
91 let user_id_typed = UserId::new(user_id);
92 let mut tasks = task_repo
93 .get_tasks_by_user_id(&user_id_typed, params.limit.map(|l| l as i32), None)
94 .await
95 .map_err(|e| {
96 tracing::error!(error = %e, "Failed to list tasks");
97 ApiError::internal_error("Failed to retrieve tasks")
98 })?;
99
100 if let Some(state) = task_state {
101 tasks.retain(|t| t.status.state == state);
102 }
103
104 tracing::debug!(user_id = %user_id, count = %tasks.len(), "Tasks listed");
105 Ok((StatusCode::OK, Json(tasks)))
106}
107
108pub async fn get_messages_by_task(
109 Extension(_req_ctx): Extension<RequestContext>,
110 State(app_context): State<AppContext>,
111 Path(task_id): Path<String>,
112) -> Result<impl IntoResponse, ApiError> {
113 tracing::debug!(task_id = %task_id, "Retrieving messages");
114
115 let task_repo = TaskRepository::new(app_context.db_pool().clone());
116
117 let task_id_typed = TaskId::new(&task_id);
118 let messages = task_repo
119 .get_messages_by_task(&task_id_typed)
120 .await
121 .map_err(|e| {
122 tracing::error!(error = %e, "Failed to retrieve messages");
123 ApiError::internal_error("Failed to retrieve messages")
124 })?;
125
126 tracing::debug!(task_id = %task_id, count = %messages.len(), "Messages retrieved");
127 Ok((StatusCode::OK, Json(messages)))
128}
129
130pub async fn delete_task(
131 Extension(_req_ctx): Extension<RequestContext>,
132 State(app_context): State<AppContext>,
133 Path(task_id): Path<String>,
134) -> Result<impl IntoResponse, ApiError> {
135 tracing::debug!(task_id = %task_id, "Deleting task");
136
137 let task_repo = TaskRepository::new(app_context.db_pool().clone());
138
139 let task_id_typed = TaskId::new(&task_id);
140 task_repo.delete_task(&task_id_typed).await.map_err(|e| {
141 tracing::error!(error = %e, "Failed to delete task");
142 ApiError::internal_error("Failed to delete task")
143 })?;
144
145 tracing::debug!(task_id = %task_id, "Task deleted");
146 Ok(StatusCode::NO_CONTENT)
147}