mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* feat(media): integrate xAI Grok Imagine (image + video + native audio)
Adds a real provider integration for xAI's Imagine API alongside OpenAI,
Volcengine and HyperFrames. The route surfaces as two model entries:
* grok-imagine-image — POST /v1/images/generations, synchronous, asks
for b64_json so the bytes arrive in one round-trip; sniffs the
returned magic bytes so JPEG payloads land with the right extension
* grok-imagine-video — POST /v1/videos/generations + GET /v1/videos/{id}
polling, with a 4s tick + onProgress heartbeat (mirrors the
Volcengine handler so the agent's bash watchdog doesn't kill long
polls). Native audio (AAC) ships in the same file as the H.264
video — that's the differentiator vs Seedance and Sora.
Picker visibility is gated by NewProjectPanel's hardcoded surface→
provider allowlist; grok is added to image + video. Settings UI picks up
the provider automatically off MEDIA_PROVIDERS.
Credentials: XAI_API_KEY (canonical, matches the official SDK) or
OD_GROK_API_KEY override; both are honoured ahead of any value pasted
into Settings, so users who already export XAI_API_KEY don't have to
re-enter it in the UI.
Verified end-to-end with a real key:
* image: 1024×1024 JPEG, single round-trip
* video: 5s 16:9 H.264 + AAC, ~46s wall clock, pending→done
* fix(i18n): include pl in EXPECTED_LOCALES so locales.test passes
Drive-by unblock for CI on this branch — `pl.ts` and `LOCALES`/`Locale`
in types.ts already include Polish, but the test's hardcoded
EXPECTED_LOCALES didn't, so every PR has been red on
locales.test.ts since the locale was added.
* fix(media): grok review feedback — credential precedence comment + poll timeout diagnostics
Two small fixes from PR #276 review:
* media-config.ts — comment claimed XAI_API_KEY won, but readEnvKey
iterates the array in order so OD_GROK_API_KEY actually wins
(matches every other provider's OD_* override convention). Rewrite
the comment to describe what the code does instead of flipping the
array — the precedence is correct, the comment was wrong.
* media.ts renderGrokVideo — single throw at the bottom couldn't tell
"timed out after N seconds with status still pending" from "submit
returned neither inline video nor a request_id to poll." Split into
two branches so operators know whether to bump
OD_GROK_VIDEO_MAX_POLL_MS or file an upstream contract bug.