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]>
This commit is contained in:
stabey
2026-09-14 21:17:21 +08:00
co-authored by Claude Opus 5
parent e83399db2f
commit 04c4a97766
42 changed files with 1880 additions and 164 deletions
+276 -14
View File
@@ -19,14 +19,17 @@ impl LocalVideoTaskSeed {
) -> Option<Self> {
let transport = LocalVideoTaskTransport::from_plan(plan)?;
let persistence = LocalVideoTaskPersistence::from_report_context(report_context, plan);
match report_kind {
let mut seed = match report_kind {
"openai_video_create_sync_finalize" => {
let upstream_id = provider_body.get("id").and_then(Value::as_str)?.trim();
if upstream_id.is_empty() {
return None;
}
let upstream_id = openai_video_provider_task_id(provider_body)?;
Some(Self::OpenAiCreate(OpenAiVideoTaskSeed {
local_short_id: None,
native_response: None,
xai_provider: report_context
.get("video_provider_xai")
.and_then(Value::as_bool)
.unwrap_or(false),
local_task_id: context_text(report_context, "local_task_id")
.unwrap_or_else(|| Uuid::new_v4().to_string()),
upstream_task_id: upstream_id.to_string(),
@@ -37,8 +40,12 @@ impl LocalVideoTaskSeed {
model: context_text(report_context, "model")
.or_else(|| request_body_text(report_context, "model")),
prompt: request_body_text(report_context, "prompt"),
size: request_body_text(report_context, "size"),
seconds: request_body_text(report_context, "seconds"),
size: context_text(report_context, "video_size")
.or_else(|| request_body_text(report_context, "size")),
seconds: context_u64(report_context, "video_duration")
.map(|v| v.to_string())
.or_else(|| request_body_text(report_context, "seconds"))
.or_else(|| request_body_text(report_context, "duration")),
remixed_from_video_id: None,
status: LocalVideoTaskStatus::Submitted,
progress_percent: 0,
@@ -52,12 +59,15 @@ impl LocalVideoTaskSeed {
}))
}
"openai_video_remix_sync_finalize" => {
let upstream_id = provider_body.get("id").and_then(Value::as_str)?.trim();
if upstream_id.is_empty() {
return None;
}
let upstream_id = openai_video_provider_task_id(provider_body)?;
Some(Self::OpenAiRemix(OpenAiVideoTaskSeed {
local_short_id: None,
native_response: None,
xai_provider: report_context
.get("video_provider_xai")
.and_then(Value::as_bool)
.unwrap_or(false),
local_task_id: context_text(report_context, "local_task_id")
.unwrap_or_else(|| Uuid::new_v4().to_string()),
upstream_task_id: upstream_id.to_string(),
@@ -68,8 +78,12 @@ impl LocalVideoTaskSeed {
model: context_text(report_context, "model")
.or_else(|| request_body_text(report_context, "model")),
prompt: request_body_text(report_context, "prompt"),
size: request_body_text(report_context, "size"),
seconds: request_body_text(report_context, "seconds"),
size: context_text(report_context, "video_size")
.or_else(|| request_body_text(report_context, "size")),
seconds: context_u64(report_context, "video_duration")
.map(|v| v.to_string())
.or_else(|| request_body_text(report_context, "seconds"))
.or_else(|| request_body_text(report_context, "duration")),
remixed_from_video_id: context_text(report_context, "task_id")
.or_else(|| request_body_text(report_context, "remix_video_id")),
status: LocalVideoTaskStatus::Submitted,
@@ -110,7 +124,11 @@ impl LocalVideoTaskSeed {
}))
}
_ => None,
}?;
if let Self::OpenAiCreate(task) | Self::OpenAiRemix(task) = &mut seed {
task.apply_provider_body(provider_body);
}
Some(seed)
}
pub fn success_report_kind(&self) -> &'static str {
@@ -144,12 +162,28 @@ impl LocalVideoTaskSeed {
pub fn client_body_json(&self) -> Value {
match self {
Self::OpenAiCreate(seed) | Self::OpenAiRemix(seed) => seed.client_body_json(),
Self::OpenAiCreate(seed) | Self::OpenAiRemix(seed) => {
if seed.is_xai_native() {
seed.native_create_body_json()
} else {
seed.client_body_json()
}
}
Self::GeminiCreate(seed) => seed.client_body_json(),
}
}
}
fn openai_video_provider_task_id(body: &Map<String, Value>) -> Option<&str> {
// xAI's OpenAI-compatible video creation returns request_id instead of id.
["id", "request_id"].into_iter().find_map(|field| {
body.get(field)
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
})
}
impl VideoTaskTruthSourceMode {
pub fn prepare_sync_success(
self,
@@ -353,6 +387,234 @@ mod tests {
resolve_local_sync_success_background_report_kind,
};
#[test]
fn xai_native_video_protocol_survives_persistence_and_preserves_provider_fields() {
use crate::{
LocalVideoTaskContentAction, LocalVideoTaskSnapshot, VideoTaskService,
VideoTaskTruthSourceMode,
};
let mut plan =
build_internal_finalize_video_plan("native-create", "openai:video", None).unwrap();
plan.url = "https://api.x.ai/v1/videos/generations".into();
plan.headers
.insert("authorization".into(), "Bearer test-key".into());
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
let context = json!({"local_task_id":"native-local", "user_id":"owner", "model":"grok-imagine-video", "video_client_protocol":"xai", "video_duration":6});
let success = service
.prepare_sync_success(
"openai_video_create_sync_finalize",
json!({"request_id":"native-upstream", "future_field":true})
.as_object()
.unwrap(),
context.as_object().unwrap(),
&plan,
)
.unwrap();
assert_eq!(
success.client_body_json(),
json!({"request_id":"native-local","future_field":true})
);
let mut snapshot = success.to_snapshot();
let body = json!({"status":"done","video":{"url":"https://vidgen.x.ai/video.mp4","duration":6,"respect_moderation":true},"future_field":[1,2]});
snapshot.apply_provider_body(body.as_object().unwrap());
assert_eq!(snapshot.read_response().body_json, body);
assert_eq!(
snapshot
.read_response_for_path("/openai/v1/videos/native-local")
.body_json["status"],
"completed"
);
let LocalVideoTaskSnapshot::OpenAi(seed) = &snapshot else {
panic!("openai task expected")
};
let Some(LocalVideoTaskContentAction::StreamPlan(download)) =
seed.build_content_stream_action(None, "download")
else {
panic!("download expected")
};
assert_eq!(download.url, "https://vidgen.x.ai/video.mp4");
assert!(download.headers.is_empty());
let stored = snapshot.to_upsert_record().into_stored();
assert!(stored.request_metadata.is_none());
assert_eq!(stored.client_api_format.as_deref(), Some("xai:video"));
let restored = LocalVideoTaskSnapshot::from_stored_task_with_transport(
&stored,
seed.transport.clone(),
)
.unwrap();
assert_eq!(restored.read_response().body_json["status"], "done");
service.record_snapshot(restored);
let poll = service
.prepare_read_refresh_sync_plan_for_user(
Some("openai"),
"/v1/videos/native-local",
"owner",
"poll",
)
.unwrap();
assert_eq!(poll.plan.url, "https://api.x.ai/v1/videos/native-upstream");
assert!(service
.prepare_read_refresh_sync_plan_for_user(
Some("openai"),
"/v1/videos/native-local",
"foreign",
"poll"
)
.is_none());
assert!(service.apply_read_refresh_projection(&poll, body.as_object().unwrap()));
assert_eq!(
service
.read_response_for_user(Some("openai"), "/v1/videos/native-local", "owner")
.unwrap()
.body_json,
body
);
}
#[test]
fn xai_video_lifecycle_creates_polls_persists_and_downloads() {
use crate::{
LocalVideoTaskContentAction, LocalVideoTaskSnapshot, VideoTaskService,
VideoTaskTruthSourceMode,
};
for api_root in ["https://cli-chat-proxy.grok.com/v1", "https://api.x.ai/v1"] {
let mut plan =
build_internal_finalize_video_plan("xai-create", "openai:video", None).unwrap();
plan.url = format!("{api_root}/videos/generations");
plan.headers
.insert("authorization".into(), "Bearer test-token".into());
let service = VideoTaskService::new(VideoTaskTruthSourceMode::RustAuthoritative);
let context = json!({"local_task_id": "local-video", "model": "grok-imagine-video", "original_request_body": {"prompt": "A cat", "seconds": "6"}});
let success = service
.prepare_sync_success(
"openai_video_create_sync_finalize",
json!({"request_id": "xai-request"}).as_object().unwrap(),
context.as_object().unwrap(),
&plan,
)
.unwrap();
assert_eq!(success.client_body_json()["id"], "local-video");
assert_eq!(success.client_body_json()["status"], "queued");
let snapshot = success.to_snapshot();
assert_eq!(
snapshot.to_upsert_record().external_task_id.as_deref(),
Some("xai-request")
);
service.record_snapshot(snapshot.clone());
let poll = service
.prepare_poll_refresh_plan_for_snapshot(snapshot, "xai-poll")
.unwrap();
assert_eq!(poll.plan.method, "GET");
assert_eq!(poll.plan.url, format!("{api_root}/videos/xai-request"));
assert_eq!(
poll.plan.headers.get("authorization"),
plan.headers.get("authorization")
);
assert!(service.apply_read_refresh_projection(
&poll,
json!({"status": "pending"}).as_object().unwrap()
));
assert_eq!(
service
.read_response(Some("openai"), "/v1/videos/local-video")
.unwrap()
.body_json["status"],
"queued"
);
assert!(service.apply_read_refresh_projection(&poll, json!({
"status": "done", "video": {"url": "https://vidgen.x.ai/result.mp4", "duration": 6}
}).as_object().unwrap()));
let snapshot = service
.snapshot_for_route(Some("openai"), "/v1/videos/local-video")
.unwrap();
assert!(!snapshot.is_active_for_refresh());
let record = snapshot.to_upsert_record();
assert_eq!(
record.status,
aether_data_contracts::repository::video_tasks::VideoTaskStatus::Completed
);
assert_eq!(
record.video_url.as_deref(),
Some("https://vidgen.x.ai/result.mp4")
);
assert_eq!(record.duration_seconds, Some(6));
let response = snapshot.read_response();
assert_eq!(response.body_json["status"], "completed");
assert_eq!(response.body_json["progress"], 100);
assert_eq!(
response.body_json["video_url"],
"https://vidgen.x.ai/result.mp4"
);
let LocalVideoTaskSnapshot::OpenAi(seed) = snapshot else {
panic!("OpenAI video expected")
};
let Some(LocalVideoTaskContentAction::StreamPlan(download)) =
seed.build_content_stream_action(None, "download")
else {
panic!("download expected")
};
assert_eq!(download.url, "https://vidgen.x.ai/result.mp4");
assert!(
download.headers.is_empty(),
"provider credentials must not be sent to the media CDN"
);
}
}
#[test]
fn xai_video_errors_are_terminal_even_without_a_status() {
use crate::{LocalVideoTaskSnapshot, VideoTaskTruthSourceMode};
let mut plan =
build_internal_finalize_video_plan("xai-create", "openai:video", None).unwrap();
plan.url = "https://cli-chat-proxy.grok.com/v1/videos/generations".into();
for body in [
json!({"code": "content_policy_violation", "error": "Rejected"}),
json!({"error": {"code": "content_policy_violation", "message": "Rejected"}}),
json!({"status": "failed", "error": "Rejected"}),
] {
let mut snapshot = VideoTaskTruthSourceMode::RustAuthoritative
.prepare_sync_success(
"openai_video_create_sync_finalize",
json!({"request_id": "xai-request"}).as_object().unwrap(),
&Default::default(),
&plan,
)
.unwrap()
.to_snapshot();
snapshot.apply_provider_body(body.as_object().unwrap());
assert!(!snapshot.is_active_for_refresh());
assert_eq!(snapshot.read_response().body_json["status"], "failed");
let LocalVideoTaskSnapshot::OpenAi(seed) = snapshot else {
panic!("OpenAI video expected")
};
assert!(seed.error_message.is_none());
}
}
#[test]
fn openai_video_id_takes_precedence_over_xai_alias() {
assert_eq!(
super::openai_video_provider_task_id(
json!({"id": "openai-id", "request_id": "trace-id"})
.as_object()
.unwrap()
),
Some("openai-id")
);
assert_eq!(
super::openai_video_provider_task_id(
json!({"id": " ", "request_id": "xai-id"})
.as_object()
.unwrap()
),
Some("xai-id")
);
assert_eq!(
super::openai_video_provider_task_id(json!({"request_id": " "}).as_object().unwrap()),
None
);
}
#[test]
fn builds_local_sync_finalize_read_response_for_supported_video_finalize_kinds() {
let delete_response = build_local_sync_finalize_read_response(