LLM call logging, plan persistence API, quote-to-feedback UX, requirement input improvements
- Add llm_call_log table and per-call timing/token tracking in agent loop
- New GET /workflows/{id}/plan endpoint to restore plan from snapshots on page load
- New GET /workflows/{id}/llm-calls endpoint + WS LlmCallLog broadcast
- Parse Usage from LLM API response (prompt_tokens, completion_tokens)
- Detailed mode toggle in execution log showing LLM call cards with phase/tokens/latency
- Quote-to-feedback: hover quote buttons on plan steps and log entries, multi-quote chips in comment input
- Requirement input: larger textarea, multi-line display with pre-wrap and scroll
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,8 +8,9 @@ use axum::{
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use crate::AppState;
|
||||
use crate::agent::AgentEvent;
|
||||
use crate::db::{Workflow, ExecutionLogEntry, Comment};
|
||||
use crate::agent::{AgentEvent, PlanStepInfo};
|
||||
use crate::db::{Workflow, ExecutionLogEntry, Comment, LlmCallLogEntry};
|
||||
use crate::state::AgentState;
|
||||
use super::{ApiResult, db_err};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
@@ -33,6 +34,8 @@ pub fn router(state: Arc<AppState>) -> Router {
|
||||
.route("/workflows/{id}/steps", get(list_steps))
|
||||
.route("/workflows/{id}/comments", get(list_comments).post(create_comment))
|
||||
.route("/workflows/{id}/report", get(get_report))
|
||||
.route("/workflows/{id}/plan", get(get_plan))
|
||||
.route("/workflows/{id}/llm-calls", get(list_llm_calls))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -153,3 +156,38 @@ async fn get_report(
|
||||
None => Err((StatusCode::NOT_FOUND, "Workflow not found").into_response()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_plan(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(workflow_id): Path<String>,
|
||||
) -> ApiResult<Vec<PlanStepInfo>> {
|
||||
let snapshot_json: Option<String> = sqlx::query_scalar(
|
||||
"SELECT state_json FROM agent_state_snapshots WHERE workflow_id = ? ORDER BY created_at DESC LIMIT 1"
|
||||
)
|
||||
.bind(&workflow_id)
|
||||
.fetch_optional(&state.db.pool)
|
||||
.await
|
||||
.map_err(db_err)?;
|
||||
|
||||
if let Some(json) = snapshot_json {
|
||||
if let Ok(agent_state) = serde_json::from_str::<AgentState>(&json) {
|
||||
return Ok(Json(crate::agent::plan_infos_from_state(&agent_state)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Json(vec![]))
|
||||
}
|
||||
|
||||
async fn list_llm_calls(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(workflow_id): Path<String>,
|
||||
) -> ApiResult<Vec<LlmCallLogEntry>> {
|
||||
sqlx::query_as::<_, LlmCallLogEntry>(
|
||||
"SELECT * FROM llm_call_log WHERE workflow_id = ? ORDER BY created_at"
|
||||
)
|
||||
.bind(&workflow_id)
|
||||
.fetch_all(&state.db.pool)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(db_err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user