Files
Aether/crates/aether-video-tasks-core/src/snapshot.rs
T
stabeyandClaude Opus 5 04c4a97766 feat(xai): add native image and video endpoints
Expose the xAI Imagine image and video surfaces on top of the `xai`
provider, and make the shared OpenAI video-task layer survive the
production configuration they need.

Native video requests live under /v1 (generations, edits, extensions,
with /v1/videos as a creation alias that only selects xAI candidates);
the OpenAI-compatible adapter stays under /openai/v1/videos and maps
`seconds` / `size` onto numeric duration, aspect ratio and resolution.
Clients receive an opaque Aether task ID scoped to the owning user;
polling uses the upstream task ID and the original credential, and
completed downloads fetch the returned media URL without forwarding
provider authorization to the media host.

Three fixes to the shared video layer are required for this to work
outside tests:

- OpenAI/xAI task persistence now supplies a stable 16-character
  short_id, which the PostgreSQL schema requires. Existing rows keep
  their original value across reconstruction, so no schema change or
  historical rewrite is needed.
- Task retrieval and content downloads are admitted by the production
  GET execution gate, and reconstructed tasks resolve proxy nodes,
  system proxy defaults, tunnel affinity and transport profiles through
  the same deployment resolver used for creation. A configured proxy
  route no longer silently becomes a direct request after restart.
- When the gateway also serves the frontend, /openai/v1/videos and its
  subpaths bypass the static SPA handler. Otherwise a video query
  returns HTTP 200 with text/html instead of the task JSON.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-14 21:17:21 +08:00

251 lines
10 KiB
Rust

use aether_data_contracts::repository::video_tasks::{StoredVideoTask, UpsertVideoTask};
use serde_json::{json, Map, Value};
use crate::transport::{gemini_metadata_video_url, gemini_video_metadata};
use crate::types::sanitize_video_task_error_code;
use crate::{
local_status_from_stored, non_empty_owned, request_body_string, GeminiVideoTaskSeed,
LocalVideoTaskPersistence, LocalVideoTaskReadResponse, LocalVideoTaskSnapshot,
LocalVideoTaskStatus, LocalVideoTaskTransport, OpenAiVideoTaskSeed,
};
impl LocalVideoTaskSnapshot {
pub fn to_upsert_record(&self) -> UpsertVideoTask {
match self {
Self::OpenAi(seed) => seed.to_upsert_record(),
Self::Gemini(seed) => seed.to_upsert_record(),
}
}
pub fn from_stored_task(task: &StoredVideoTask) -> Option<Self> {
let mut snapshot = task
.request_metadata
.as_ref()
.and_then(|metadata| metadata.get("rust_local_snapshot"))
.cloned()
.and_then(|value| serde_json::from_value::<LocalVideoTaskSnapshot>(value).ok())?;
// The row is the ownership source of truth. Older embedded snapshots can
// contain stale identity fields after a task import or repair.
match &mut snapshot {
Self::OpenAi(seed) => {
seed.local_short_id = task.short_id.clone();
seed.user_id = task.user_id.clone();
seed.api_key_id = task.api_key_id.clone();
}
Self::Gemini(seed) => {
seed.user_id = task.user_id.clone();
seed.api_key_id = task.api_key_id.clone();
}
}
Some(snapshot)
}
pub fn from_stored_task_with_transport(
task: &StoredVideoTask,
transport: LocalVideoTaskTransport,
) -> Option<Self> {
let provider_api_format = task.provider_api_format.as_deref()?.trim();
let persistence = LocalVideoTaskPersistence::from_stored_task(task)?;
match provider_api_format {
"openai:video" => {
let upstream_task_id = non_empty_owned(task.external_task_id.as_ref())?;
Some(Self::OpenAi(OpenAiVideoTaskSeed {
local_short_id: task.short_id.clone(),
native_response: None,
xai_provider: persistence.client_api_format == "xai:video",
local_task_id: task.id.clone(),
upstream_task_id,
created_at_unix_ms: task.created_at_unix_ms,
user_id: task.user_id.clone(),
api_key_id: task.api_key_id.clone(),
model: non_empty_owned(task.model.as_ref()),
prompt: non_empty_owned(task.prompt.as_ref()).or_else(|| {
request_body_string(&persistence.original_request_body, "prompt")
}),
size: non_empty_owned(task.size.as_ref()).or_else(|| {
request_body_string(&persistence.original_request_body, "size")
}),
seconds: task
.duration_seconds
.map(|value| value.to_string())
.or_else(|| {
request_body_string(&persistence.original_request_body, "seconds")
}),
remixed_from_video_id: request_body_string(
&persistence.original_request_body,
"remix_video_id",
)
.or_else(|| {
request_body_string(
&persistence.original_request_body,
"remixed_from_video_id",
)
}),
status: local_status_from_stored(task.status),
progress_percent: task.progress_percent,
completed_at_unix_secs: task.completed_at_unix_secs,
expires_at_unix_secs: None,
error_code: task.error_code.clone(),
error_message: task.error_message.clone(),
video_url: non_empty_owned(task.video_url.as_ref()),
persistence,
transport,
}))
}
"gemini:video" => {
let local_short_id =
non_empty_owned(task.short_id.as_ref()).unwrap_or_else(|| task.id.clone());
let upstream_operation_name = non_empty_owned(task.external_task_id.as_ref())?;
let model = non_empty_owned(task.model.as_ref())?;
Some(Self::Gemini(GeminiVideoTaskSeed {
local_short_id,
upstream_operation_name,
user_id: task.user_id.clone(),
api_key_id: task.api_key_id.clone(),
model,
status: local_status_from_stored(task.status),
progress_percent: task.progress_percent,
error_code: task.error_code.clone(),
error_message: task.error_message.clone(),
metadata: gemini_video_metadata(task.video_url.as_deref()),
persistence,
transport,
}))
}
_ => None,
}
}
pub(crate) fn sanitize_persisted_diagnostics(&mut self) -> bool {
match self {
Self::OpenAi(seed) => {
let previous_error_code = seed.error_code.clone();
let error_code =
sanitized_error_code_for_status(seed.status, seed.error_code.take());
let changed = previous_error_code != error_code || seed.error_message.is_some();
seed.error_code = error_code;
seed.error_message = None;
changed
}
Self::Gemini(seed) => {
let previous_error_code = seed.error_code.clone();
let error_code =
sanitized_error_code_for_status(seed.status, seed.error_code.take());
let safe_metadata =
gemini_video_metadata(gemini_metadata_video_url(&seed.metadata).as_deref());
let changed = previous_error_code != error_code
|| seed.error_message.is_some()
|| seed.metadata != safe_metadata;
seed.error_code = error_code;
seed.error_message = None;
seed.metadata = safe_metadata;
changed
}
}
}
pub fn read_response_for_path(&self, path: &str) -> LocalVideoTaskReadResponse {
if let Self::OpenAi(seed) = self {
let mut seed = seed.clone();
if path.starts_with("/openai/v1/videos/") {
seed.persistence.client_api_format = "openai:video".to_string();
} else if path.starts_with("/v1/videos/") && seed.uses_xai_provider() {
seed.persistence.client_api_format = "xai:video".to_string();
}
return Self::OpenAi(seed).read_response();
}
self.read_response()
}
pub fn read_response(&self) -> LocalVideoTaskReadResponse {
match self {
Self::OpenAi(seed) => match seed.status {
LocalVideoTaskStatus::Cancelled => LocalVideoTaskReadResponse {
status_code: 404,
body_json: json!({"detail": "Video task was cancelled"}),
},
LocalVideoTaskStatus::Deleted => LocalVideoTaskReadResponse {
status_code: 404,
body_json: json!({"detail": "Video task not found"}),
},
_ => LocalVideoTaskReadResponse {
status_code: 200,
body_json: seed.client_body_json(),
},
},
Self::Gemini(seed) => match seed.status {
LocalVideoTaskStatus::Cancelled => LocalVideoTaskReadResponse {
status_code: 404,
body_json: json!({"detail": "Video task was cancelled"}),
},
LocalVideoTaskStatus::Deleted => LocalVideoTaskReadResponse {
status_code: 404,
body_json: json!({"detail": "Video task not found"}),
},
_ => LocalVideoTaskReadResponse {
status_code: 200,
body_json: seed.client_body_json(),
},
},
}
}
pub fn belongs_to_user(&self, user_id: &str) -> bool {
let user_id = user_id.trim();
if user_id.is_empty() {
return false;
}
let owner = match self {
Self::OpenAi(seed) => seed.user_id.as_deref(),
Self::Gemini(seed) => seed.user_id.as_deref(),
};
owner.map(str::trim) == Some(user_id)
}
pub fn is_active_for_refresh(&self) -> bool {
match self {
Self::OpenAi(seed) => matches!(
seed.status,
LocalVideoTaskStatus::Submitted
| LocalVideoTaskStatus::Queued
| LocalVideoTaskStatus::Processing
),
Self::Gemini(seed) => matches!(
seed.status,
LocalVideoTaskStatus::Submitted
| LocalVideoTaskStatus::Queued
| LocalVideoTaskStatus::Processing
),
}
}
pub fn apply_provider_body(&mut self, provider_body: &Map<String, Value>) {
match self {
Self::OpenAi(seed) => seed.apply_provider_body(provider_body),
Self::Gemini(seed) => seed.apply_provider_body(provider_body),
}
}
pub fn provider_name(&self) -> Option<&str> {
match self {
Self::OpenAi(seed) => seed.transport.provider_name.as_deref(),
Self::Gemini(seed) => seed.transport.provider_name.as_deref(),
}
}
}
fn sanitized_error_code_for_status(
status: LocalVideoTaskStatus,
error_code: Option<String>,
) -> Option<String> {
match status {
LocalVideoTaskStatus::Failed => sanitize_video_task_error_code(error_code)
.or_else(|| Some("provider_error".to_string())),
LocalVideoTaskStatus::Expired => Some("expired".to_string()),
LocalVideoTaskStatus::Cancelled => Some("cancelled".to_string()),
_ => None,
}
}