Merge pull request #63 from modelstudioai/feat/i18n-english-only

feat(cli): standardize user-facing CLI text to English
This commit is contained in:
Gong Shiqi
2026-06-17 14:27:27 +08:00
committed by GitHub
39 changed files with 236 additions and 278 deletions
+6 -6
View File
@@ -35,12 +35,12 @@ export default defineCommand({
{ flag: "--has-thoughts", description: "Show agent thinking process" },
],
examples: [
'bl app call --app-id abc123 --prompt "你好"',
'bl app call --app-id abc123 --prompt "描述这张图片" --image https://example.com/photo.jpg',
'bl app call --app-id abc123 --prompt "分析图片" --image img1.jpg --image img2.jpg',
'bl app call --app-id abc123 --prompt "继续" --session-id sess_xxx --stream',
'bl app call --app-id abc123 --prompt "搜索资料" --pipeline-ids pipe1,pipe2',
'bl app call --app-id abc123 --prompt "开始" --biz-params \'{"key":"value"}\'',
'bl app call --app-id abc123 --prompt "Hello"',
'bl app call --app-id abc123 --prompt "Describe this image" --image https://example.com/photo.jpg',
'bl app call --app-id abc123 --prompt "Analyze the image" --image img1.jpg --image img2.jpg',
'bl app call --app-id abc123 --prompt "Continue" --session-id sess_xxx --stream',
'bl app call --app-id abc123 --prompt "Search for materials" --pipeline-ids pipe1,pipe2',
'bl app call --app-id abc123 --prompt "Start" --biz-params \'{"key":"value"}\'',
],
async run(config: Config, flags: GlobalFlags) {
const appId = flags.appId as string;
+1 -1
View File
@@ -37,7 +37,7 @@ export default defineCommand({
],
examples: [
"bl app list",
"bl app list --name 客服",
"bl app list --name customer service",
"bl app list --page 2 --page-size 10",
"bl app list --output json",
],
+3 -3
View File
@@ -64,11 +64,11 @@ export default defineCommand({
{ flag: "--out-prefix <prefix>", description: "Filename prefix (default: edited)" },
],
examples: [
'bl image edit --image ./photo.png --prompt "把背景换成海滩"',
'bl image edit --image ./photo.png --prompt "Replace the background with a beach"',
'bl image edit --image https://example.com/logo.png --prompt "Change color to blue" --n 3',
'bl image edit --image ./a.png --image ./b.png --prompt "把两张图合并成一张拼图"',
'bl image edit --image ./a.png --image ./b.png --prompt "Merge two images into one collage"',
'bl image edit --image https://example.com/photo.png --prompt "Remove the person" --model qwen-image-2.0-pro',
'bl image edit --image ./photo.png --prompt "把背景换成海滩" --watermark false',
'bl image edit --image ./photo.png --prompt "Replace the background with a beach" --watermark false',
],
async run(config: Config, flags: GlobalFlags) {
// Normalize --image to string array (supports both single and repeated flags)
+1 -1
View File
@@ -82,7 +82,7 @@ export default defineCommand({
},
],
examples: [
'bl image generate --prompt "一只穿太空服的猫在火星上"',
'bl image generate --prompt "A cat in a spacesuit on Mars"',
'bl image generate --prompt "Logo design" --n 3 --out-dir ./generated/',
'bl image generate --prompt "Mountain landscape" --size 2688*1536',
'bl image generate --prompt "A castle" --seed 42 --prompt-extend false',
@@ -77,8 +77,8 @@ export default defineCommand({
"`--workspace-id` is NOT required when using --api-key.",
],
examples: [
'bl knowledge retrieve --index-id idx_xxx --query "如何使用阿里云百炼"',
'bl knowledge retrieve --api-key $DASHSCOPE_API_KEY --index-id idx_xxx --query "RAG检索" --rerank --rerank-model qwen3-rerank-hybrid',
'bl knowledge retrieve --index-id idx_xxx --query "How to use Alibaba Cloud Bailian"',
'bl knowledge retrieve --api-key $DASHSCOPE_API_KEY --index-id idx_xxx --query "RAG retrieval" --rerank --rerank-model qwen3-rerank-hybrid',
],
async run(config: Config, flags: GlobalFlags) {
const indexId = flags.indexId as string;
+2 -2
View File
@@ -57,8 +57,8 @@ export default defineCommand({
{ flag: "--url <url>", description: "Override the MCP endpoint URL (for non-Bailian servers)" },
],
examples: [
'bl mcp call market-cmapi00073529.SmartStockSelection --query "筛选ROE>15%的消费股"',
'bl mcp call market-cmapi00073529.FinQuery --json \'{"q":"贵州茅台","limit":5}\'',
'bl mcp call market-cmapi00073529.SmartStockSelection --query "Screen consumer stocks with ROE > 15%"',
'bl mcp call market-cmapi00073529.FinQuery --json \'{"q":"Guizhou Maotai","limit":5}\'',
"bl mcp call market-cmapi00073529.SmartFundSelection --arg riskLevel=R3 --arg minScale=10",
],
async run(config: Config, flags: GlobalFlags) {
+1 -1
View File
@@ -38,7 +38,7 @@ export default defineCommand({
{ flag: "--page-size <n>", description: "Results per page (default: 30)", type: "number" },
{ flag: "--region <region>", description: "API region (default: cn-beijing)" },
],
examples: ["bl mcp list", "bl mcp list --name 金融", "bl mcp list --output json"],
examples: ["bl mcp list", "bl mcp list --name finance", "bl mcp list --output json"],
async run(config: Config, flags: GlobalFlags) {
const serverName = (flags.name as string) || "";
const type = (flags.type as string) || "OFFICIAL";
+3 -3
View File
@@ -26,9 +26,9 @@ export default defineCommand({
{ flag: "--memory-library-id <id>", description: "Memory library ID (isolate memory space)" },
],
examples: [
'bl memory add --user-id user1 --content "用户喜欢Python编程"',
'bl memory add --user-id user1 --messages \'[{"role":"user","content":"我喜欢旅行"}]\'',
'bl memory add --user-id user1 --content "住在北京" --profile-schema schema_xxx',
'bl memory add --user-id user1 --content "The user likes Python programming"',
'bl memory add --user-id user1 --messages \'[{"role":"user","content":"I like traveling"}]\'',
'bl memory add --user-id user1 --content "Lives in Beijing" --profile-schema schema_xxx',
],
async run(config: Config, flags: GlobalFlags) {
const userId = flags.userId as string;
@@ -20,12 +20,12 @@ export default defineCommand({
{ flag: "--description <text>", description: "Schema description" },
{
flag: "--attributes <json>",
description: 'Attributes JSON array: [{"name":"age","description":"年龄"}]',
description: 'Attributes JSON array: [{"name":"age","description":"age"}]',
required: true,
},
],
examples: [
'bl memory profile create --name "user_basic" --attributes \'[{"name":"age","description":"年龄"},{"name":"hobby","description":"爱好"}]\'',
'bl memory profile create --name "user_basic" --attributes \'[{"name":"age","description":"age"},{"name":"hobby","description":"hobby"}]\'',
],
async run(config: Config, flags: GlobalFlags) {
const name = flags.name as string;
+2 -2
View File
@@ -27,8 +27,8 @@ export default defineCommand({
{ flag: "--memory-library-id <id>", description: "Memory library ID" },
],
examples: [
'bl memory search --user-id user1 --query "编程偏好"',
'bl memory search --user-id user1 --messages \'[{"role":"user","content":"推荐一本书"}]\' --top-k 5',
'bl memory search --user-id user1 --query "programming preferences"',
'bl memory search --user-id user1 --messages \'[{"role":"user","content":"recommend a book"}]\' --top-k 5',
],
async run(config: Config, flags: GlobalFlags) {
const userId = flags.userId as string;
+3 -1
View File
@@ -24,7 +24,9 @@ export default defineCommand({
},
{ flag: "--memory-library-id <id>", description: "Memory library ID (non-default library)" },
],
examples: ['bl memory update --node-id node_xxx --user-id user1 --content "更新后的记忆内容"'],
examples: [
'bl memory update --node-id node_xxx --user-id user1 --content "updated memory content"',
],
async run(config: Config, flags: GlobalFlags) {
const nodeId = flags.nodeId as string;
if (!nodeId)
+8 -8
View File
@@ -21,7 +21,7 @@ import { promptText, failIfMissing } from "../../output/prompt.ts";
import { emitResult } from "../../output/output.ts";
import { resolveOutputDir, resolveCredential } from "bailian-cli-core";
const OMNI_VOICES = ["Chelsie", "Cherry", "Ethan", "Serena", "Tina"];
const OMNI_VOICES = ["Chelsie", "Cherry", "Ethan", "Serena", "Sunny", "Tina"];
/**
* Extension to input audio format.
@@ -119,14 +119,14 @@ export default defineCommand({
{ flag: "--temperature <n>", description: "Sampling temperature (0.0, 2.0]", type: "number" },
],
examples: [
'bl omni --message "你好,你是谁?"',
'bl omni --message "描述这张图片" --image ./photo.jpg',
'bl omni --message "这段音频在说什么?" --audio https://example.com/audio.wav',
'bl omni --message "总结这个视频" --video https://example.com/video.mp4',
'bl omni --message "这个视频讲了什么" --video ./local-video.mp4 --text-only',
'bl omni --message "用四川话回答:今天天气怎么样" --voice Serena',
'bl omni --message "Hello, who are you?"',
'bl omni --message "Describe this image" --image ./photo.jpg',
'bl omni --message "What is this audio saying?" --audio https://example.com/audio.wav',
'bl omni --message "Summarize this video" --video https://example.com/video.mp4',
'bl omni --message "What is this video about?" --video ./local-video.mp4 --text-only',
'bl omni --message "Answer in Sichuan dialect: How\'s the weather today?" --voice Sunny',
'bl omni --message "Hello" --text-only --output json',
'bl omni --message "朗读这段话" --audio-out greeting.wav',
'bl omni --message "Read this passage aloud" --audio-out greeting.wav',
],
async run(config: Config, flags: GlobalFlags) {
// --- Parse messages ---
+12 -19
View File
@@ -64,9 +64,9 @@ function formatRatio(usage: number, limit: number): string {
function getStatus(usage: number, limit: number): string {
if (limit <= 0) return "-";
const pct = (usage / limit) * 100;
if (pct >= 100) return "已限流";
if (pct >= 80) return "接近限流";
return "正常";
if (pct >= 100) return "Rate Limited";
if (pct >= 80) return "Near limit";
return "Normal";
}
function getNestedRecord(
@@ -193,8 +193,7 @@ function printTable(rows: CheckRow[], noColor: boolean): void {
const yellow = noColor ? (t: string) => t : (t: string) => `\x1b[33m${t}\x1b[0m`;
const red = noColor ? (t: string) => t : (t: string) => `\x1b[31m${t}\x1b[0m`;
const headersCn = ["模型", "RPM 用量/限额", "TPM 用量/限额", "状态"];
const headersEn = ["Model", "RPM Usage/Limit", "TPM Usage/Limit", "Status"];
const headers = ["Model", "RPM Usage/Limit", "TPM Usage/Limit", "Status"];
const tableRows = rows.map((r) => {
const rpmStr = r.rpmUsage < 0 ? "-" : formatRatio(r.rpmUsage, r.rpmLimit);
@@ -215,36 +214,30 @@ function printTable(rows: CheckRow[], noColor: boolean): void {
return;
}
const widths = headersCn.map((label, col) =>
Math.max(
displayWidth(label),
displayWidth(headersEn[col]),
...tableRows.map((r) => displayWidth(r.cells[col])),
),
const widths = headers.map((label, col) =>
Math.max(displayWidth(label), ...tableRows.map((r) => displayWidth(r.cells[col]))),
);
const cnLine = headersCn.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const enLine = headersEn.map((label, col) => dim(padEnd(label, widths[col]))).join(" ");
const headerLine = headers.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const separator = widths.map((w) => dim("─".repeat(w))).join("──");
process.stdout.write(cnLine + "\n");
process.stdout.write(enLine + "\n");
process.stdout.write(headerLine + "\n");
process.stdout.write(separator + "\n");
const statusCol = 3;
for (const r of tableRows) {
const cells = r.cells.map((cell, col) => {
if (col === statusCol) {
if (cell === "已限流") return red(padEnd(cell, widths[col]));
if (cell === "接近限流") return yellow(padEnd(cell, widths[col]));
if (cell === "正常") return green(padEnd(cell, widths[col]));
if (cell === "Rate Limited") return red(padEnd(cell, widths[col]));
if (cell === "Near limit") return yellow(padEnd(cell, widths[col]));
if (cell === "Normal") return green(padEnd(cell, widths[col]));
}
return padEnd(cell, widths[col]);
});
process.stdout.write(cells.join(" ") + "\n");
}
process.stdout.write(dim(`\n共 ${rows.length} 个模型 (Total: ${rows.length})`) + "\n");
process.stdout.write(dim(`\nTotal: ${rows.length} models`) + "\n");
}
export default defineCommand({
+6 -13
View File
@@ -65,8 +65,7 @@ function printTable(records: LimitApplicationItem[], noColor: boolean, total: nu
const bold = noColor ? (t: string) => t : (t: string) => `\x1b[1m${t}\x1b[0m`;
const dim = noColor ? (t: string) => t : (t: string) => `\x1b[2m${t}\x1b[0m`;
const headersCn = ["模型", "Token 账号限流", "申请时间"];
const headersEn = ["Model", "Token Limit", "Applied At"];
const headers = ["Model", "Token Limit", "Applied At"];
const rows = records.map((r) => [
r.deployedModel,
@@ -74,27 +73,21 @@ function printTable(records: LimitApplicationItem[], noColor: boolean, total: nu
formatDateTime(r.gmtCreate),
]);
const widths = headersCn.map((label, col) =>
Math.max(
displayWidth(label),
displayWidth(headersEn[col]),
...rows.map((row) => displayWidth(row[col])),
),
const widths = headers.map((label, col) =>
Math.max(displayWidth(label), ...rows.map((row) => displayWidth(row[col]))),
);
const cnLine = headersCn.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const enLine = headersEn.map((label, col) => dim(padEnd(label, widths[col]))).join(" ");
const headerLine = headers.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const separator = widths.map((w) => dim("─".repeat(w))).join("──");
process.stdout.write(cnLine + "\n");
process.stdout.write(enLine + "\n");
process.stdout.write(headerLine + "\n");
process.stdout.write(separator + "\n");
for (const row of rows) {
process.stdout.write(row.map((cell, col) => padEnd(cell, widths[col])).join(" ") + "\n");
}
process.stdout.write(dim(`\n共 ${total} 条记录 (Total: ${total})`) + "\n");
process.stdout.write(dim(`\nTotal: ${total} records`) + "\n");
}
export default defineCommand({
+6 -13
View File
@@ -108,8 +108,7 @@ function printTable(models: ModelWithQpm[], noColor: boolean): void {
const bold = noColor ? (t: string) => t : (t: string) => `\x1b[1m${t}\x1b[0m`;
const dim = noColor ? (t: string) => t : (t: string) => `\x1b[2m${t}\x1b[0m`;
const headersCn = ["模型", "RPM", "TPM", "可设上限 TPM"];
const headersEn = ["Model", "Req/min", "Token/min", "Max TPM"];
const headers = ["Model", "Req/min", "Token/min", "Max TPM"];
const rows = models.map((m) => {
const qpm = m.qpmInfo;
@@ -135,27 +134,21 @@ function printTable(models: ModelWithQpm[], noColor: boolean): void {
return;
}
const widths = headersCn.map((label, col) =>
Math.max(
displayWidth(label),
displayWidth(headersEn[col]),
...rows.map((row) => displayWidth(row[col])),
),
const widths = headers.map((label, col) =>
Math.max(displayWidth(label), ...rows.map((row) => displayWidth(row[col]))),
);
const cnLine = headersCn.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const enLine = headersEn.map((label, col) => dim(padEnd(label, widths[col]))).join(" ");
const headerLine = headers.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const separator = widths.map((w) => dim("─".repeat(w))).join("──");
process.stdout.write(cnLine + "\n");
process.stdout.write(enLine + "\n");
process.stdout.write(headerLine + "\n");
process.stdout.write(separator + "\n");
for (const row of rows) {
process.stdout.write(row.map((cell, col) => padEnd(cell, widths[col])).join(" ") + "\n");
}
process.stdout.write(dim(`\n共 ${models.length} 个模型 (Total: ${models.length})`) + "\n");
process.stdout.write(dim(`\nTotal: ${models.length} models`) + "\n");
}
export default defineCommand({
+2 -2
View File
@@ -21,9 +21,9 @@ export default defineCommand({
{ flag: "--list-tools", description: "List available MCP tools and exit" },
],
examples: [
'bl search web --query "阿里云百炼最新功能"',
'bl search web --query "Alibaba Cloud Bailian latest features"',
'bl search web --query "TypeScript 5.9 new features" --count 5',
'bl search web --query "今日新闻"',
'bl search web --query "Today\'s news"',
"bl search web --list-tools",
],
async run(config: Config, flags: GlobalFlags) {
@@ -171,7 +171,8 @@ export default defineCommand({
{ flag: "--language <lang>", description: "Language hint (e.g. zh, en, ja, ko, fr, de)" },
{
flag: "--instruction <text>",
description: 'Natural language instruction to control speech style (e.g. "请用温柔的语调")',
description:
'Natural language instruction to control speech style (e.g. "Use a gentle tone")',
},
{ flag: "--enable-ssml", description: "Enable SSML markup parsing in input text" },
{
@@ -182,13 +183,13 @@ export default defineCommand({
],
examples: [
"bl speech synthesize --list-voices --model cosyvoice-v3-flash",
'bl speech synthesize --text "你好,我是千问" --voice <voice_id>',
'bl speech synthesize --text "Hello, I am Qwen" --voice <voice_id>',
'bl speech synthesize --text "Hello world" --voice <voice_id> --language en',
"bl speech synthesize --text-file script.txt --out speech.wav --voice <voice_id>",
'bl speech synthesize --text "今天天气真好" --voice <voice_id> --instruction "请用温柔的语调说话"',
'bl speech synthesize --text "Today is a good day" --voice <voice_id> --instruction "Use a gentle tone"',
'bl speech synthesize --text "Hello" --voice <voice_id> --format wav --sample-rate 24000',
"# Stream to audio player (macOS)",
'bl speech synthesize --text "你好" --voice <voice_id> --stream | afplay -',
'bl speech synthesize --text "Hello" --voice <voice_id> --stream | afplay -',
"# Pipe to ffplay",
'bl speech synthesize --text "Hello" --voice <voice_id> --stream | ffplay -nodisp -autoexit -f s16le -ar 24000 -ac 1 -',
],
+6 -13
View File
@@ -78,8 +78,7 @@ function printTable(
typeMap: Map<string, string>,
noColor: boolean,
): void {
const headersCn = ["模型", "类型", "剩余/总量", "使用率", "过期时间", "用完即停"];
const headersEn = ["Model", "Type", "Remaining/Total", "Usage", "Expires", "Auto-Stop"];
const headers = ["Model", "Type", "Remaining/Total", "Usage", "Expires", "Auto-Stop"];
const rows = quotas.map((quota) => {
const hasQuota = quota.quotaInitTotal != null && quota.quotaTotal != null;
@@ -102,12 +101,8 @@ function printTable(
];
});
const widths = headersCn.map((label, col) =>
Math.max(
displayWidth(label),
displayWidth(headersEn[col]),
...rows.map((row) => displayWidth(row[col])),
),
const widths = headers.map((label, col) =>
Math.max(displayWidth(label), ...rows.map((row) => displayWidth(row[col]))),
);
const dim = noColor ? (text: string) => text : (text: string) => `\x1b[2m${text}\x1b[0m`;
@@ -115,13 +110,11 @@ function printTable(
const green = noColor ? (text: string) => text : (text: string) => `\x1b[32m${text}\x1b[0m`;
const yellow = noColor ? (text: string) => text : (text: string) => `\x1b[33m${text}\x1b[0m`;
const autoStopCol = headersCn.length - 1;
const cnLine = headersCn.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const enLine = headersEn.map((label, col) => dim(padEnd(label, widths[col]))).join(" ");
const autoStopCol = headers.length - 1;
const headerLine = headers.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const separator = widths.map((width) => dim("─".repeat(width))).join("──");
process.stdout.write(cnLine + "\n");
process.stdout.write(enLine + "\n");
process.stdout.write(headerLine + "\n");
process.stdout.write(separator + "\n");
for (const row of rows) {
+27 -47
View File
@@ -155,33 +155,32 @@ function resolveUsageMap(item: ModelStatisticItem): Record<string, number> {
}
interface UsageLabel {
cn: string;
en: string;
unit?: string;
}
const USAGE_KEY_LABELS: Record<string, UsageLabel> = {
total_token: { cn: "总 Token", en: "Total Tokens", unit: "tokens" },
input_token: { cn: "输入 Token", en: "Input Tokens", unit: "tokens" },
output_token: { cn: "输出 Token", en: "Output Tokens", unit: "tokens" },
input_token_cache: { cn: "缓存 Token", en: "Cached Tokens", unit: "tokens" },
input_token_cache_read: { cn: "缓存读取", en: "Cache Read", unit: "tokens" },
input_token_cache_creation: { cn: "缓存创建", en: "Cache Creation", unit: "tokens" },
thinking_input_token: { cn: "思考输入", en: "Thinking Input", unit: "tokens" },
thinking_output_token: { cn: "思考输出", en: "Thinking Output", unit: "tokens" },
text_input_token: { cn: "文本输入", en: "Text Input", unit: "tokens" },
purein_text_output_token: { cn: "文本输出", en: "Text Output", unit: "tokens" },
embedding_token: { cn: "向量", en: "Embedding", unit: "tokens" },
image_number: { cn: "图片数", en: "Images", unit: "张" },
video_duration: { cn: "视频时长", en: "Video Duration", unit: "秒" },
content_duration: { cn: "音频时长", en: "Audio Duration", unit: "秒" },
tts_text_number: { cn: "语音合成", en: "TTS Chars", unit: "字符" },
total_token_avg: { cn: "平均 Token/次", en: "Avg Tokens/Req" },
total_token: { en: "Total Tokens", unit: "tokens" },
input_token: { en: "Input Tokens", unit: "tokens" },
output_token: { en: "Output Tokens", unit: "tokens" },
input_token_cache: { en: "Cached Tokens", unit: "tokens" },
input_token_cache_read: { en: "Cache Read", unit: "tokens" },
input_token_cache_creation: { en: "Cache Creation", unit: "tokens" },
thinking_input_token: { en: "Thinking Input", unit: "tokens" },
thinking_output_token: { en: "Thinking Output", unit: "tokens" },
text_input_token: { en: "Text Input", unit: "tokens" },
purein_text_output_token: { en: "Text Output", unit: "tokens" },
embedding_token: { en: "Embedding", unit: "tokens" },
image_number: { en: "Images", unit: "images" },
video_duration: { en: "Video Duration", unit: "sec" },
content_duration: { en: "Audio Duration", unit: "sec" },
tts_text_number: { en: "TTS Chars", unit: "chars" },
total_token_avg: { en: "Avg Tokens/Req" },
};
function formatLabel(label: UsageLabel): string {
const unitSuffix = label.unit ? ` [${label.unit}]` : "";
return `${label.cn} (${label.en})${unitSuffix}`;
return `${label.en}${unitSuffix}`;
}
function printOverview(
@@ -195,12 +194,12 @@ function printOverview(
const dim = noColor ? (text: string) => text : (text: string) => `\x1b[2m${text}\x1b[0m`;
process.stdout.write(
`${dim("时间范围 Period:")} ${formatDate(startTime)} ~ ${formatDate(endTime)} ${dim(`(${days} 天)`)}\n\n`,
`${dim("Time Range Period:")} ${formatDate(startTime)} ~ ${formatDate(endTime)} ${dim(`(${days} days)`)}\n\n`,
);
const rows: [string, string][] = [
["调用模型数 (Models Called)", formatNumber(stat.modelCount ?? 0)],
["调用成功次数 (Successful Calls)", formatNumber(stat.callSuccessCount ?? 0)],
["Models Called", formatNumber(stat.modelCount ?? 0)],
["Successful Calls", formatNumber(stat.callSuccessCount ?? 0)],
];
for (const usage of stat.usages ?? []) {
@@ -226,7 +225,7 @@ function printModelTable(
const dim = noColor ? (text: string) => text : (text: string) => `\x1b[2m${text}\x1b[0m`;
process.stdout.write(
`${dim("时间范围 Period:")} ${formatDate(startTime)} ~ ${formatDate(endTime)} ${dim(`(${days} 天)`)}\n\n`,
`${dim("Time Range Period:")} ${formatDate(startTime)} ~ ${formatDate(endTime)} ${dim(`(${days} days)`)}\n\n`,
);
if (items.length === 0) {
@@ -257,20 +256,7 @@ function printModelTable(
return (idxA === -1 ? 999 : idxA) - (idxB === -1 ? 999 : idxB);
});
const headersCn = [
"模型",
"调用次数",
...orderedKeys.map((key) => {
const label = USAGE_KEY_LABELS[key];
if (!label) return key;
return label.unit ? `${label.cn} [${label.unit}]` : label.cn;
}),
];
const headersEn = [
"Model",
"Calls",
...orderedKeys.map((key) => USAGE_KEY_LABELS[key]?.en ?? key),
];
const headers = ["Model", "Calls", ...orderedKeys.map((key) => USAGE_KEY_LABELS[key]?.en ?? key)];
const rows = items.map((item, idx) => [
item.model,
formatNumber(item.callSuccessCount ?? 0),
@@ -280,20 +266,14 @@ function printModelTable(
}),
]);
const widths = headersCn.map((label, col) =>
Math.max(
displayWidth(label),
displayWidth(headersEn[col]),
...rows.map((row) => displayWidth(row[col])),
),
const widths = headers.map((label, col) =>
Math.max(displayWidth(label), ...rows.map((row) => displayWidth(row[col]))),
);
const cnLine = headersCn.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const enLine = headersEn.map((label, col) => dim(padEnd(label, widths[col]))).join(" ");
const headerLine = headers.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const separator = widths.map((width) => dim("─".repeat(width))).join("──");
process.stdout.write(cnLine + "\n");
process.stdout.write(enLine + "\n");
process.stdout.write(headerLine + "\n");
process.stdout.write(separator + "\n");
for (const row of rows) {
@@ -301,7 +281,7 @@ function printModelTable(
process.stdout.write(cells.join(" ") + "\n");
}
process.stdout.write(dim(`\n共 ${items.length} 个模型 (Total: ${items.length})`) + "\n");
process.stdout.write(dim(`\nTotal: ${items.length} models`) + "\n");
}
export default defineCommand({
+7 -4
View File
@@ -39,7 +39,10 @@ export default defineCommand({
description: "Input video URL or local file (mp4/mov, 2-10s)",
required: true,
},
{ flag: "--prompt <text>", description: 'Edit instruction (e.g. "将画面转换为黏土风格")' },
{
flag: "--prompt <text>",
description: 'Edit instruction (e.g. "Convert the scene to a claymation style")',
},
{ flag: "--ref-image <url>", description: "Reference image URL (up to 4, comma-separated)" },
{
flag: "--negative-prompt <text>",
@@ -78,10 +81,10 @@ export default defineCommand({
},
],
examples: [
'bl video edit --video https://example.com/input.mp4 --prompt "将整个画面转换为黏土风格"',
'bl video edit --video https://example.com/input.mp4 --prompt "替换衣服为图片中的款式" --ref-image https://example.com/clothes.png',
'bl video edit --video https://example.com/input.mp4 --prompt "Convert the entire scene to claymation style"',
'bl video edit --video https://example.com/input.mp4 --prompt "Replace the outfit with the style shown in the image" --ref-image https://example.com/clothes.png',
'bl video edit --video https://example.com/input.mp4 --prompt "Convert to anime style" --resolution 720P --download output.mp4',
'bl video edit --video https://example.com/input.mp4 --prompt "给视频里的小猫穿上衣服" --watermark false',
'bl video edit --video https://example.com/input.mp4 --prompt "Put clothes on the kitten in the video" --watermark false',
],
async run(config: Config, flags: GlobalFlags) {
// --- Validate video URL ---
+2 -2
View File
@@ -85,9 +85,9 @@ export default defineCommand({
},
],
examples: [
'bl video generate --prompt "一个人在读书,静态镜头"',
'bl video generate --prompt "A person reading a book, static shot"',
'bl video generate --prompt "Ocean waves at sunset." --download sunset.mp4',
'bl video generate --image https://example.com/cat.png --prompt "让画面中的猫动起来"',
'bl video generate --image https://example.com/cat.png --prompt "Make the cat in the scene move"',
'bl video generate --prompt "Mountain landscape" --resolution 1280*720 --duration 5',
'bl video generate --prompt "A cat playing with a ball" --watermark false',
],
+8 -8
View File
@@ -36,7 +36,7 @@ export default defineCommand({
{ flag: "--model <model>", description: "Model ID (default: happyhorse-1.0-r2v)" },
{
flag: "--prompt <text>",
description: "Video description with reference markers (图1, 视频1, etc.)",
description: "Video description with reference markers (image1, video1, etc.)",
required: true,
},
{
@@ -88,11 +88,11 @@ export default defineCommand({
},
],
examples: [
'bl video ref --prompt "图1在草地上奔跑" --image person.jpg',
'bl video ref --prompt "视频1在弹吉他,图1走过来" --ref-video scene.mp4 --image person.jpg',
'bl video ref --prompt "图1说话" --image person.jpg --image-voice voice.mp3 --resolution 1080P',
'bl video ref --prompt "图1和图2在对话" --image a.jpg --image b.jpg --image-voice va.mp3 --image-voice vb.mp3',
'bl video ref --prompt "图1在喝水" --image person.jpg --watermark false',
'bl video ref --prompt "Image1 running on the grass" --image person.jpg',
'bl video ref --prompt "Video 1 plays guitar, Image 1 walks over" --ref-video scene.mp4 --image person.jpg',
'bl video ref --prompt "Image 1 speaks" --image person.jpg --image-voice voice.mp3 --resolution 1080P',
'bl video ref --prompt "Image 1 and Image 2 have a conversation" --image a.jpg --image b.jpg --image-voice va.mp3 --image-voice vb.mp3',
'bl video ref --prompt "Image 1 drinks water" --image person.jpg --watermark false',
],
async run(config: Config, flags: GlobalFlags) {
// --- Validate prompt ---
@@ -100,7 +100,7 @@ export default defineCommand({
if (!prompt) {
if (isInteractive({ nonInteractive: config.nonInteractive })) {
const hint = await promptText({
message: "Enter your video prompt (use 图1, 视频1 to reference inputs):",
message: "Enter your video prompt (use Image1, Video1 to reference inputs):",
});
if (!hint) {
process.stderr.write("Video generation cancelled.\n");
@@ -119,7 +119,7 @@ export default defineCommand({
throw new BailianError(
"At least one --image or --ref-video is required.",
ExitCode.USAGE,
'bl video ref --prompt "描述" --image person.jpg',
'bl video ref --prompt "description" --image person.jpg',
);
}
+2 -2
View File
@@ -72,8 +72,8 @@ export default defineCommand({
],
examples: [
"bl vision describe --image photo.jpg",
'bl vision describe --image https://example.com/photo.jpg --prompt "这只狗是什么品种?"',
'bl vision describe --video https://example.com/video.mp4 --prompt "总结视频内容"',
'bl vision describe --image https://example.com/photo.jpg --prompt "What breed is this dog?"',
'bl vision describe --video https://example.com/video.mp4 --prompt "Summarize the video content"',
"bl vision describe --video ./local-video.mp4",
'bl vision describe --image photo.png --prompt "Extract the text" --model qwen-vl-plus',
],
+6 -15
View File
@@ -46,8 +46,7 @@ function printTable(workspaces: WorkspaceInfo[], noColor: boolean): void {
const dim = noColor ? (text: string) => text : (text: string) => `\x1b[2m${text}\x1b[0m`;
const green = noColor ? (text: string) => text : (text: string) => `\x1b[32m${text}\x1b[0m`;
const headersCn = ["空间名称", "Workspace ID", "默认空间"];
const headersEn = ["Name", "", "Default"];
const headers = ["Name", "Workspace ID", "Default"];
const rows = workspaces.map((ws) => [
ws.agentName,
@@ -55,20 +54,14 @@ function printTable(workspaces: WorkspaceInfo[], noColor: boolean): void {
ws.defaultAgent ? "Yes" : "-",
]);
const widths = headersCn.map((label, col) =>
Math.max(
displayWidth(label),
displayWidth(headersEn[col]),
...rows.map((row) => displayWidth(row[col])),
),
const widths = headers.map((label, col) =>
Math.max(displayWidth(label), ...rows.map((row) => displayWidth(row[col]))),
);
const cnLine = headersCn.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const enLine = headersEn.map((label, col) => dim(padEnd(label, widths[col]))).join(" ");
const headerLine = headers.map((label, col) => bold(padEnd(label, widths[col]))).join(" ");
const separator = widths.map((width) => dim("─".repeat(width))).join("──");
process.stdout.write(cnLine + "\n");
process.stdout.write(enLine + "\n");
process.stdout.write(headerLine + "\n");
process.stdout.write(separator + "\n");
for (const row of rows) {
@@ -79,9 +72,7 @@ function printTable(workspaces: WorkspaceInfo[], noColor: boolean): void {
process.stdout.write(cells.join(" ") + "\n");
}
process.stdout.write(
dim(`\n共 ${workspaces.length} 个空间 (Total: ${workspaces.length})`) + "\n",
);
process.stdout.write(dim(`\nTotal: ${workspaces.length}`) + "\n");
}
export default defineCommand({
+4 -4
View File
@@ -1,10 +1,10 @@
import { API_KEY_PAGE } from "../urls.ts";
const QUICK_START_TASKS = [
"帮我生成一套鸭舌帽的亚马逊电商主图(白底 + 场景图 + 模特上身图)",
"帮我生成一段 3 分钟的幽默相声音频",
"帮我生成一套小红帽故事绘本 PDF(含插图)",
"帮我分析这个视频的内容并写一篇小红书文案",
"Help me generate a set of Amazon e-commerce main images for baseball caps (white background + lifestyle shots + model wear shots)",
"Help me generate a 3-minute humorous crosstalk audio clip",
"Help me generate a Little Red Riding Hood picture-book PDF (with illustrations)",
"Help me analyze this video and write a Xiaohongshu-style post",
];
function colors() {
+11 -15
View File
@@ -96,7 +96,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
expect(data.data?.input?.supports).toBeUndefined();
});
test("quota list 文本输出包含双行表头", async () => {
test("quota list 文本输出包含英文表头", async () => {
const { stdout, stderr, exitCode } = await runCli([
"quota",
"list",
@@ -105,11 +105,9 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("模型");
expect(stdout).toContain("Model");
expect(stdout).toContain("RPM");
expect(stdout).toContain("TPM");
expect(stdout).toContain("可设上限 TPM");
expect(stdout).toContain("Req/min");
expect(stdout).toContain("Token/min");
expect(stdout).toContain("Max TPM");
});
@@ -125,7 +123,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("qwen3.6-plus");
expect(stdout).toMatch(/共 1 个模型/);
expect(stdout).toMatch(/Total: 1 models/);
});
test("quota list --model 不存在的模型报错", async () => {
@@ -235,7 +233,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
expect(data.apis).toContain("zeldaEasy.bailian-telemetry.monitor.getMonitorData");
});
test("quota check 文本输出包含双行表头", async () => {
test("quota check 文本输出包含英文表头", async () => {
const { stdout, stderr, exitCode } = await runCli([
"quota",
"check",
@@ -244,12 +242,10 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("模型");
expect(stdout).toContain("Model");
expect(stdout).toContain("RPM 用量/限额");
expect(stdout).toContain("RPM Usage/Limit");
expect(stdout).toContain("TPM 用量/限额");
expect(stdout).toContain("状态");
expect(stdout).toContain("TPM Usage/Limit");
expect(stdout).toContain("Status");
});
test("quota check --model 指定单模型", async () => {
@@ -264,7 +260,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("qwen3.6-plus");
expect(stdout).toMatch(/共 1 个模型/);
expect(stdout).toMatch(/Total: 1 models/);
});
test("quota check --model 逗号分隔多模型", async () => {
@@ -280,7 +276,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("qwen3.6-plus");
expect(stdout).toContain("qwen-plus");
expect(stdout).toMatch(/共 2 个模型/);
expect(stdout).toMatch(/Total: 2 models/);
});
test("quota check JSON 输出包含用量和限额字段", async () => {
@@ -311,7 +307,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
expect(data[0].tpmLimit).toBeTypeOf("number");
});
test("quota check 状态列显示正常/接近限流/已限流之一", async () => {
test("quota check 状态列显示 Normal/Near limit/Rate Limited 之一", async () => {
const { stdout, stderr, exitCode } = await runCli([
"quota",
"check",
@@ -323,7 +319,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: quota(Console)", () => {
]);
expect(exitCode, stderr).toBe(0);
const hasStatus =
stdout.includes("正常") || stdout.includes("接近限流") || stdout.includes("已限流");
stdout.includes("Normal") || stdout.includes("Near limit") || stdout.includes("Rate Limited");
expect(hasStatus).toBe(true);
});
+21 -8
View File
@@ -176,8 +176,8 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
expect(data.successResponse).toBe(true);
});
test("usage stats 概览文本输出包含中英文表头", async () => {
const { stderr, exitCode } = await runCli([
test("usage stats 概览文本输出包含英文标签", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
@@ -187,10 +187,13 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("Time Range Period:");
expect(stdout).toContain("Models Called");
expect(stdout).toContain("Successful Calls");
});
test("usage stats 概览文本输出包含 Token 用量", async () => {
const { stderr, exitCode } = await runCli([
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
@@ -200,10 +203,11 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toMatch(/Total Tokens|Input Tokens|Output Tokens/);
});
test("usage stats --model 单模型文本输出包含双行表头", async () => {
const { stderr, exitCode } = await runCli([
test("usage stats --model 单模型文本输出包含英文表头", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
@@ -215,10 +219,14 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("Time Range Period:");
expect(stdout).toContain("Model");
expect(stdout).toContain("Calls");
expect(stdout).toMatch(/Total: \d+ models/);
});
test("usage stats --model 逗号分隔多模型返回多行", async () => {
const { stderr, exitCode } = await runCli([
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
@@ -230,6 +238,9 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("qwen3.6-plus");
expect(stdout).toContain("deepseek-v4-pro");
expect(stdout).toMatch(/Total: 2 models/);
});
test("usage stats --model 不存在的模型返回空表格", async () => {
@@ -249,7 +260,7 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
});
test("usage stats --days 1 短时间范围正常返回", async () => {
const { stderr, exitCode } = await runCli([
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
@@ -261,10 +272,11 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("(1 days)");
});
test("usage stats --type Vision 按类型过滤", async () => {
const { stderr, exitCode } = await runCli([
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
@@ -276,5 +288,6 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("Time Range Period:");
});
});
+3 -3
View File
@@ -140,7 +140,7 @@ Commonly used:
```bash
# Chat
bl text chat --message "用中文写一首关于春天的诗"
bl text chat --message "Write a poem about spring in Chinese"
# Image
bl image generate --prompt "A cat in space" --out-dir ./out/
@@ -149,11 +149,11 @@ bl image generate --prompt "A cat in space" --out-dir ./out/
bl video generate --prompt "Sunset on the beach" --download sunset.mp4
# Omni (local files OK)
bl omni --message "描述视频内容" --video ./demo.mp4 --text-only
bl omni --message "Describe the video content" --video ./demo.mp4 --text-only
# App
bl app list --output json
bl app call --app-id <code> --prompt "你好"
bl app call --app-id <code> --prompt "Hello"
```
More examples per command: see `reference/<group>.md` (e.g. [`reference/text.md`](reference/text.md)).
+5 -5
View File
@@ -137,9 +137,9 @@ If it still fails with INCLUDE signals → offer reporting.
When INCLUDE matches, ask in **Chinese** (adjust if the user prefers English):
> `bl` 命令出现了疑似 CLI 自身的问题。
> 是否需要帮你整理信息,向百炼 CLI 团队提交 GitHub Issue?
> 提交前会自动脱敏 API Key;你也可以只复制模版自行提交。
> The `bl` command hit what looks like a CLI bug.
> Would you like help gathering details to file a GitHub Issue with the Bailian CLI team?
> API Keys will be redacted automatically before submission; you can also copy the template and submit yourself.
If the user agrees → [Collect information](#collect-information) → [Submit](#submit).
@@ -290,7 +290,7 @@ If a matching open issue exists:
Before submitting, **always show the redacted issue body to the user** and ask for confirmation:
> 以下是即将提交的 Issue 内容(已脱敏),请确认是否提交:
> Below is the redacted Issue content to be submitted—confirm submission?
> show body
Only proceed after the user confirms.
@@ -329,7 +329,7 @@ If `gh` is not installed or not authenticated:
1. Write the complete redacted issue body to a local file (e.g. `./cli-bug-report.md`)
2. Print the file content to the user
3. Provide the direct URL: [https://github.com/modelstudioai/cli/issues/new?template=bug_report.yml](https://github.com/modelstudioai/cli/issues/new?template=bug_report.yml)
4. Instruct: "请在浏览器中打开上面的链接,将内容粘贴到 issue body 中提交。"
4. Instruct: "Open the link above in your browser and paste the content into the issue body to submit."
Do **not** block on `gh` — always provide a manual path.
+7 -7
View File
@@ -40,27 +40,27 @@ Index: [index.md](index.md)
#### Examples
```bash
bl app call --app-id abc123 --prompt "你好"
bl app call --app-id abc123 --prompt "Hello"
```
```bash
bl app call --app-id abc123 --prompt "描述这张图片" --image https://example.com/photo.jpg
bl app call --app-id abc123 --prompt "Describe this image" --image https://example.com/photo.jpg
```
```bash
bl app call --app-id abc123 --prompt "分析图片" --image img1.jpg --image img2.jpg
bl app call --app-id abc123 --prompt "Analyze the image" --image img1.jpg --image img2.jpg
```
```bash
bl app call --app-id abc123 --prompt "继续" --session-id sess_xxx --stream
bl app call --app-id abc123 --prompt "Continue" --session-id sess_xxx --stream
```
```bash
bl app call --app-id abc123 --prompt "搜索资料" --pipeline-ids pipe1,pipe2
bl app call --app-id abc123 --prompt "Search for materials" --pipeline-ids pipe1,pipe2
```
```bash
bl app call --app-id abc123 --prompt "开始" --biz-params '{"key":"value"}'
bl app call --app-id abc123 --prompt "Start" --biz-params '{"key":"value"}'
```
### `bl app list`
@@ -87,7 +87,7 @@ bl app list
```
```bash
bl app list --name 客服
bl app list --name customer service
```
```bash
+4 -4
View File
@@ -41,7 +41,7 @@ Index: [index.md](index.md)
#### Examples
```bash
bl image edit --image ./photo.png --prompt "把背景换成海滩"
bl image edit --image ./photo.png --prompt "Replace the background with a beach"
```
```bash
@@ -49,7 +49,7 @@ bl image edit --image https://example.com/logo.png --prompt "Change color to blu
```
```bash
bl image edit --image ./a.png --image ./b.png --prompt "把两张图合并成一张拼图"
bl image edit --image ./a.png --image ./b.png --prompt "Merge two images into one collage"
```
```bash
@@ -57,7 +57,7 @@ bl image edit --image https://example.com/photo.png --prompt "Remove the person"
```
```bash
bl image edit --image ./photo.png --prompt "把背景换成海滩" --watermark false
bl image edit --image ./photo.png --prompt "Replace the background with a beach" --watermark false
```
### `bl image generate`
@@ -88,7 +88,7 @@ bl image edit --image ./photo.png --prompt "把背景换成海滩" --watermark f
#### Examples
```bash
bl image generate --prompt "一只穿太空服的猫在火星上"
bl image generate --prompt "A cat in a spacesuit on Mars"
```
```bash
+2 -2
View File
@@ -47,9 +47,9 @@ Index: [index.md](index.md)
#### Examples
```bash
bl knowledge retrieve --index-id idx_xxx --query "如何使用阿里云百炼"
bl knowledge retrieve --index-id idx_xxx --query "How to use Alibaba Cloud Bailian"
```
```bash
bl knowledge retrieve --api-key $DASHSCOPE_API_KEY --index-id idx_xxx --query "RAG检索" --rerank --rerank-model qwen3-rerank-hybrid
bl knowledge retrieve --api-key $DASHSCOPE_API_KEY --index-id idx_xxx --query "RAG retrieval" --rerank --rerank-model qwen3-rerank-hybrid
```
+3 -3
View File
@@ -36,11 +36,11 @@ Index: [index.md](index.md)
#### Examples
```bash
bl mcp call market-cmapi00073529.SmartStockSelection --query "筛选ROE>15%的消费股"
bl mcp call market-cmapi00073529.SmartStockSelection --query "Screen consumer stocks with ROE > 15%"
```
```bash
bl mcp call market-cmapi00073529.FinQuery --json '{"q":"贵州茅台","limit":5}'
bl mcp call market-cmapi00073529.FinQuery --json '{"q":"Guizhou Maotai","limit":5}'
```
```bash
@@ -72,7 +72,7 @@ bl mcp list
```
```bash
bl mcp list --name 金融
bl mcp list --name finance
```
```bash
+12 -12
View File
@@ -40,15 +40,15 @@ Index: [index.md](index.md)
#### Examples
```bash
bl memory add --user-id user1 --content "用户喜欢Python编程"
bl memory add --user-id user1 --content "The user likes Python programming"
```
```bash
bl memory add --user-id user1 --messages '[{"role":"user","content":"我喜欢旅行"}]'
bl memory add --user-id user1 --messages '[{"role":"user","content":"I like traveling"}]'
```
```bash
bl memory add --user-id user1 --content "住在北京" --profile-schema schema_xxx
bl memory add --user-id user1 --content "Lives in Beijing" --profile-schema schema_xxx
```
### `bl memory delete`
@@ -110,16 +110,16 @@ bl memory list --user-id user1 --page-size 20 --page 2
#### Options
| Flag | Type | Required | Description |
| ---------------------- | ------ | -------- | ------------------------------------------------------------ |
| `--name <name>` | string | yes | Schema name (required) |
| `--description <text>` | string | no | Schema description |
| `--attributes <json>` | string | yes | Attributes JSON array: [{"name":"age","description":"年龄"}] |
| Flag | Type | Required | Description |
| ---------------------- | ------ | -------- | ----------------------------------------------------------- |
| `--name <name>` | string | yes | Schema name (required) |
| `--description <text>` | string | no | Schema description |
| `--attributes <json>` | string | yes | Attributes JSON array: [{"name":"age","description":"age"}] |
#### Examples
```bash
bl memory profile create --name "user_basic" --attributes '[{"name":"age","description":"年龄"},{"name":"hobby","description":"爱好"}]'
bl memory profile create --name "user_basic" --attributes '[{"name":"age","description":"age"},{"name":"hobby","description":"hobby"}]'
```
### `bl memory profile get`
@@ -164,11 +164,11 @@ bl memory profile get --schema-id schema_xxx --user-id user1
#### Examples
```bash
bl memory search --user-id user1 --query "编程偏好"
bl memory search --user-id user1 --query "programming preferences"
```
```bash
bl memory search --user-id user1 --messages '[{"role":"user","content":"推荐一本书"}]' --top-k 5
bl memory search --user-id user1 --messages '[{"role":"user","content":"recommend a book"}]' --top-k 5
```
### `bl memory update`
@@ -191,5 +191,5 @@ bl memory search --user-id user1 --messages '[{"role":"user","content":"推荐
#### Examples
```bash
bl memory update --node-id node_xxx --user-id user1 --content "更新后的记忆内容"
bl memory update --node-id node_xxx --user-id user1 --content "updated memory content"
```
+21 -21
View File
@@ -23,45 +23,45 @@ Index: [index.md](index.md)
#### Options
| Flag | Type | Required | Description |
| ---------------------- | ------- | -------- | ----------------------------------------------------------------------------- |
| `--message <text>` | array | yes | Message text (repeatable, prefix role: to set role) |
| `--model <model>` | string | no | Model ID (default: qwen3.5-omni-plus) |
| `--system <text>` | string | no | System prompt |
| `--image <url>` | array | no | Image URL or local file (repeatable) |
| `--audio <url>` | array | no | Audio URL or local file (.wav/.mp3/.amr/.aac/.m4a/.ogg/.3gp/.3gpp) |
| `--video <url>` | array | no | Video file URL / local path, or comma-separated frame URLs |
| `--voice <voice>` | string | no | Output voice (default: Cherry). Options: Chelsie, Cherry, Ethan, Serena, Tina |
| `--audio-format <fmt>` | string | no | Audio output format (default: wav) |
| `--audio-out <path>` | string | no | Save audio to file (default: auto-generate) |
| `--text-only` | boolean | no | Output text only, no audio generation |
| `--max-tokens <n>` | number | no | Maximum tokens to generate |
| `--temperature <n>` | number | no | Sampling temperature (0.0, 2.0] |
| Flag | Type | Required | Description |
| ---------------------- | ------- | -------- | ------------------------------------------------------------------------------------ |
| `--message <text>` | array | yes | Message text (repeatable, prefix role: to set role) |
| `--model <model>` | string | no | Model ID (default: qwen3.5-omni-plus) |
| `--system <text>` | string | no | System prompt |
| `--image <url>` | array | no | Image URL or local file (repeatable) |
| `--audio <url>` | array | no | Audio URL or local file (.wav/.mp3/.amr/.aac/.m4a/.ogg/.3gp/.3gpp) |
| `--video <url>` | array | no | Video file URL / local path, or comma-separated frame URLs |
| `--voice <voice>` | string | no | Output voice (default: Cherry). Options: Chelsie, Cherry, Ethan, Serena, Sunny, Tina |
| `--audio-format <fmt>` | string | no | Audio output format (default: wav) |
| `--audio-out <path>` | string | no | Save audio to file (default: auto-generate) |
| `--text-only` | boolean | no | Output text only, no audio generation |
| `--max-tokens <n>` | number | no | Maximum tokens to generate |
| `--temperature <n>` | number | no | Sampling temperature (0.0, 2.0] |
#### Examples
```bash
bl omni --message "你好,你是谁?"
bl omni --message "Hello, who are you?"
```
```bash
bl omni --message "描述这张图片" --image ./photo.jpg
bl omni --message "Describe this image" --image ./photo.jpg
```
```bash
bl omni --message "这段音频在说什么?" --audio https://example.com/audio.wav
bl omni --message "What is this audio saying?" --audio https://example.com/audio.wav
```
```bash
bl omni --message "总结这个视频" --video https://example.com/video.mp4
bl omni --message "Summarize this video" --video https://example.com/video.mp4
```
```bash
bl omni --message "这个视频讲了什么" --video ./local-video.mp4 --text-only
bl omni --message "What is this video about?" --video ./local-video.mp4 --text-only
```
```bash
bl omni --message "用四川话回答:今天天气怎么样" --voice Serena
bl omni --message "Answer in Sichuan dialect: How's the weather today?" --voice Sunny
```
```bash
@@ -69,5 +69,5 @@ bl omni --message "Hello" --text-only --output json
```
```bash
bl omni --message "朗读这段话" --audio-out greeting.wav
bl omni --message "Read this passage aloud" --audio-out greeting.wav
```
+2 -2
View File
@@ -32,7 +32,7 @@ Index: [index.md](index.md)
#### Examples
```bash
bl search web --query "阿里云百炼最新功能"
bl search web --query "Alibaba Cloud Bailian latest features"
```
```bash
@@ -40,7 +40,7 @@ bl search web --query "TypeScript 5.9 new features" --count 5
```
```bash
bl search web --query "今日新闻"
bl search web --query "Today's news"
```
```bash
+4 -4
View File
@@ -91,7 +91,7 @@ bl speech recognize --url https://example.com/audio.mp3 --no-wait --quiet
| `--pitch <pitch>` | string | no | Pitch multiplier 0.5-2.0 (default: 1.0) |
| `--seed <seed>` | string | no | Random seed 0-65535 for reproducible synthesis |
| `--language <lang>` | string | no | Language hint (e.g. zh, en, ja, ko, fr, de) |
| `--instruction <text>` | string | no | Natural language instruction to control speech style (e.g. "请用温柔的语调") |
| `--instruction <text>` | string | no | Natural language instruction to control speech style (e.g. "Use a gentle tone") |
| `--enable-ssml` | boolean | no | Enable SSML markup parsing in input text |
| `--out <path>` | string | no | Save audio to file (default: auto-generate in temp dir) |
| `--stream` | boolean | no | Stream raw PCM audio to stdout (pipe to player) |
@@ -103,7 +103,7 @@ bl speech synthesize --list-voices --model cosyvoice-v3-flash
```
```bash
bl speech synthesize --text "你好,我是千问" --voice <voice_id>
bl speech synthesize --text "Hello, I am Qwen" --voice <voice_id>
```
```bash
@@ -115,7 +115,7 @@ bl speech synthesize --text-file script.txt --out speech.wav --voice <voice_id>
```
```bash
bl speech synthesize --text "今天天气真好" --voice <voice_id> --instruction "请用温柔的语调说话"
bl speech synthesize --text "Today is a good day" --voice <voice_id> --instruction "Use a gentle tone"
```
```bash
@@ -127,7 +127,7 @@ bl speech synthesize --text "Hello" --voice <voice_id> --format wav --sample-rat
```
```bash
bl speech synthesize --text "你好" --voice <voice_id> --stream | afplay -
bl speech synthesize --text "Hello" --voice <voice_id> --stream | afplay -
```
```bash
+12 -12
View File
@@ -56,7 +56,7 @@ bl video download --task-id 3b256896-xxxx --out video.mp4 --quiet
| --------------------------- | ------- | -------- | --------------------------------------------------------------------------------------- |
| `--model <model>` | string | no | Model ID (default: happyhorse-1.0-video-edit) |
| `--video <url>` | string | yes | Input video URL or local file (mp4/mov, 2-10s) |
| `--prompt <text>` | string | no | Edit instruction (e.g. "将画面转换为黏土风格") |
| `--prompt <text>` | string | no | Edit instruction (e.g. "Convert the scene to a claymation style") |
| `--ref-image <url>` | string | no | Reference image URL (up to 4, comma-separated) |
| `--negative-prompt <text>` | string | no | Negative prompt to exclude unwanted content |
| `--resolution <res>` | string | no | Resolution: 720P or 1080P (default: 1080P) |
@@ -74,11 +74,11 @@ bl video download --task-id 3b256896-xxxx --out video.mp4 --quiet
#### Examples
```bash
bl video edit --video https://example.com/input.mp4 --prompt "将整个画面转换为黏土风格"
bl video edit --video https://example.com/input.mp4 --prompt "Convert the entire scene to claymation style"
```
```bash
bl video edit --video https://example.com/input.mp4 --prompt "替换衣服为图片中的款式" --ref-image https://example.com/clothes.png
bl video edit --video https://example.com/input.mp4 --prompt "Replace the outfit with the style shown in the image" --ref-image https://example.com/clothes.png
```
```bash
@@ -86,7 +86,7 @@ bl video edit --video https://example.com/input.mp4 --prompt "Convert to anime s
```
```bash
bl video edit --video https://example.com/input.mp4 --prompt "给视频里的小猫穿上衣服" --watermark false
bl video edit --video https://example.com/input.mp4 --prompt "Put clothes on the kitten in the video" --watermark false
```
### `bl video generate`
@@ -119,7 +119,7 @@ bl video edit --video https://example.com/input.mp4 --prompt "给视频里的小
#### Examples
```bash
bl video generate --prompt "一个人在读书,静态镜头"
bl video generate --prompt "A person reading a book, static shot"
```
```bash
@@ -127,7 +127,7 @@ bl video generate --prompt "Ocean waves at sunset." --download sunset.mp4
```
```bash
bl video generate --image https://example.com/cat.png --prompt "让画面中的猫动起来"
bl video generate --image https://example.com/cat.png --prompt "Make the cat in the scene move"
```
```bash
@@ -151,7 +151,7 @@ bl video generate --prompt "A cat playing with a ball" --watermark false
| Flag | Type | Required | Description |
| --------------------------- | ------- | -------- | --------------------------------------------------------------------------------------- |
| `--model <model>` | string | no | Model ID (default: happyhorse-1.0-r2v) |
| `--prompt <text>` | string | yes | Video description with reference markers (图1, 视频1, etc.) |
| `--prompt <text>` | string | yes | Video description with reference markers (image1, video1, etc.) |
| `--image <url>` | array | no | Reference image URL or local file (repeatable for multiple subjects) |
| `--ref-video <url>` | array | no | Reference video URL or local file (repeatable) |
| `--image-voice <url>` | array | no | Voice URL for corresponding image (pairs by position) |
@@ -170,23 +170,23 @@ bl video generate --prompt "A cat playing with a ball" --watermark false
#### Examples
```bash
bl video ref --prompt "图1在草地上奔跑" --image person.jpg
bl video ref --prompt "Image1 running on the grass" --image person.jpg
```
```bash
bl video ref --prompt "视频1在弹吉他,图1走过来" --ref-video scene.mp4 --image person.jpg
bl video ref --prompt "Video 1 plays guitar, Image 1 walks over" --ref-video scene.mp4 --image person.jpg
```
```bash
bl video ref --prompt "图1说话" --image person.jpg --image-voice voice.mp3 --resolution 1080P
bl video ref --prompt "Image 1 speaks" --image person.jpg --image-voice voice.mp3 --resolution 1080P
```
```bash
bl video ref --prompt "图1和图2在对话" --image a.jpg --image b.jpg --image-voice va.mp3 --image-voice vb.mp3
bl video ref --prompt "Image 1 and Image 2 have a conversation" --image a.jpg --image b.jpg --image-voice va.mp3 --image-voice vb.mp3
```
```bash
bl video ref --prompt "图1在喝水" --image person.jpg --watermark false
bl video ref --prompt "Image 1 drinks water" --image person.jpg --watermark false
```
### `bl video task get`
+2 -2
View File
@@ -37,11 +37,11 @@ bl vision describe --image photo.jpg
```
```bash
bl vision describe --image https://example.com/photo.jpg --prompt "这只狗是什么品种?"
bl vision describe --image https://example.com/photo.jpg --prompt "What breed is this dog?"
```
```bash
bl vision describe --video https://example.com/video.mp4 --prompt "总结视频内容"
bl vision describe --video https://example.com/video.mp4 --prompt "Summarize the video content"
```
```bash