(
store: Arc<ArticleRequestStore>,
config: ArticleRequestWorkerConfig,
email_notifier: Option<Arc<EmailNotifier>>,
request_id: &str,
)
| 180 | } |
| 181 | |
| 182 | async fn process_one_request( |
| 183 | store: Arc<ArticleRequestStore>, |
| 184 | config: ArticleRequestWorkerConfig, |
| 185 | email_notifier: Option<Arc<EmailNotifier>>, |
| 186 | request_id: &str, |
| 187 | ) -> Result<()> { |
| 188 | let request = match store.get_request(request_id).await? { |
| 189 | Some(w) => w, |
| 190 | None => { |
| 191 | tracing::warn!("article request worker skipped missing request {request_id}"); |
| 192 | return Ok(()); |
| 193 | }, |
| 194 | }; |
| 195 | |
| 196 | if request.status == REQUEST_STATUS_REJECTED || request.status == REQUEST_STATUS_DONE { |
| 197 | tracing::info!("article request worker skipped finalized request {request_id}"); |
| 198 | return Ok(()); |
| 199 | } |
| 200 | |
| 201 | if request.status == REQUEST_STATUS_APPROVED { |
| 202 | store |
| 203 | .transition_request(request_id, REQUEST_STATUS_RUNNING, None, None, None, None) |
| 204 | .await?; |
| 205 | } else if request.status != REQUEST_STATUS_RUNNING { |
| 206 | tracing::warn!( |
| 207 | "article request worker skipped request {request_id} with status {}", |
| 208 | request.status |
| 209 | ); |
| 210 | return Ok(()); |
| 211 | } |
| 212 | |
| 213 | let run_id = format!("arrun-{}-{}", request_id, chrono::Utc::now().timestamp_millis()); |
| 214 | if let Err(err) = store |
| 215 | .create_ai_run(NewArticleRequestAiRunInput { |
| 216 | run_id: run_id.clone(), |
| 217 | request_id: request_id.to_string(), |
| 218 | runner_program: config.runner_program.clone(), |
| 219 | }) |
| 220 | .await |
| 221 | { |
| 222 | let reason = format!("failed to create article request ai run: {err}"); |
| 223 | mark_request_failed(&store, request_id, &reason, None).await; |
| 224 | return Ok(()); |
| 225 | } |
| 226 | |
| 227 | let run_output = match run_request_runner(store.clone(), &config, &request, &run_id).await { |
| 228 | Ok(output) => output, |
| 229 | Err(err) => { |
| 230 | let reason = err.to_string(); |
| 231 | let is_timeout = reason.contains("timed out"); |
| 232 | |
| 233 | // On timeout, check if the result file was already written before giving up. |
| 234 | // The runner may have completed ingestion but exceeded the wall-clock limit |
| 235 | // during post-verification steps. |
| 236 | if is_timeout { |
| 237 | let result_path = build_result_file_path(&config.result_dir, request_id); |
| 238 | if let Ok(result) = read_runner_result(&result_path).await { |
| 239 | tracing::info!( |
no test coverage detected