collective

Browse / github.com/storytold/artcraft / patch

Investigate Sora client latency logs (#1069)

Investigate Sora client latency logs (#1069) ## Summary - On ArtCraft Sora (`sora_2` / `sora_2_pro`) task success or failure, log structured client elapsed ms with model, plan slug, and inference job token. - No poll-interval or generation behavior changes. Companion backend metrics PR is in `artcraft-services` on the same branch name. Tracks [#1069](https://github.com/storytold/artcraft/issues/1069). **Investigation only** — fix PR deferred until admin reviews Datadog data. ## Test plan - [ ] Complete (or fail) a Sora ArtCraft video task and confirm the structured `info` log line - [ ] Confirm non-Sora video completions are unchanged

Status
open
Score
0.00
Adopters
0 other operators
Author
giorgioabbruscato (imported from pull request #2282, open)
Base
d4e4ac69bbd93d237eaf5d0260bc6a5ff3a5e373
Size
+84 −1 in 5 files
Review
pending
Shared
2026-10-28
Id
bc892f2d4e6bd4d102547f8c

How it went

No other operator has reported using it yet.

Diff

raw · crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_failed_job.rs crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_successful_job.rs crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/mod.rs crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/sora_latency_client_log.rs crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/storyteller_task_polling_thread.rs

diff --git a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_failed_job.rs b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_failed_job.rs--- a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_failed_job.rs+++ b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_failed_job.rs@@ -1,10 +1,13 @@ use crate::core::events::generation_events::common::GenerationModel; use crate::core::events::basic_sendable_event_trait::BasicSendableEvent; use crate::core::events::generation_events::generation_failed_event::GenerationFailedEvent;+use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::task_database::TaskDatabase; use crate::core::utils::enum_conversion::generation_provider::to_generation_service_provider; use crate::core::utils::enum_conversion::task_type::to_generation_action;+use super::sora_latency_client_log::maybe_log_sora_client_completion; use artcraft_api_defs::jobs::list_session_jobs::ListSessionJobsItem;+use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet; use enums::tauri::tasks::task_status::TaskStatus; use errors::AnyhowResult; use log::info;@@ -16,6 +19,8 @@ use sqlite_tasks::queries::update_task_status_with_rich_failure::{update_task_st  pub async fn handle_failed_job(   app_handle: &AppHandle,+  app_env_configs: &AppEnvConfigs,+  maybe_creds: Option<&StorytellerCredentialSet>,   job: &ListSessionJobsItem,   task: &Task,   task_database: &TaskDatabase,@@ -37,6 +42,8 @@ pub async fn handle_failed_job(     maybe_failure_message,   }).await?; +  maybe_log_sora_client_completion(app_env_configs, maybe_creds, job, task, "failure").await;+   let service = to_generation_service_provider(task.provider);   let action = to_generation_action(task.task_type); diff --git a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_successful_job.rs b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_successful_job.rs--- a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_successful_job.rs+++ b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/handle_storyteller_successful_job.rs@@ -8,6 +8,7 @@ use crate::core::utils::enum_conversion::generation_provider::to_generation_serv use crate::core::utils::enum_conversion::task_type::to_generation_action; use crate::core::utils::auto_download::{auto_download_task, clear_auto_download_checkpoint}; use super::events::maybe_handle_frontend_caller_notification::maybe_handle_frontend_caller_notification;+use super::sora_latency_client_log::maybe_log_sora_client_completion; use artcraft_api_defs::jobs::list_session_jobs::ListSessionJobsItem; use artcraft_api_defs::utils::media_links_to_thumbnail_template::media_links_to_thumbnail_template; use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;@@ -62,6 +63,8 @@ pub async fn handle_successful_job(   clear_auto_download_checkpoint(app_handle, task);   send_additional_success_events(app_handle, app_env_configs, creds, job, task).await; +  maybe_log_sora_client_completion(app_env_configs, creds, job, task, "success").await;+   let service = to_generation_service_provider(task.provider);   let action = to_generation_action(task.task_type); diff --git a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/mod.rs b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/mod.rs--- a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/mod.rs+++ b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/mod.rs@@ -1,4 +1,5 @@ mod events; mod handle_storyteller_failed_job; mod handle_storyteller_successful_job;+mod sora_latency_client_log; pub mod storyteller_task_polling_thread;diff --git a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/sora_latency_client_log.rs b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/sora_latency_client_log.rsnew file mode 100644--- /dev/null+++ b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/sora_latency_client_log.rs@@ -0,0 +1,72 @@+//! Client-side wall-clock log for Sora ArtCraft jobs (#1069 investigation).+//! No behavior change — structured `info!` only.++use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;+use artcraft_api_defs::jobs::list_session_jobs::ListSessionJobsItem;+use artcraft_api_defs::subscriptions::get_session_subscription::GetSessionSubscriptionResponse;+use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;+use artcraft_client::endpoints::subscriptions::get_session_subscription::get_session_subscription;+use chrono::Utc;+use enums::common::payments_namespace::PaymentsNamespace;+use enums::tauri::tasks::task_model_type::TaskModelType;+use log::{info, warn};+use sqlite_tasks::queries::task::Task;++fn is_sora_model(model: Option<&TaskModelType>) -> Option<&'static str> {+  match model {+    Some(TaskModelType::Sora2) => Some("sora_2"),+    Some(TaskModelType::Sora2Pro) => Some("sora_2_pro"),+    _ => None,+  }+}++fn plan_tag_from_subscription_response(response: &GetSessionSubscriptionResponse) -> &'static str {+  match response.active_subscription.as_ref().map(|s| s.product_slug.as_str()) {+    None => "free",+    Some("artcraft_basic") => "artcraft_basic",+    Some("artcraft_pro") => "artcraft_pro",+    Some("artcraft_max") => "artcraft_max",+    Some(_) => "unknown",+  }+}++/// Best-effort structured log when a Sora ArtCraft task completes locally.+pub async fn maybe_log_sora_client_completion(+  app_env_configs: &AppEnvConfigs,+  maybe_creds: Option<&StorytellerCredentialSet>,+  job: &ListSessionJobsItem,+  task: &Task,+  outcome: &str,+) {+  let Some(model) = is_sora_model(task.model_type.as_ref()) else {+    return;+  };++  let plan = match get_session_subscription(+    &app_env_configs.storyteller_host,+    maybe_creds,+    PaymentsNamespace::Artcraft,+  )+  .await+  {+    Ok(response) => plan_tag_from_subscription_response(&response),+    Err(err) => {+      warn!(+        "Sora latency client log: subscription lookup failed (using unknown): {:?}",+        err+      );+      "unknown"+    }+  };++  let elapsed_ms = (Utc::now() - job.created_at).num_milliseconds().max(0);+  let job_token = task+    .provider_job_id+    .as_deref()+    .unwrap_or(job.job_token.as_str());++  info!(+    "sora_latency_client model={} plan={} outcome={} client_elapsed_ms={} inference_job_token={}",+    model, plan, outcome, elapsed_ms, job_token+  );+}diff --git a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/storyteller_task_polling_thread.rs b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/storyteller_task_polling_thread.rs--- a/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/storyteller_task_polling_thread.rs+++ b/crates/desktop/artcraft/src/services/storyteller/threads/storyteller_task_polling_thread/storyteller_task_polling_thread.rs@@ -119,7 +119,7 @@ async fn polling_loop(             TaskStatus::CompleteFailure => continue, // NB: We're done with this task.             _ => {}           }-          handle_failed_job(app_handle, job, task, task_database).await?;+          handle_failed_job(app_handle, app_env_configs, creds.as_ref(), job, task, task_database).await?;         }         _ => continue,       }