feat(ai): add Alibaba image generation

This commit is contained in:
Aiden Cline 2026-07-19 16:08:26 +00:00 committed by 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴
commit 07ce99dc9e
8 changed files with 687 additions and 0 deletions

View file

@ -0,0 +1,28 @@
{
"version": 1,
"metadata": {
"tags": ["prefix:alibaba-images", "provider:alibaba", "protocol:alibaba-images"],
"name": "alibaba-images/generates-with-qwen-image-2-0",
"recordedAt": "2026-07-19T16:05:19.642Z"
},
"interactions": [
{
"transport": "http",
"request": {
"method": "POST",
"url": "https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"qwen-image-2.0\",\"input\":{\"messages\":[{\"role\":\"user\",\"content\":[{\"text\":\"A simple flat black circle centered on a plain white background.\"}]}]},\"parameters\":{\"size\":\"512*512\",\"prompt_extend\":false,\"watermark\":false}}"
},
"response": {
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": "{\"output\":{\"choices\":[{\"finish_reason\":\"stop\",\"message\":{\"content\":[{\"image\":\"https://dashscope-463f.oss-accelerate.aliyuncs.com/7d/7b/20260720/991d10e3/fe425a64-dc6f-4c67-a4fa-a06810984ba9.png?Expires=1785082919&OSSAccessKeyId=fixture&Signature=fixture\"}],\"role\":\"assistant\"}}]},\"usage\":{\"height\":512,\"image_count\":1,\"width\":512},\"request_id\":\"484490f4-9881-90eb-8d52-d270d2395920\"}"
}
}
]
}

View file

@ -0,0 +1,28 @@
{
"version": 1,
"metadata": {
"tags": ["prefix:alibaba-images", "provider:alibaba", "protocol:alibaba-images"],
"name": "alibaba-images/generates-with-wan-2-7",
"recordedAt": "2026-07-19T16:05:23.617Z"
},
"interactions": [
{
"transport": "http",
"request": {
"method": "POST",
"url": "https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"wan2.7-image\",\"input\":{\"messages\":[{\"role\":\"user\",\"content\":[{\"text\":\"A simple flat black square centered on a plain white background.\"}]}]},\"parameters\":{\"size\":\"1K\",\"thinking_mode\":false,\"watermark\":false}}"
},
"response": {
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": "{\"output\":{\"choices\":[{\"finish_reason\":\"stop\",\"message\":{\"content\":[{\"image\":\"https://dashscope-463f.oss-accelerate.aliyuncs.com/1d/ae/20260720/10739b93/1784477122_595381b5.png?Expires=1784563522&OSSAccessKeyId=fixture&Signature=fixture\",\"type\":\"image\"}],\"role\":\"assistant\"}}],\"finished\":true},\"usage\":{\"image_count\":1,\"input_tokens\":31,\"output_tokens\":2,\"size\":\"1024*1024\",\"total_tokens\":33},\"request_id\":\"ec5cacb5-c7b0-9e46-8459-49003a3816c3\"}"
}
}
]
}

View file

@ -0,0 +1,47 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Image } from "../../src"
import { Alibaba } from "../../src/providers"
import { recordedTests } from "../recorded-test"
const alibaba = Alibaba.configure({
apiKey: process.env.DASHSCOPE_API_KEY ?? "fixture",
})
const recorded = recordedTests({
prefix: "alibaba-images",
provider: "alibaba",
protocol: "alibaba-images",
requires: ["DASHSCOPE_API_KEY"],
})
describe("Alibaba Images recorded", () => {
recorded.effect("generates with Qwen Image 2.0", () =>
Effect.gen(function* () {
const response = yield* Image.generate({
model: alibaba.image("qwen-image-2.0"),
prompt: "A simple flat black circle centered on a plain white background.",
size: { width: 512, height: 512 },
providerOptions: { alibaba: { qwen: { promptExtend: false, watermark: false } } },
})
expect(response.images).toHaveLength(1)
expect(response.image?.mediaType).toBe("image/png")
expect(response.image?.data).toStartWith("https://")
}),
)
recorded.effect("generates with Wan 2.7", () =>
Effect.gen(function* () {
const response = yield* Image.generate({
model: alibaba.image("wan2.7-image"),
prompt: "A simple flat black square centered on a plain white background.",
providerOptions: { alibaba: { wan: { resolution: "1K", thinkingMode: false, watermark: false } } },
})
expect(response.images).toHaveLength(1)
expect(response.image?.mediaType).toBe("image/png")
expect(response.image?.data).toStartWith("https://")
}),
)
})

View file

@ -0,0 +1,196 @@
import { describe, expect } from "bun:test"
import { Effect, Layer } from "effect"
import { HttpClientRequest } from "effect/unstable/http"
import { Image, ImageClient } from "../../src"
import { Alibaba } from "../../src/providers"
import { it } from "../lib/effect"
import { dynamicResponse } from "../lib/http"
const response = (family: "qwen" | "wan") => ({
output: {
choices: [
{
finish_reason: "stop",
message: {
role: "assistant",
content: [
{
image: "https://dashscope-result-intl.oss-cn-singapore.aliyuncs.com/result.png?Expires=1893456000",
...(family === "wan" ? { type: "image" } : {}),
},
],
},
},
],
...(family === "wan" ? { finished: true } : {}),
},
usage:
family === "wan"
? { image_count: 1, input_tokens: 10, output_tokens: 2, total_tokens: 12, size: "2048*2048" }
: { image_count: 1, width: 1024, height: 768 },
request_id: `request-${family}`,
})
describe("Alibaba Images", () => {
it.effect("generates Qwen Image 2.0 through the international synchronous route", () =>
Effect.gen(function* () {
const result = yield* Image.generate({
model: Alibaba.configure({
apiKey: "test",
baseURL: "https://dashscope-intl.test/api/v1",
image: { providerOptions: { qwen: { promptExtend: true, watermark: false } } },
http: { headers: { "x-default": "yes" } },
}).image("qwen-image-2.0-pro"),
prompt: "A robot tending a rooftop garden",
count: 2,
size: { width: 1024, height: 768 },
seed: 42,
providerOptions: { alibaba: { qwen: { negativePrompt: "blurry" } } },
http: { headers: { "x-request": "yes" }, query: { trace: "1" }, body: { metadata: "test" } },
})
expect(result.image?.data).toContain("dashscope-result-intl")
expect(result.image?.providerMetadata).toEqual({
alibaba: {
modelId: "qwen-image-2.0-pro",
family: "qwen",
expiresAt: "2030-01-01T00:00:00.000Z",
},
})
expect(result.providerMetadata).toEqual({
alibaba: { requestId: "request-qwen", modelId: "qwen-image-2.0-pro", family: "qwen" },
})
}).pipe(
Effect.provide(
ImageClient.layer.pipe(
Layer.provide(
dynamicResponse((input) =>
Effect.gen(function* () {
const request = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)
expect(request.url).toBe(
"https://dashscope-intl.test/api/v1/services/aigc/multimodal-generation/generation?trace=1",
)
expect(request.headers.get("authorization")).toBe("Bearer test")
expect(request.headers.get("x-default")).toBe("yes")
expect(request.headers.get("x-request")).toBe("yes")
expect(JSON.parse(input.text)).toEqual({
model: "qwen-image-2.0-pro",
input: {
messages: [{ role: "user", content: [{ text: "A robot tending a rooftop garden" }] }],
},
parameters: {
size: "1024*768",
n: 2,
negative_prompt: "blurry",
prompt_extend: true,
watermark: false,
seed: 42,
},
metadata: "test",
})
return input.respond(JSON.stringify(response("qwen")), {
headers: { "content-type": "application/json" },
})
}),
),
),
),
),
),
)
it.effect("generates Wan 2.7 through the international synchronous route", () =>
Effect.gen(function* () {
const result = yield* Image.generate({
model: Alibaba.configure({ apiKey: "test", baseURL: "https://dashscope-intl.test/api/v1" }).image(
"wan2.7-image-pro",
),
prompt: "A flower shop with a wooden door",
count: 1,
seed: 7,
providerOptions: {
alibaba: {
wan: {
resolution: "2K",
thinkingMode: true,
watermark: false,
colorPalette: [
{ hex: "#112233", ratio: "60.00%" },
{ hex: "#445566", ratio: "25.00%" },
{ hex: "#778899", ratio: "15.00%" },
],
},
},
},
})
expect(result.images).toHaveLength(1)
expect(result.image?.mediaType).toBe("image/png")
expect(result.usage?.totalTokens).toBe(12)
expect(result.usage?.providerMetadata).toEqual({ alibaba: response("wan").usage })
}).pipe(
Effect.provide(
ImageClient.layer.pipe(
Layer.provide(
dynamicResponse((input) =>
Effect.gen(function* () {
const request = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)
expect(request.url).toBe(
"https://dashscope-intl.test/api/v1/services/aigc/multimodal-generation/generation",
)
expect(JSON.parse(input.text)).toEqual({
model: "wan2.7-image-pro",
input: { messages: [{ role: "user", content: [{ text: "A flower shop with a wooden door" }] }] },
parameters: {
size: "2K",
n: 1,
thinking_mode: true,
color_palette: [
{ hex: "#112233", ratio: "60.00%" },
{ hex: "#445566", ratio: "25.00%" },
{ hex: "#778899", ratio: "15.00%" },
],
watermark: false,
seed: 7,
},
})
return input.respond(JSON.stringify(response("wan")), {
headers: { "content-type": "application/json" },
})
}),
),
),
),
),
),
)
it.effect("surfaces Alibaba error envelopes as typed provider errors", () =>
Image.generate({
model: Alibaba.configure({ apiKey: "test" }).image("qwen-image-2.0"),
prompt: "A robot",
}).pipe(
Effect.flip,
Effect.tap((error) =>
Effect.sync(() => {
expect(error.reason._tag).toBe("UnknownProvider")
expect(error.message).toContain("InvalidParameter: invalid prompt")
}),
),
Effect.provide(
ImageClient.layer.pipe(
Layer.provide(
dynamicResponse((input) =>
Effect.succeed(
input.respond(
JSON.stringify({ request_id: "request-error", code: "InvalidParameter", message: "invalid prompt" }),
{ headers: { "content-type": "application/json" } },
),
),
),
),
),
),
),
)
})