
























@@ -81,6 +81,136 @@ describe("xai video generation provider", () => {
8181);
8282});
838384+it("sends a single unroled image as xAI first-frame image-to-video", async () => {
85+postJsonRequestMock.mockResolvedValue({
86+response: {
87+json: async () => ({
88+request_id: "req_image",
89+}),
90+},
91+release: vi.fn(async () => {}),
92+});
93+fetchWithTimeoutMock
94+.mockResolvedValueOnce({
95+json: async () => ({
96+request_id: "req_image",
97+status: "done",
98+video: { url: "https://cdn.x.ai/image-video.mp4" },
99+}),
100+})
101+.mockResolvedValueOnce({
102+headers: new Headers({ "content-type": "video/mp4" }),
103+arrayBuffer: async () => Buffer.from("image-video-bytes"),
104+});
105+106+const provider = buildXaiVideoGenerationProvider();
107+const result = await provider.generateVideo({
108+provider: "xai",
109+model: "grok-imagine-video",
110+prompt: "Animate this logo into a clean bumper",
111+cfg: {},
112+inputImages: [{ buffer: Buffer.from("png-bytes"), mimeType: "image/png" }],
113+});
114+115+const body = postJsonRequestMock.mock.calls[0]?.[0]?.body as Record<string, unknown>;
116+expect(postJsonRequestMock).toHaveBeenCalledWith(
117+expect.objectContaining({
118+url: "https://api.x.ai/v1/videos/generations",
119+body: expect.objectContaining({
120+image: {
121+url: expect.stringMatching(/^data:image\/png;base64,/),
122+},
123+}),
124+}),
125+);
126+expect(body).not.toHaveProperty("reference_images");
127+expect(result.metadata).toEqual(
128+expect.objectContaining({
129+mode: "generate",
130+}),
131+);
132+});
133+134+it("sends reference_image roles through xAI reference_images mode", async () => {
135+postJsonRequestMock.mockResolvedValue({
136+response: {
137+json: async () => ({
138+request_id: "req_refs",
139+}),
140+},
141+release: vi.fn(async () => {}),
142+});
143+fetchWithTimeoutMock
144+.mockResolvedValueOnce({
145+json: async () => ({
146+request_id: "req_refs",
147+status: "done",
148+video: { url: "https://cdn.x.ai/reference-video.mp4" },
149+}),
150+})
151+.mockResolvedValueOnce({
152+headers: new Headers({ "content-type": "video/mp4" }),
153+arrayBuffer: async () => Buffer.from("reference-video-bytes"),
154+});
155+156+const provider = buildXaiVideoGenerationProvider();
157+const result = await provider.generateVideo({
158+provider: "xai",
159+model: "grok-imagine-video",
160+prompt: "Make a cinematic brand vignette using these references",
161+cfg: {},
162+durationSeconds: 12,
163+aspectRatio: "9:16",
164+resolution: "720P",
165+inputImages: [
166+{ url: "https://example.com/subject.png", role: "reference_image" },
167+{ url: "https://example.com/style.png", role: "reference_image" },
168+],
169+});
170+171+const body = postJsonRequestMock.mock.calls[0]?.[0]?.body as Record<string, unknown>;
172+expect(postJsonRequestMock).toHaveBeenCalledWith(
173+expect.objectContaining({
174+url: "https://api.x.ai/v1/videos/generations",
175+body: expect.objectContaining({
176+reference_images: [
177+{ url: "https://example.com/subject.png" },
178+{ url: "https://example.com/style.png" },
179+],
180+duration: 10,
181+aspect_ratio: "9:16",
182+resolution: "720p",
183+}),
184+}),
185+);
186+expect(body).not.toHaveProperty("image");
187+expect(result.metadata).toEqual(
188+expect.objectContaining({
189+mode: "referenceToVideo",
190+}),
191+);
192+});
193+194+it("rejects mixed xAI first-frame and reference-image roles", async () => {
195+const provider = buildXaiVideoGenerationProvider();
196+197+await expect(
198+provider.generateVideo({
199+provider: "xai",
200+model: "grok-imagine-video",
201+prompt: "Use both images",
202+cfg: {},
203+inputImages: [
204+{ url: "https://example.com/subject.png", role: "reference_image" },
205+{ url: "https://example.com/first-frame.png", role: "first_frame" },
206+],
207+}),
208+).rejects.toThrow(
209+"xAI reference-image video generation requires every image role to be reference_image.",
210+);
211+expect(postJsonRequestMock).not.toHaveBeenCalled();
212+});
213+84214it("routes video inputs to the extension endpoint when duration is set", async () => {
85215postJsonRequestMock.mockResolvedValue({
86216response: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。