mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
feat: update image default model
This commit is contained in:
@@ -26,7 +26,7 @@ Equip your AI Agent out-of-the-box with these capabilities, composable across co
|
||||
|
||||
- **Text chat** — Qwen3.8-max: major gains in agentic coding, frontend coding, and vibe coding
|
||||
- **Multimodal (Omni)** — Full omni-modal support across text + image + audio + video
|
||||
- **Image generation & editing** — Qwen-Image 2.0: pro text rendering, photorealism, strong semantic adherence, multi-image composition
|
||||
- **Image generation & editing** — Qwen-Image 3.0: pro text rendering, photorealism, strong semantic adherence, multi-image composition
|
||||
- **Video generation & editing** — happyhorse-1.1 series: text-/image-/reference-to-video and natural-language video editing (up to 9-image reference)
|
||||
- **Speech synthesis & recognition** — CosyVoice streaming TTS, voice cloning from 5–20s samples; FunAudio-ASR covers 30 languages including 7 Chinese dialects and 20+ Mandarin accents
|
||||
- **Image & video understanding** — Qwen-VL: long-form video analysis, chart/document parsing, visual reasoning, multilingual OCR
|
||||
|
||||
@@ -26,7 +26,7 @@ _专为 AI Agent 打造,每个命令均可作为结构化工具调用。_
|
||||
|
||||
- **文本对话** — Qwen3.8-max:Agentic coding、前端编程、Vibe coding 等能力显著增强
|
||||
- **全模态对话** — 文本 + 图像 + 音频 + 视频全模态支持
|
||||
- **图像生成与编辑** — Qwen-Image 2.0:专业文字渲染、真实质感、强语义遵循、多图合成
|
||||
- **图像生成与编辑** — Qwen-Image 3.0:专业文字渲染、真实质感、强语义遵循、多图合成
|
||||
- **视频生成与编辑** — happyhorse-1.1 系列,支持文生 / 图生 / 参考生(最多 9 张图参考)/ 自然语言视频编辑
|
||||
- **语音合成与识别** — CosyVoice 实时流式合成,5-20s 样本即可克隆;FunAudio-ASR 覆盖 30 种语种,含汉语七大方言与 20+ 口音官话
|
||||
- **图像与视频理解** — Qwen-VL:长视频解析、复杂图表与文档识别、视觉推理、多语种 OCR
|
||||
|
||||
@@ -61,7 +61,7 @@ export const UI_BOOLEAN_KEYS = new Set<string>(["telemetry"]);
|
||||
// without persisting a value that would pin the model.
|
||||
export const UI_MODEL_DEFAULTS: Record<string, string> = {
|
||||
default_text_model: "qwen3.8-max",
|
||||
default_image_model: "qwen-image-2.0",
|
||||
default_image_model: "qwen-image-3.0",
|
||||
default_video_model: "happyhorse-1.1-t2v",
|
||||
default_speech_model: "cosyvoice-v3-flash",
|
||||
default_omni_model: "qwen3.5-omni-plus",
|
||||
@@ -86,7 +86,8 @@ export const UI_MODEL_CATALOG: Record<string, ModelOption[]> = {
|
||||
{ id: "qwen3.6-flash", role: "fast · advisor intent" },
|
||||
],
|
||||
default_image_model: [
|
||||
{ id: "qwen-image-2.0", role: "image/generate default · sync" },
|
||||
{ id: "qwen-image-3.0", role: "image/generate default · sync" },
|
||||
{ id: "qwen-image-2.0", role: "image/generate · sync" },
|
||||
{ id: "qwen-image-max", role: "image/generate · sync" },
|
||||
{ id: "qwen-image-edit-2.0", role: "image/edit · sync" },
|
||||
{ id: "wanx2.x", role: "image/generate · async series" },
|
||||
|
||||
@@ -23,7 +23,7 @@ export default defineCommand({
|
||||
"--file photo.jpg --model qwen3-vl-plus",
|
||||
"--file video.mp4 --model wan2.1-t2v-plus",
|
||||
"--file audio.wav --model qwen3-asr-flash",
|
||||
"--file cat.png --model qwen-image-2.0",
|
||||
"--file cat.png --model qwen-image-3.0",
|
||||
],
|
||||
async run(ctx) {
|
||||
const { settings, flags } = ctx;
|
||||
|
||||
@@ -47,7 +47,7 @@ const EDIT_FLAGS = {
|
||||
model: {
|
||||
type: "string",
|
||||
valueHint: "<model>",
|
||||
description: "Model ID (default: qwen-image-2.0)",
|
||||
description: "Model ID (default: qwen-image-3.0)",
|
||||
},
|
||||
size: {
|
||||
type: "string",
|
||||
@@ -123,7 +123,7 @@ export default defineCommand({
|
||||
}
|
||||
const prompt = flags.prompt;
|
||||
|
||||
const model = flags.model || settings.defaultImageModel || "qwen-image-2.0";
|
||||
const model = flags.model || settings.defaultImageModel || "qwen-image-3.0";
|
||||
const route = resolveImageEditApi(model);
|
||||
|
||||
// Auto-upload local files (resolve all images in parallel)
|
||||
|
||||
@@ -35,7 +35,7 @@ const GENERATE_FLAGS = {
|
||||
model: {
|
||||
type: "string",
|
||||
valueHint: "<model>",
|
||||
description: "Model ID (default: qwen-image-2.0)",
|
||||
description: "Model ID (default: qwen-image-3.0)",
|
||||
},
|
||||
size: {
|
||||
type: "string",
|
||||
@@ -105,7 +105,7 @@ export default defineCommand({
|
||||
const { settings, flags } = ctx;
|
||||
const prompt = flags.prompt;
|
||||
|
||||
const model = flags.model || settings.defaultImageModel || "qwen-image-2.0";
|
||||
const model = flags.model || settings.defaultImageModel || "qwen-image-3.0";
|
||||
const route = resolveImageGenerateApi(model);
|
||||
const defaultSize = "1:1";
|
||||
const sizeInput = flags.size || defaultSize;
|
||||
|
||||
@@ -89,13 +89,13 @@ test("GET /api/config 返回全部 profile、明文密钥与持久化激活项",
|
||||
expect(res.json.enums.console_site).toEqual(["domestic", "international"]);
|
||||
expect(res.json.booleanKeys).toContain("telemetry");
|
||||
// Default field hints are surfaced as prefilled values in the UI.
|
||||
expect(res.json.fieldDefaults.default_image_model).toBe("qwen-image-2.0");
|
||||
expect(res.json.fieldDefaults.default_image_model).toBe("qwen-image-3.0");
|
||||
expect(res.json.fieldDefaults.default_text_model).toBe("qwen3.8-max");
|
||||
expect(res.json.fieldDefaults.output_dir).toContain("bailian-output");
|
||||
expect(res.json.fieldDefaults.timeout).toBe("300");
|
||||
expect(res.json.fieldDefaults.base_url).toBe("https://dashscope.aliyuncs.com");
|
||||
// Per-category model catalog (click-to-fill suggestions) is exposed too.
|
||||
expect(res.json.modelCatalog.default_image_model[0]).toMatchObject({ id: "qwen-image-2.0" });
|
||||
expect(res.json.modelCatalog.default_image_model[0]).toMatchObject({ id: "qwen-image-3.0" });
|
||||
expect(res.json.modelCatalog.default_video_model.map((m: { id: string }) => m.id)).toContain(
|
||||
"happyhorse-1.1-i2v",
|
||||
);
|
||||
|
||||
@@ -194,13 +194,13 @@ describe.skipIf(!isBailianE2EMediaEnabled() || !isDashScopeE2EReady())("e2e: ima
|
||||
expect(stderr).toMatch(/--prompt|Usage:/i);
|
||||
});
|
||||
|
||||
test("【qwen-image-2.0】图片编辑", async () => {
|
||||
test("【qwen-image-3.0】图片编辑", async () => {
|
||||
const outDir = makeE2eOutputDir(e2eLabelFromMetaUrl(import.meta.url));
|
||||
const gen = await runCommandE2e(IMAGE_ROUTES, [
|
||||
"image",
|
||||
"generate",
|
||||
"--model",
|
||||
"qwen-image-2.0",
|
||||
"qwen-image-3.0",
|
||||
"--prompt",
|
||||
"一只简笔画小猫,白底",
|
||||
"--out-dir",
|
||||
@@ -220,7 +220,7 @@ describe.skipIf(!isBailianE2EMediaEnabled() || !isDashScopeE2EReady())("e2e: ima
|
||||
"image",
|
||||
"edit",
|
||||
"--model",
|
||||
"qwen-image-2.0",
|
||||
"qwen-image-3.0",
|
||||
"--image",
|
||||
imagePath!,
|
||||
"--prompt",
|
||||
|
||||
@@ -202,19 +202,19 @@ describe.skipIf(!isBailianE2EMediaEnabled() || !isDashScopeE2EReady())(
|
||||
"image",
|
||||
"generate",
|
||||
"--model",
|
||||
"qwen-image-2.0",
|
||||
"qwen-image-3.0",
|
||||
]);
|
||||
expect(exitCode).toBe(2);
|
||||
expect(stderr).toMatch(/--prompt|Usage:/i);
|
||||
});
|
||||
|
||||
test("【qwen-image-2.0】图片生成", async () => {
|
||||
test("【qwen-image-3.0】图片生成", async () => {
|
||||
const outDir = makeE2eOutputDir(e2eLabelFromMetaUrl(import.meta.url));
|
||||
const { stdout, stderr, exitCode } = await runCommandE2e(IMAGE_ROUTES, [
|
||||
"image",
|
||||
"generate",
|
||||
"--model",
|
||||
"qwen-image-2.0",
|
||||
"qwen-image-3.0",
|
||||
"--prompt",
|
||||
"一只简笔画小猫,白底",
|
||||
"--out-dir",
|
||||
|
||||
@@ -159,7 +159,7 @@ describe.skipIf(!isBailianE2EVideoEnabled() || !isDashScopeE2EReady())(
|
||||
"image",
|
||||
"generate",
|
||||
"--model",
|
||||
"qwen-image-2.0",
|
||||
"qwen-image-3.0",
|
||||
"--prompt",
|
||||
"一只简笔画小猫,白底",
|
||||
"--out-dir",
|
||||
|
||||
@@ -169,7 +169,7 @@ describe.skipIf(!isBailianE2EVideoEnabled() || !isDashScopeE2EReady())(
|
||||
"image",
|
||||
"generate",
|
||||
"--model",
|
||||
"qwen-image-2.0",
|
||||
"qwen-image-3.0",
|
||||
"--prompt",
|
||||
"一片绿色的树叶,白底",
|
||||
"--out-dir",
|
||||
|
||||
@@ -10,7 +10,7 @@ import { image2ImagePath, imagePath, imageSyncPath, imageText2ImagePath } from "
|
||||
* - async text2image + prompt: wan2.5/2.2/2.1-t2i*, wanx*-t2i*
|
||||
*
|
||||
* Edit (I2I):
|
||||
* - sync multimodal + messages(+images): qwen-image-2.0*, qwen-image-edit*, wan2.6-image*, wan2.7-image*
|
||||
* - sync multimodal + messages(+images): qwen-image-3.0*, qwen-image-2.0*, qwen-image-edit*, wan2.6-image*, wan2.7-image*
|
||||
* (pure T2I models such as z-image / qwen-image-plus / qwen-image-max are NOT edit models)
|
||||
* - async image2image + prompt/images: wan2.5-i2i*
|
||||
* - async image2image + function/base_image_url: *imageedit* (e.g. wanx2.1-imageedit)
|
||||
@@ -60,6 +60,7 @@ const SYNC_GENERATE_PREFIXES = ["qwen-image", "wan2.7-image", "z-image"] as cons
|
||||
* Pure T2I models (z-image / qwen-image-plus / qwen-image-max) are excluded.
|
||||
*/
|
||||
const SYNC_EDIT_PREFIXES = [
|
||||
"qwen-image-3.0",
|
||||
"qwen-image-2.0",
|
||||
"qwen-image-edit",
|
||||
"wan2.7-image",
|
||||
@@ -109,7 +110,12 @@ export function isWanxFunctionImageEditModel(model: string): boolean {
|
||||
}
|
||||
|
||||
export function resolveImageSizeProfile(model: string): ImageSizeProfile {
|
||||
if (model.startsWith("qwen-image-2.0") || model.startsWith("qwen-image-edit")) {
|
||||
// 3.0 暂复用 2.0 高分比例表(CLI ratio→像素便捷映射);不做独立 3.0 profile。
|
||||
if (
|
||||
model.startsWith("qwen-image-3.0") ||
|
||||
model.startsWith("qwen-image-2.0") ||
|
||||
model.startsWith("qwen-image-edit")
|
||||
) {
|
||||
return "qwen-image-2.0";
|
||||
}
|
||||
// Remaining qwen-image* (plus / max / bare qwen-image) share the fixed table.
|
||||
@@ -133,7 +139,13 @@ export function resolveImageSizeProfile(model: string): ImageSizeProfile {
|
||||
|
||||
/** Official / CLI defaults for prompt_extend when the flag is omitted. */
|
||||
export function resolvePromptExtendDefault(model: string): boolean | undefined {
|
||||
if (model.startsWith("qwen-image-2.0") || model.startsWith("qwen-image-max")) return true;
|
||||
if (
|
||||
model.startsWith("qwen-image-3.0") ||
|
||||
model.startsWith("qwen-image-2.0") ||
|
||||
model.startsWith("qwen-image-max")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// Z-Image docs default prompt_extend to false.
|
||||
if (model.startsWith("z-image")) return false;
|
||||
return undefined;
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "../src/client/image-routes.ts";
|
||||
|
||||
test("sync multimodal family covers qwen-image, wan2.6/2.7 image, and z-image", () => {
|
||||
expect(isSyncMultimodalImageModel("qwen-image-3.0")).toBe(true);
|
||||
expect(isSyncMultimodalImageModel("qwen-image-2.0")).toBe(true);
|
||||
expect(isSyncMultimodalImageModel("qwen-image-2.0-pro")).toBe(true);
|
||||
expect(isSyncMultimodalImageModel("qwen-image-plus")).toBe(true);
|
||||
@@ -41,6 +42,7 @@ test("legacy image2image is wan2.5-i2i only; wanx imageedit uses function protoc
|
||||
});
|
||||
|
||||
test("size profiles are model-specific, not sync/async", () => {
|
||||
expect(resolveImageSizeProfile("qwen-image-3.0")).toBe("qwen-image-2.0");
|
||||
expect(resolveImageSizeProfile("qwen-image-2.0")).toBe("qwen-image-2.0");
|
||||
expect(resolveImageSizeProfile("qwen-image")).toBe("qwen-image-fixed");
|
||||
expect(resolveImageSizeProfile("qwen-image-plus")).toBe("qwen-image-fixed");
|
||||
@@ -55,6 +57,7 @@ test("size profiles are model-specific, not sync/async", () => {
|
||||
});
|
||||
|
||||
test("prompt_extend defaults follow model docs", () => {
|
||||
expect(resolvePromptExtendDefault("qwen-image-3.0")).toBe(true);
|
||||
expect(resolvePromptExtendDefault("qwen-image-2.0")).toBe(true);
|
||||
expect(resolvePromptExtendDefault("qwen-image-max")).toBe(true);
|
||||
expect(resolvePromptExtendDefault("z-image-turbo")).toBe(false);
|
||||
@@ -85,6 +88,11 @@ test("resolveImageGenerateApi picks path, input style, and size profile", () =>
|
||||
kind: "async-image-generation",
|
||||
sizeProfile: "wan26",
|
||||
});
|
||||
expect(resolveImageGenerateApi("qwen-image-3.0")).toMatchObject({
|
||||
kind: "sync-multimodal",
|
||||
sizeProfile: "qwen-image-2.0",
|
||||
promptExtendDefault: true,
|
||||
});
|
||||
expect(resolveImageGenerateApi("qwen-image-2.0")).toMatchObject({
|
||||
kind: "sync-multimodal",
|
||||
sizeProfile: "qwen-image-2.0",
|
||||
@@ -115,6 +123,10 @@ test("resolveImageEditApi excludes pure T2I models from sync edit", () => {
|
||||
kind: "sync-multimodal",
|
||||
useSync: true,
|
||||
});
|
||||
expect(resolveImageEditApi("qwen-image-3.0")).toMatchObject({
|
||||
kind: "sync-multimodal",
|
||||
useSync: true,
|
||||
});
|
||||
expect(resolveImageEditApi("qwen-image-2.0")).toMatchObject({
|
||||
kind: "sync-multimodal",
|
||||
useSync: true,
|
||||
|
||||
@@ -171,7 +171,7 @@ export async function imageGenerate(
|
||||
});
|
||||
}
|
||||
|
||||
const model = input.model || "qwen-image-2.0";
|
||||
const model = input.model || "qwen-image-3.0";
|
||||
const route = resolveImageGenerateApi(model);
|
||||
const n = input.n ?? 1;
|
||||
|
||||
@@ -269,7 +269,7 @@ export async function imageEdit(
|
||||
}
|
||||
|
||||
const images = Array.isArray(input.image) ? input.image : input.image ? [input.image] : [];
|
||||
const model = input.model || "qwen-image-2.0";
|
||||
const model = input.model || "qwen-image-3.0";
|
||||
const route = resolveImageEditApi(model);
|
||||
const n = input.n ?? 1;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { ImageSizeProfile } from "bailian-cli-core";
|
||||
* Do not infer size from sync/async — that mismatches model constraints.
|
||||
*/
|
||||
|
||||
/** qwen-image-2.0 / qwen-image-edit recommended high-res presets. */
|
||||
/** qwen-image-2.0 / qwen-image-edit recommended high-res presets(3.0 经 profile 复用此表). */
|
||||
export const QWEN_IMAGE_20_RATIO_MAP: Record<string, string> = {
|
||||
"16:9": "2688*1536",
|
||||
"9:16": "1536*2688",
|
||||
|
||||
Reference in New Issue
Block a user