Task attempt 16132ff6-fd9b-4551-86b9-c6b161df4f57 - Final changes
This commit is contained in:
50
backend/.sqlx/query-bd57e47989627ac5e9f44e83f4bacff590d357586b149c7c17ff0e8f349e7025.json
generated
Normal file
50
backend/.sqlx/query-bd57e47989627ac5e9f44e83f4bacff590d357586b149c7c17ff0e8f349e7025.json
generated
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT \n taa.id as \"activity_id!: Uuid\",\n taa.execution_process_id as \"execution_process_id!: Uuid\",\n taa.status as \"status!: TaskAttemptStatus\",\n taa.note,\n taa.created_at as \"created_at!: DateTime<Utc>\",\n es.prompt\n FROM task_attempt_activities taa\n INNER JOIN execution_processes ep ON taa.execution_process_id = ep.id\n LEFT JOIN executor_sessions es ON es.execution_process_id = ep.id\n WHERE ep.task_attempt_id = $1\n ORDER BY taa.created_at ASC",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "activity_id!: Uuid",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "execution_process_id!: Uuid",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status!: TaskAttemptStatus",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "note",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created_at!: DateTime<Utc>",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "prompt",
|
||||||
|
"ordinal": 5,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "bd57e47989627ac5e9f44e83f4bacff590d357586b149c7c17ff0e8f349e7025"
|
||||||
|
}
|
||||||
@@ -24,6 +24,17 @@ pub struct CreateTaskAttemptActivity {
|
|||||||
pub note: Option<String>,
|
pub note: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct TaskAttemptActivityWithPrompt {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub execution_process_id: Uuid,
|
||||||
|
pub status: TaskAttemptStatus,
|
||||||
|
pub note: Option<String>,
|
||||||
|
pub created_at: DateTime<Utc>,
|
||||||
|
pub prompt: Option<String>, // From executor_session
|
||||||
|
}
|
||||||
|
|
||||||
impl TaskAttemptActivity {
|
impl TaskAttemptActivity {
|
||||||
pub async fn find_by_execution_process_id(
|
pub async fn find_by_execution_process_id(
|
||||||
pool: &SqlitePool,
|
pool: &SqlitePool,
|
||||||
@@ -84,4 +95,40 @@ impl TaskAttemptActivity {
|
|||||||
|
|
||||||
Ok(records.into_iter().map(|r| r.id).collect())
|
Ok(records.into_iter().map(|r| r.id).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find activities for all execution processes in a task attempt, with executor session prompts
|
||||||
|
pub async fn find_with_prompts_by_task_attempt_id(
|
||||||
|
pool: &SqlitePool,
|
||||||
|
task_attempt_id: Uuid,
|
||||||
|
) -> Result<Vec<TaskAttemptActivityWithPrompt>, sqlx::Error> {
|
||||||
|
let records = sqlx::query!(
|
||||||
|
r#"SELECT
|
||||||
|
taa.id as "activity_id!: Uuid",
|
||||||
|
taa.execution_process_id as "execution_process_id!: Uuid",
|
||||||
|
taa.status as "status!: TaskAttemptStatus",
|
||||||
|
taa.note,
|
||||||
|
taa.created_at as "created_at!: DateTime<Utc>",
|
||||||
|
es.prompt
|
||||||
|
FROM task_attempt_activities taa
|
||||||
|
INNER JOIN execution_processes ep ON taa.execution_process_id = ep.id
|
||||||
|
LEFT JOIN executor_sessions es ON es.execution_process_id = ep.id
|
||||||
|
WHERE ep.task_attempt_id = $1
|
||||||
|
ORDER BY taa.created_at ASC"#,
|
||||||
|
task_attempt_id
|
||||||
|
)
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(records
|
||||||
|
.into_iter()
|
||||||
|
.map(|record| TaskAttemptActivityWithPrompt {
|
||||||
|
id: record.activity_id,
|
||||||
|
execution_process_id: record.execution_process_id,
|
||||||
|
status: record.status,
|
||||||
|
note: record.note,
|
||||||
|
created_at: record.created_at,
|
||||||
|
prompt: record.prompt,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::models::{
|
|||||||
BranchStatus, CreateFollowUpAttempt, CreateTaskAttempt, TaskAttempt, TaskAttemptStatus,
|
BranchStatus, CreateFollowUpAttempt, CreateTaskAttempt, TaskAttempt, TaskAttemptStatus,
|
||||||
WorktreeDiff,
|
WorktreeDiff,
|
||||||
},
|
},
|
||||||
task_attempt_activity::{CreateTaskAttemptActivity, TaskAttemptActivity},
|
task_attempt_activity::{CreateTaskAttemptActivity, TaskAttemptActivity, TaskAttemptActivityWithPrompt},
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ pub async fn get_task_attempts(
|
|||||||
pub async fn get_task_attempt_activities(
|
pub async fn get_task_attempt_activities(
|
||||||
Path((project_id, task_id, attempt_id)): Path<(Uuid, Uuid, Uuid)>,
|
Path((project_id, task_id, attempt_id)): Path<(Uuid, Uuid, Uuid)>,
|
||||||
Extension(pool): Extension<SqlitePool>,
|
Extension(pool): Extension<SqlitePool>,
|
||||||
) -> Result<ResponseJson<ApiResponse<Vec<TaskAttemptActivity>>>, StatusCode> {
|
) -> Result<ResponseJson<ApiResponse<Vec<TaskAttemptActivityWithPrompt>>>, StatusCode> {
|
||||||
// Verify task attempt exists and belongs to the correct task
|
// Verify task attempt exists and belongs to the correct task
|
||||||
match TaskAttempt::exists_for_task(&pool, attempt_id, task_id, project_id).await {
|
match TaskAttempt::exists_for_task(&pool, attempt_id, task_id, project_id).await {
|
||||||
Ok(false) => return Err(StatusCode::NOT_FOUND),
|
Ok(false) => return Err(StatusCode::NOT_FOUND),
|
||||||
@@ -62,40 +62,8 @@ pub async fn get_task_attempt_activities(
|
|||||||
Ok(true) => {}
|
Ok(true) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all execution processes for the task attempt
|
// Get activities with prompts for the task attempt
|
||||||
let execution_processes =
|
match TaskAttemptActivity::find_with_prompts_by_task_attempt_id(&pool, attempt_id).await {
|
||||||
match ExecutionProcess::find_by_task_attempt_id(&pool, attempt_id).await {
|
|
||||||
Ok(processes) => processes,
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!(
|
|
||||||
"Failed to fetch execution processes for attempt {}: {}",
|
|
||||||
attempt_id,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get activities for all execution processes
|
|
||||||
let mut all_activities = Vec::new();
|
|
||||||
for process in execution_processes {
|
|
||||||
match TaskAttemptActivity::find_by_execution_process_id(&pool, process.id).await {
|
|
||||||
Ok(mut activities) => all_activities.append(&mut activities),
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!(
|
|
||||||
"Failed to fetch activities for execution process {}: {}",
|
|
||||||
process.id,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort activities by created_at
|
|
||||||
all_activities.sort_by(|a, b| a.created_at.cmp(&b.created_at));
|
|
||||||
|
|
||||||
match Ok::<Vec<TaskAttemptActivity>, sqlx::Error>(all_activities) {
|
|
||||||
Ok(activities) => Ok(ResponseJson(ApiResponse {
|
Ok(activities) => Ok(ResponseJson(ApiResponse {
|
||||||
success: true,
|
success: true,
|
||||||
data: Some(activities),
|
data: Some(activities),
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import type {
|
|||||||
TaskStatus,
|
TaskStatus,
|
||||||
TaskAttempt,
|
TaskAttempt,
|
||||||
TaskAttemptActivity,
|
TaskAttemptActivity,
|
||||||
|
TaskAttemptActivityWithPrompt,
|
||||||
TaskAttemptStatus,
|
TaskAttemptStatus,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
TaskWithAttemptStatus,
|
TaskWithAttemptStatus,
|
||||||
@@ -150,7 +151,7 @@ export function TaskDetailsPanel({
|
|||||||
);
|
);
|
||||||
// Combined attempt data state
|
// Combined attempt data state
|
||||||
const [attemptData, setAttemptData] = useState<{
|
const [attemptData, setAttemptData] = useState<{
|
||||||
activities: TaskAttemptActivity[];
|
activities: TaskAttemptActivityWithPrompt[];
|
||||||
processes: ExecutionProcessSummary[];
|
processes: ExecutionProcessSummary[];
|
||||||
runningProcessDetails: Record<string, ExecutionProcess>;
|
runningProcessDetails: Record<string, ExecutionProcess>;
|
||||||
}>({
|
}>({
|
||||||
@@ -421,7 +422,7 @@ export function TaskDetailsPanel({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (activitiesResponse.ok && processesResponse.ok) {
|
if (activitiesResponse.ok && processesResponse.ok) {
|
||||||
const activitiesResult: ApiResponse<TaskAttemptActivity[]> =
|
const activitiesResult: ApiResponse<TaskAttemptActivityWithPrompt[]> =
|
||||||
await activitiesResponse.json();
|
await activitiesResponse.json();
|
||||||
const processesResult: ApiResponse<ExecutionProcessSummary[]> =
|
const processesResult: ApiResponse<ExecutionProcessSummary[]> =
|
||||||
await processesResponse.json();
|
await processesResponse.json();
|
||||||
@@ -1160,7 +1161,30 @@ export function TaskDetailsPanel({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Show stdio output for running processes */}
|
{/* Show prompt for coding agent executions */}
|
||||||
|
{activity.prompt &&
|
||||||
|
(activity.status === "setuprunning" ||
|
||||||
|
activity.status === "setupcomplete" ||
|
||||||
|
activity.status === "setupfailed" ||
|
||||||
|
activity.status === "executorrunning" ||
|
||||||
|
activity.status === "executorcomplete" ||
|
||||||
|
activity.status === "executorfailed") && (
|
||||||
|
<div className="mt-2 mb-4">
|
||||||
|
<div className="p-3 bg-blue-50 dark:bg-blue-950/30 rounded-md border border-blue-200 dark:border-blue-800">
|
||||||
|
<div className="flex items-start gap-2 mb-2">
|
||||||
|
<Code className="h-4 w-4 text-blue-600 dark:text-blue-400 mt-0.5" />
|
||||||
|
<span className="text-sm font-medium text-blue-900 dark:text-blue-100">
|
||||||
|
Prompt
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<pre className="text-sm text-blue-800 dark:text-blue-200 whitespace-pre-wrap break-words">
|
||||||
|
{activity.prompt}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Show stdio output for running processes */}
|
||||||
{(activity.status === "setuprunning" ||
|
{(activity.status === "setuprunning" ||
|
||||||
activity.status === "executorrunning") &&
|
activity.status === "executorrunning") &&
|
||||||
attemptData.runningProcessDetails[
|
attemptData.runningProcessDetails[
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ export type TaskAttemptActivity = { id: string, execution_process_id: string, st
|
|||||||
|
|
||||||
export type CreateTaskAttemptActivity = { execution_process_id: string, status: TaskAttemptStatus | null, note: string | null, };
|
export type CreateTaskAttemptActivity = { execution_process_id: string, status: TaskAttemptStatus | null, note: string | null, };
|
||||||
|
|
||||||
|
export type TaskAttemptActivityWithPrompt = { id: string, execution_process_id: string, status: TaskAttemptStatus, note: string | null, created_at: string, prompt: string | null, };
|
||||||
|
|
||||||
export type DirectoryEntry = { name: string, path: string, is_directory: boolean, is_git_repo: boolean, };
|
export type DirectoryEntry = { name: string, path: string, is_directory: boolean, is_git_repo: boolean, };
|
||||||
|
|
||||||
export type DiffChunkType = "Equal" | "Insert" | "Delete";
|
export type DiffChunkType = "Equal" | "Insert" | "Delete";
|
||||||
|
|||||||
Reference in New Issue
Block a user