优化模型接入,解除模型写死限制
This commit is contained in:
parent
3dba471500
commit
869bb5e998
@ -554,6 +554,557 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
|
||||
]);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "t_textModel",
|
||||
builder: (table) => {
|
||||
table.integer("id").notNullable();
|
||||
table.text("manufacturer");
|
||||
table.text("model");
|
||||
table.text("responseFormat");
|
||||
table.integer("image");
|
||||
table.integer("think");
|
||||
table.integer("tool");
|
||||
table.primary(["id"]);
|
||||
},
|
||||
initData: async (knex) => {
|
||||
await knex("t_textModel").insert([
|
||||
{ manufacturer: "deepSeek", model: "deepseek-chat", responseFormat: "schema", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "deepSeek", model: "deepseek-reasoner", responseFormat: "schema", image: 0, think: 1, tool: 1 },
|
||||
{ manufacturer: "volcengine", model: "doubao-seed-2-0-pro-260215", responseFormat: "object", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "volcengine", model: "doubao-seed-2-0-lite-260215", responseFormat: "object", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "volcengine", model: "doubao-seed-2-0-mini-260215", responseFormat: "object", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "volcengine", model: "doubao-seed-1-8-251228", responseFormat: "schema", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "volcengine", model: "doubao-seed-1-6-251015", responseFormat: "schema", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "volcengine", model: "doubao-seed-1-6-lite-251015", responseFormat: "schema", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "volcengine", model: "doubao-seed-1-6-flash-250828", responseFormat: "schema", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.7", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.7-flashx", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.6", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.5-air", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.5-airx", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4-long", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4-flashx-250414", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.7-flash", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.5-flash", responseFormat: "object", image: 0, think: 1, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4-flash-250414", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "zhipu", model: "glm-4.6v", responseFormat: "object", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "qwen", model: "qwen-vl-max", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "qwen", model: "qwen-plus-latest", responseFormat: "schema", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "qwen", model: "qwen-max", responseFormat: "schema", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "qwen", model: "qwen2.5-72b-instruct", responseFormat: "schema", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "qwen", model: "qwen2.5-14b-instruct-1m", responseFormat: "schema", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "qwen", model: "qwen2.5-vl-72b-instruct", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "openai", model: "gpt-4o", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "openai", model: "gpt-4o-mini", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "openai", model: "gpt-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "openai", model: "gpt-5.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "openai", model: "gpt-5.2", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "gemini", model: "gemini-2.5-pro", responseFormat: "schema", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "gemini", model: "gemini-2.5-flash", responseFormat: "schema", image: 1, think: 1, tool: 1 },
|
||||
{ manufacturer: "gemini", model: "gemini-2.0-flash", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "gemini", model: "gemini-2.0-flash-lite", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "gemini", model: "gemini-1.5-pro", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "gemini", model: "gemini-1.5-flash", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-opus-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-haiku-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-sonnet-4-5", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-opus-4-1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-opus-4-0", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-sonnet-4-0", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-3-7-sonnet-latest", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "anthropic", model: "claude-3-5-haiku-latest", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "xai", model: "grok-3", responseFormat: "schema", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "xai", model: "grok-4", responseFormat: "schema", image: 0, think: 0, tool: 1 },
|
||||
{ manufacturer: "xai", model: "grok-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "other", model: "gpt-4.1", responseFormat: "schema", image: 1, think: 0, tool: 1 },
|
||||
{ manufacturer: "modelScope", model: "deepseek-ai/DeepSeek-V3.2", responseFormat: "object", image: 0, think: 0, tool: 1 },
|
||||
]);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "t_imageModel",
|
||||
builder: (table) => {
|
||||
table.integer("id").notNullable();
|
||||
table.text("manufacturer");
|
||||
table.text("model");
|
||||
table.integer("grid");
|
||||
table.text("type");
|
||||
table.primary(["id"]);
|
||||
},
|
||||
initData: async (knex) => {
|
||||
await knex("t_imageModel").insert([
|
||||
{ id: 1, manufacturer: "volcengine", model: "doubao-seedream-4-5-251128", grid: 0, type: "ti2i" },
|
||||
{ id: 2, manufacturer: "volcengine", model: "doubao-seedream-4-0-250828", grid: 0, type: "ti2i" },
|
||||
{ id: 3, manufacturer: "kling", model: "kling-image-o1", grid: 0, type: "ti2i" },
|
||||
{ id: 4, manufacturer: "gemini", model: "gemini-2.5-flash-image", grid: 1, type: "ti2i" },
|
||||
{ id: 5, manufacturer: "gemini", model: "gemini-3-pro-image-preview", grid: 1, type: "ti2i" },
|
||||
{ id: 6, manufacturer: "vidu", model: "viduq1", grid: 0, type: "i2i" },
|
||||
{ id: 7, manufacturer: "vidu", model: "viduq2", grid: 0, type: "ti2i" },
|
||||
{ id: 8, manufacturer: "runninghub", model: "nanobanana", grid: 1, type: "ti2i" },
|
||||
{ id: 9, manufacturer: "modelScope", model: "Qwen/Qwen-Image", grid: 1, type: "ti2i" },
|
||||
]);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "t_videoModel",
|
||||
builder: (table) => {
|
||||
table.integer("id").notNullable();
|
||||
table.text("manufacturer");
|
||||
table.text("model");
|
||||
table.text("durationResolutionMap");
|
||||
table.text("aspectRatio");
|
||||
table.integer("audio");
|
||||
table.text("type");
|
||||
table.primary(["id"]);
|
||||
},
|
||||
initData: async (knex) => {
|
||||
await knex("t_videoModel").insert([
|
||||
{
|
||||
id: 1,
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seedance-1-5-pro-251215",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text", "endFrameOptional"]),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seedance-1-0-pro-250528",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text", "endFrameOptional"]),
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seedance-1-0-pro-fast-251015",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text", "singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seedance-1-0-lite-i2v-250428",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["endFrameOptional", "reference"]),
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seedance-1-0-lite-t2v-250428",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v1(STD)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["720p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v1(STD)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["720p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v1(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v1(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v1-6(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v1-6(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v2-5-turbo(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v2-5-turbo(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v2-6(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "1:1", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
manufacturer: "kling",
|
||||
model: "kling-v2-6(PRO)",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq3-pro",
|
||||
durationResolutionMap: JSON.stringify([
|
||||
{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], resolution: ["540p", "720p", "1080p"] },
|
||||
]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq3-pro",
|
||||
durationResolutionMap: JSON.stringify([
|
||||
{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], resolution: ["540p", "720p", "1080p"] },
|
||||
]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq2-pro-fast",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq2-pro",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq2-pro",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq2-turbo",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "3:4", "4:3", "1:1"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq2-turbo",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], resolution: ["540p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq1",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "1:1"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq1",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
manufacturer: "vidu",
|
||||
model: "viduq1-classic",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
manufacturer: "vidu",
|
||||
model: "vidu2.0",
|
||||
durationResolutionMap: JSON.stringify([
|
||||
{ duration: [4], resolution: ["360p", "720p", "1080p"] },
|
||||
{ duration: [8], resolution: ["720p"] },
|
||||
]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "reference", "startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.6-t2v",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "1:1", "4:3", "3:4"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.5-t2v-preview",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "1:1", "4:3", "3:4"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.2-t2v-plus",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "1:1", "4:3", "3:4"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
manufacturer: "wan",
|
||||
model: "wanx2.1-t2v-turbo",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "1:1", "4:3", "3:4"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
manufacturer: "wan",
|
||||
model: "wanx2.1-t2v-plus",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["720p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16", "1:1", "4:3", "3:4"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text"]),
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.6-i2v-flash",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.6-i2v",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 34,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.5-i2v-preview",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 10], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 35,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.2-i2v-flash",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 36,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.2-i2v-plus",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 37,
|
||||
manufacturer: "wan",
|
||||
model: "wanx2.1-i2v-plus",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["720p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 38,
|
||||
manufacturer: "wan",
|
||||
model: "wanx2.1-i2v-turbo",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [3, 4, 5], resolution: ["480p", "720p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 39,
|
||||
manufacturer: "wan",
|
||||
model: "wan2.2-kf2v-flash",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["480p", "720p", "1080p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 40,
|
||||
manufacturer: "wan",
|
||||
model: "wanx2.1-kf2v-plus",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5], resolution: ["720p"] }]),
|
||||
aspectRatio: JSON.stringify([]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["startEndRequired"]),
|
||||
},
|
||||
{
|
||||
id: 41,
|
||||
manufacturer: "gemini",
|
||||
model: "veo-3.1-generate-preview",
|
||||
durationResolutionMap: JSON.stringify([
|
||||
{ duration: [4, 6], resolution: ["720p"] },
|
||||
{ duration: [8], resolution: ["720p", "1080p"] },
|
||||
]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text", "singleImage", "startEndRequired", "endFrameOptional", "reference"]),
|
||||
},
|
||||
{
|
||||
id: 42,
|
||||
manufacturer: "gemini",
|
||||
model: "veo-3.1-fast-generate-preview",
|
||||
durationResolutionMap: JSON.stringify([
|
||||
{ duration: [4, 6], resolution: ["720p"] },
|
||||
{ duration: [8], resolution: ["720p", "1080p"] },
|
||||
]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text", "singleImage", "startEndRequired", "endFrameOptional", "reference"]),
|
||||
},
|
||||
{
|
||||
id: 43,
|
||||
manufacturer: "gemini",
|
||||
model: "veo-3.0-generate-preview",
|
||||
durationResolutionMap: JSON.stringify([
|
||||
{ duration: [4, 6], resolution: ["720p"] },
|
||||
{ duration: [8], resolution: ["720p", "1080p"] },
|
||||
]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text", "singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 44,
|
||||
manufacturer: "gemini",
|
||||
model: "veo-3.0-fast-generate-preview",
|
||||
durationResolutionMap: JSON.stringify([
|
||||
{ duration: [4, 6], resolution: ["720p"] },
|
||||
{ duration: [8], resolution: ["720p", "1080p"] },
|
||||
]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16"]),
|
||||
audio: 1,
|
||||
type: JSON.stringify(["text", "singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 45,
|
||||
manufacturer: "gemini",
|
||||
model: "veo-2.0-generate-001",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [5, 6, 7, 8], resolution: ["720p"] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["text", "singleImage"]),
|
||||
},
|
||||
{
|
||||
id: 46,
|
||||
manufacturer: "runninghub",
|
||||
model: "sora-2",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [10, 15], resolution: [] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "text"]),
|
||||
},
|
||||
{
|
||||
id: 47,
|
||||
manufacturer: "runninghub",
|
||||
model: "sora-2-pro",
|
||||
durationResolutionMap: JSON.stringify([{ duration: [15, 25], resolution: [] }]),
|
||||
aspectRatio: JSON.stringify(["16:9", "9:16"]),
|
||||
audio: 0,
|
||||
type: JSON.stringify(["singleImage", "text"]),
|
||||
},
|
||||
]);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const t of tables) {
|
||||
|
||||
130
src/router.ts
130
src/router.ts
@ -1,4 +1,4 @@
|
||||
// @routes-hash a5e432459af85c08bbc13a86444f292c
|
||||
// @routes-hash 88e9c15b913a8fd111d3adf162583c91
|
||||
import { Express } from "express";
|
||||
|
||||
import route1 from "./routes/assets/addAssets";
|
||||
@ -48,37 +48,39 @@ import route44 from "./routes/script/geScriptApi";
|
||||
import route45 from "./routes/setting/addModel";
|
||||
import route46 from "./routes/setting/configurationModel";
|
||||
import route47 from "./routes/setting/delModel";
|
||||
import route48 from "./routes/setting/getAiModelMap";
|
||||
import route49 from "./routes/setting/getLog";
|
||||
import route50 from "./routes/setting/getSetting";
|
||||
import route51 from "./routes/setting/getVideoModelList";
|
||||
import route52 from "./routes/setting/updateModel";
|
||||
import route53 from "./routes/setting/updeteModel";
|
||||
import route54 from "./routes/storyboard/batchSuperScoreImage";
|
||||
import route55 from "./routes/storyboard/chatStoryboard";
|
||||
import route56 from "./routes/storyboard/generateShotImage";
|
||||
import route57 from "./routes/storyboard/generateStoryboardApi";
|
||||
import route58 from "./routes/storyboard/generateVideoPrompt";
|
||||
import route59 from "./routes/storyboard/getStoryboard";
|
||||
import route60 from "./routes/storyboard/keepStoryboard";
|
||||
import route61 from "./routes/storyboard/saveStoryboard";
|
||||
import route62 from "./routes/storyboard/uploadImage";
|
||||
import route63 from "./routes/task/getTaskApi";
|
||||
import route64 from "./routes/task/taskDetails";
|
||||
import route65 from "./routes/user/getUser";
|
||||
import route66 from "./routes/video/addVideo";
|
||||
import route67 from "./routes/video/addVideoConfig";
|
||||
import route68 from "./routes/video/deleteVideoConfig";
|
||||
import route69 from "./routes/video/generatePrompt";
|
||||
import route70 from "./routes/video/generateVideo";
|
||||
import route71 from "./routes/video/getManufacturer";
|
||||
import route72 from "./routes/video/getVideo";
|
||||
import route73 from "./routes/video/getVideoConfigs";
|
||||
import route74 from "./routes/video/getVideoModel";
|
||||
import route75 from "./routes/video/getVideoStoryboards";
|
||||
import route76 from "./routes/video/reviseVideoStoryboards";
|
||||
import route77 from "./routes/video/saveVideo";
|
||||
import route78 from "./routes/video/upDateVideoConfig";
|
||||
import route48 from "./routes/setting/getAiModelList";
|
||||
import route49 from "./routes/setting/getAiModelMap";
|
||||
import route50 from "./routes/setting/getLog";
|
||||
import route51 from "./routes/setting/getSetting";
|
||||
import route52 from "./routes/setting/getVideoModelDetail";
|
||||
import route53 from "./routes/setting/getVideoModelList";
|
||||
import route54 from "./routes/setting/updateModel";
|
||||
import route55 from "./routes/setting/updeteModel";
|
||||
import route56 from "./routes/storyboard/batchSuperScoreImage";
|
||||
import route57 from "./routes/storyboard/chatStoryboard";
|
||||
import route58 from "./routes/storyboard/generateShotImage";
|
||||
import route59 from "./routes/storyboard/generateStoryboardApi";
|
||||
import route60 from "./routes/storyboard/generateVideoPrompt";
|
||||
import route61 from "./routes/storyboard/getStoryboard";
|
||||
import route62 from "./routes/storyboard/keepStoryboard";
|
||||
import route63 from "./routes/storyboard/saveStoryboard";
|
||||
import route64 from "./routes/storyboard/uploadImage";
|
||||
import route65 from "./routes/task/getTaskApi";
|
||||
import route66 from "./routes/task/taskDetails";
|
||||
import route67 from "./routes/user/getUser";
|
||||
import route68 from "./routes/video/addVideo";
|
||||
import route69 from "./routes/video/addVideoConfig";
|
||||
import route70 from "./routes/video/deleteVideoConfig";
|
||||
import route71 from "./routes/video/generatePrompt";
|
||||
import route72 from "./routes/video/generateVideo";
|
||||
import route73 from "./routes/video/getManufacturer";
|
||||
import route74 from "./routes/video/getVideo";
|
||||
import route75 from "./routes/video/getVideoConfigs";
|
||||
import route76 from "./routes/video/getVideoModel";
|
||||
import route77 from "./routes/video/getVideoStoryboards";
|
||||
import route78 from "./routes/video/reviseVideoStoryboards";
|
||||
import route79 from "./routes/video/saveVideo";
|
||||
import route80 from "./routes/video/upDateVideoConfig";
|
||||
|
||||
export default async (app: Express) => {
|
||||
app.use("/assets/addAssets", route1);
|
||||
@ -128,35 +130,37 @@ export default async (app: Express) => {
|
||||
app.use("/setting/addModel", route45);
|
||||
app.use("/setting/configurationModel", route46);
|
||||
app.use("/setting/delModel", route47);
|
||||
app.use("/setting/getAiModelMap", route48);
|
||||
app.use("/setting/getLog", route49);
|
||||
app.use("/setting/getSetting", route50);
|
||||
app.use("/setting/getVideoModelList", route51);
|
||||
app.use("/setting/updateModel", route52);
|
||||
app.use("/setting/updeteModel", route53);
|
||||
app.use("/storyboard/batchSuperScoreImage", route54);
|
||||
app.use("/storyboard/chatStoryboard", route55);
|
||||
app.use("/storyboard/generateShotImage", route56);
|
||||
app.use("/storyboard/generateStoryboardApi", route57);
|
||||
app.use("/storyboard/generateVideoPrompt", route58);
|
||||
app.use("/storyboard/getStoryboard", route59);
|
||||
app.use("/storyboard/keepStoryboard", route60);
|
||||
app.use("/storyboard/saveStoryboard", route61);
|
||||
app.use("/storyboard/uploadImage", route62);
|
||||
app.use("/task/getTaskApi", route63);
|
||||
app.use("/task/taskDetails", route64);
|
||||
app.use("/user/getUser", route65);
|
||||
app.use("/video/addVideo", route66);
|
||||
app.use("/video/addVideoConfig", route67);
|
||||
app.use("/video/deleteVideoConfig", route68);
|
||||
app.use("/video/generatePrompt", route69);
|
||||
app.use("/video/generateVideo", route70);
|
||||
app.use("/video/getManufacturer", route71);
|
||||
app.use("/video/getVideo", route72);
|
||||
app.use("/video/getVideoConfigs", route73);
|
||||
app.use("/video/getVideoModel", route74);
|
||||
app.use("/video/getVideoStoryboards", route75);
|
||||
app.use("/video/reviseVideoStoryboards", route76);
|
||||
app.use("/video/saveVideo", route77);
|
||||
app.use("/video/upDateVideoConfig", route78);
|
||||
app.use("/setting/getAiModelList", route48);
|
||||
app.use("/setting/getAiModelMap", route49);
|
||||
app.use("/setting/getLog", route50);
|
||||
app.use("/setting/getSetting", route51);
|
||||
app.use("/setting/getVideoModelDetail", route52);
|
||||
app.use("/setting/getVideoModelList", route53);
|
||||
app.use("/setting/updateModel", route54);
|
||||
app.use("/setting/updeteModel", route55);
|
||||
app.use("/storyboard/batchSuperScoreImage", route56);
|
||||
app.use("/storyboard/chatStoryboard", route57);
|
||||
app.use("/storyboard/generateShotImage", route58);
|
||||
app.use("/storyboard/generateStoryboardApi", route59);
|
||||
app.use("/storyboard/generateVideoPrompt", route60);
|
||||
app.use("/storyboard/getStoryboard", route61);
|
||||
app.use("/storyboard/keepStoryboard", route62);
|
||||
app.use("/storyboard/saveStoryboard", route63);
|
||||
app.use("/storyboard/uploadImage", route64);
|
||||
app.use("/task/getTaskApi", route65);
|
||||
app.use("/task/taskDetails", route66);
|
||||
app.use("/user/getUser", route67);
|
||||
app.use("/video/addVideo", route68);
|
||||
app.use("/video/addVideoConfig", route69);
|
||||
app.use("/video/deleteVideoConfig", route70);
|
||||
app.use("/video/generatePrompt", route71);
|
||||
app.use("/video/generateVideo", route72);
|
||||
app.use("/video/getManufacturer", route73);
|
||||
app.use("/video/getVideo", route74);
|
||||
app.use("/video/getVideoConfigs", route75);
|
||||
app.use("/video/getVideoModel", route76);
|
||||
app.use("/video/getVideoStoryboards", route77);
|
||||
app.use("/video/reviseVideoStoryboards", route78);
|
||||
app.use("/video/saveVideo", route79);
|
||||
app.use("/video/upDateVideoConfig", route80);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import express from "express";
|
||||
import u from "@/utils";
|
||||
import { z } from "zod";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { success } from "@/lib/responseFormat";
|
||||
import { error, success } from "@/lib/responseFormat";
|
||||
import { validateFields } from "@/middleware/middleware";
|
||||
import sharp from "sharp";
|
||||
const router = express.Router();
|
||||
@ -124,60 +124,67 @@ export default router.post(
|
||||
assetsId: id,
|
||||
});
|
||||
const apiConfig = await u.getPromptAi("assetsImage");
|
||||
try {
|
||||
const contentStr = await u.ai.image(
|
||||
{
|
||||
systemPrompt,
|
||||
prompt: userPrompt,
|
||||
imageBase64: base64 ? [base64] : [],
|
||||
size: "2K",
|
||||
aspectRatio: "16:9",
|
||||
},
|
||||
apiConfig,
|
||||
);
|
||||
|
||||
const contentStr = await u.ai.image(
|
||||
{
|
||||
systemPrompt,
|
||||
prompt: userPrompt,
|
||||
imageBase64: base64 ? [base64] : [],
|
||||
size: "2K",
|
||||
aspectRatio: "16:9",
|
||||
},
|
||||
apiConfig,
|
||||
);
|
||||
let insertType;
|
||||
const match = contentStr.match(/base64,([A-Za-z0-9+/=]+)/);
|
||||
let buffer = Buffer.from(match && match.length >= 2 ? match[1]! : contentStr!, "base64");
|
||||
|
||||
let insertType;
|
||||
const match = contentStr.match(/base64,([A-Za-z0-9+/=]+)/);
|
||||
let buffer = Buffer.from(match && match.length >= 2 ? match[1]! : contentStr!, "base64");
|
||||
if (type != "storyboard") {
|
||||
//添加文本
|
||||
// buffer = await imageAddText(name, buffer);
|
||||
}
|
||||
let imagePath;
|
||||
if (type == "role") {
|
||||
insertType = "角色";
|
||||
imagePath = `/${projectId}/role/${uuidv4()}.jpg`;
|
||||
}
|
||||
if (type == "scene") {
|
||||
insertType = "场景";
|
||||
imagePath = `/${projectId}/scene/${uuidv4()}.jpg`;
|
||||
}
|
||||
if (type == "props") {
|
||||
insertType = "道具";
|
||||
imagePath = `/${projectId}/props/${uuidv4()}.jpg`;
|
||||
}
|
||||
if (type == "storyboard") {
|
||||
insertType = "分镜";
|
||||
imagePath = `/${projectId}/storyboard/${uuidv4()}.jpg`;
|
||||
}
|
||||
|
||||
if (type != "storyboard") {
|
||||
//添加文本
|
||||
// buffer = await imageAddText(name, buffer);
|
||||
}
|
||||
let imagePath;
|
||||
if (type == "role") {
|
||||
insertType = "角色";
|
||||
imagePath = `/${projectId}/role/${uuidv4()}.jpg`;
|
||||
}
|
||||
if (type == "scene") {
|
||||
insertType = "场景";
|
||||
imagePath = `/${projectId}/scene/${uuidv4()}.jpg`;
|
||||
}
|
||||
if (type == "props") {
|
||||
insertType = "道具";
|
||||
imagePath = `/${projectId}/props/${uuidv4()}.jpg`;
|
||||
}
|
||||
if (type == "storyboard") {
|
||||
insertType = "分镜";
|
||||
imagePath = `/${projectId}/storyboard/${uuidv4()}.jpg`;
|
||||
}
|
||||
await u.oss.writeFile(imagePath!, buffer);
|
||||
const imageData = await u.db("t_image").where("id", imageId).select("*").first();
|
||||
if (imageData) {
|
||||
await u.db("t_image").where("id", imageId).update({
|
||||
state: "生成成功",
|
||||
filePath: imagePath,
|
||||
type: insertType,
|
||||
});
|
||||
|
||||
await u.oss.writeFile(imagePath!, buffer);
|
||||
const imageData = await u.db("t_image").where("id", imageId).select("*").first();
|
||||
if (imageData) {
|
||||
const path = await u.oss.getFileUrl(imagePath!);
|
||||
|
||||
// const state = await u.db("t_assets").where("id", id).select("state").first();
|
||||
|
||||
return res.status(200).send(success({ path, assetsId: id }));
|
||||
} else {
|
||||
return res.status(500).send("资产已被删除");
|
||||
}
|
||||
} catch (e) {
|
||||
await u.db("t_image").where("id", imageId).update({
|
||||
state: "生成成功",
|
||||
filePath: imagePath,
|
||||
type: insertType,
|
||||
state: "生成失败",
|
||||
});
|
||||
|
||||
const path = await u.oss.getFileUrl(imagePath!);
|
||||
|
||||
// const state = await u.db("t_assets").where("id", id).select("state").first();
|
||||
|
||||
return res.status(200).send(success({ path, assetsId: id }));
|
||||
} else {
|
||||
return res.status(500).send("资产已被删除");
|
||||
const msg = u.error(e).message || "图片生成失败";
|
||||
return res.status(400).send(error(msg));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -27,6 +27,7 @@ export default router.post(
|
||||
resolution: "720p",
|
||||
aspectRatio: "16:9",
|
||||
audio: false,
|
||||
mode: "single",
|
||||
},
|
||||
{
|
||||
model: modelName,
|
||||
|
||||
33
src/routes/setting/getAiModelList.ts
Normal file
33
src/routes/setting/getAiModelList.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import express from "express";
|
||||
import u from "@/utils";
|
||||
import { z } from "zod";
|
||||
import { success } from "@/lib/responseFormat";
|
||||
import { validateFields } from "@/middleware/middleware";
|
||||
const router = express.Router();
|
||||
|
||||
export default router.post(
|
||||
"/",
|
||||
validateFields({
|
||||
type: z.enum(["text", "image", "video"]),
|
||||
}),
|
||||
async (req, res) => {
|
||||
const { type } = req.body;
|
||||
const sqlTableMap = {
|
||||
text: "t_textModel",
|
||||
image: "t_imageModel",
|
||||
video: "t_videoModel",
|
||||
};
|
||||
const modelLists = await u
|
||||
.db(sqlTableMap[type as "image" | "text" | "video"])
|
||||
.whereNot("manufacturer", "other")
|
||||
.select("id", "manufacturer", "model");
|
||||
const result: Record<string, any[]> = {};
|
||||
for (const row of modelLists) {
|
||||
if (!result[row.manufacturer]) {
|
||||
result[row.manufacturer] = [];
|
||||
}
|
||||
result[row.manufacturer].push({ label: row.model, value: row.model });
|
||||
}
|
||||
res.status(200).send(success(result));
|
||||
},
|
||||
);
|
||||
31
src/routes/setting/getVideoModelDetail.ts
Normal file
31
src/routes/setting/getVideoModelDetail.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import express from "express";
|
||||
import u from "@/utils";
|
||||
import { success } from "@/lib/responseFormat";
|
||||
const router = express.Router();
|
||||
|
||||
export default router.post("/", async (req, res) => {
|
||||
const videoData = await u.db("t_videoModel").select("*");
|
||||
const allData = videoData.map((i) => {
|
||||
const durationResolutionMap = JSON.parse(i.durationResolutionMap ?? "[]");
|
||||
const aspectRatio = JSON.parse(i.aspectRatio ?? "[]");
|
||||
const type = JSON.parse(i.type ?? "[]");
|
||||
return {
|
||||
...i,
|
||||
durationResolutionMap,
|
||||
aspectRatio,
|
||||
type,
|
||||
audio: i.audio === 1,
|
||||
};
|
||||
});
|
||||
|
||||
const otherConfig = {
|
||||
manufacturer: "other",
|
||||
model: "",
|
||||
durationResolutionMap: [{ duration: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], resolution: ["480p", "720p", "1080p"] }],
|
||||
aspectRatio: ["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"],
|
||||
type: ["text", "endFrameOptional", "singleImage", "multiImage"],
|
||||
audio: true,
|
||||
};
|
||||
const returnData = [otherConfig, ...allData];
|
||||
res.status(200).send(success(returnData));
|
||||
});
|
||||
@ -119,7 +119,7 @@ export default router.post(
|
||||
res.status(200).send(success({ id: videoId, configId: configId || null }));
|
||||
|
||||
// 异步生成视频
|
||||
generateVideoAsync(videoId, projectId, fileUrl, savePath, prompt, duration, resolution, audioEnabled, aiConfigData);
|
||||
generateVideoAsync(videoId, projectId, fileUrl, savePath, prompt, duration, resolution, audioEnabled, aiConfigData, mode);
|
||||
},
|
||||
);
|
||||
|
||||
@ -134,6 +134,7 @@ async function generateVideoAsync(
|
||||
resolution: string,
|
||||
audioEnabled: boolean,
|
||||
aiConfigData: t_config,
|
||||
mode: string,
|
||||
) {
|
||||
try {
|
||||
const projectData = await u.db("t_project").where("id", projectId).select("artStyle", "videoRatio").first();
|
||||
@ -175,6 +176,7 @@ ${prompt}
|
||||
aspectRatio: projectData?.videoRatio as any,
|
||||
resolution: resolution as any,
|
||||
audio: audioEnabled,
|
||||
mode: mode as any,
|
||||
},
|
||||
{
|
||||
baseURL: aiConfigData?.baseUrl!,
|
||||
|
||||
65
src/types/database.d.ts
vendored
65
src/types/database.d.ts
vendored
@ -1,6 +1,20 @@
|
||||
// @db-hash 8ef9e37c14c453b2d95832b971baca8a
|
||||
// @db-hash ab4e3e93bfba304164daa7d28c804eaf
|
||||
//该文件由脚本自动生成,请勿手动修改
|
||||
|
||||
export interface _t_video_old_20260210 {
|
||||
'aiConfigId'?: number | null;
|
||||
'configId'?: number | null;
|
||||
'filePath'?: string | null;
|
||||
'firstFrame'?: string | null;
|
||||
'id'?: number;
|
||||
'model'?: string | null;
|
||||
'prompt'?: string | null;
|
||||
'resolution'?: string | null;
|
||||
'scriptId'?: number | null;
|
||||
'state'?: number | null;
|
||||
'storyboardImgs'?: string | null;
|
||||
'time'?: number | null;
|
||||
}
|
||||
export interface t_aiModelMap {
|
||||
'configId'?: number | null;
|
||||
'id'?: number;
|
||||
@ -39,7 +53,6 @@ export interface t_config {
|
||||
'manufacturer'?: string | null;
|
||||
'model'?: string | null;
|
||||
'modelType'?: string | null;
|
||||
'name'?: string | null;
|
||||
'type'?: string | null;
|
||||
'userId'?: number | null;
|
||||
}
|
||||
@ -53,6 +66,20 @@ export interface t_image {
|
||||
'type'?: string | null;
|
||||
'videoId'?: number | null;
|
||||
}
|
||||
export interface t_imageConfig {
|
||||
'grid'?: number | null;
|
||||
'id'?: number;
|
||||
'manufacturer'?: string | null;
|
||||
'model'?: string | null;
|
||||
'type'?: string | null;
|
||||
}
|
||||
export interface t_imageModel {
|
||||
'grid'?: number | null;
|
||||
'id'?: number;
|
||||
'manufacturer'?: string | null;
|
||||
'model'?: string | null;
|
||||
'type'?: string | null;
|
||||
}
|
||||
export interface t_novel {
|
||||
'chapter'?: string | null;
|
||||
'chapterData'?: string | null;
|
||||
@ -118,6 +145,24 @@ export interface t_taskList {
|
||||
'startTime'?: string | null;
|
||||
'state'?: string | null;
|
||||
}
|
||||
export interface t_textConfig {
|
||||
'id'?: number;
|
||||
'image'?: number | null;
|
||||
'manufacturer'?: string | null;
|
||||
'model'?: string | null;
|
||||
'responseFormat'?: string | null;
|
||||
'think'?: number | null;
|
||||
'tool'?: number | null;
|
||||
}
|
||||
export interface t_textModel {
|
||||
'id'?: number;
|
||||
'image'?: number | null;
|
||||
'manufacturer'?: string | null;
|
||||
'model'?: string | null;
|
||||
'responseFormat'?: string | null;
|
||||
'think'?: number | null;
|
||||
'tool'?: number | null;
|
||||
}
|
||||
export interface t_user {
|
||||
'id'?: number;
|
||||
'name'?: string | null;
|
||||
@ -139,6 +184,7 @@ export interface t_video {
|
||||
'time'?: number | null;
|
||||
}
|
||||
export interface t_videoConfig {
|
||||
'aiConfigId'?: number | null;
|
||||
'audioEnabled'?: number | null;
|
||||
'createTime'?: number | null;
|
||||
'duration'?: number | null;
|
||||
@ -155,13 +201,25 @@ export interface t_videoConfig {
|
||||
'startFrame'?: string | null;
|
||||
'updateTime'?: number | null;
|
||||
}
|
||||
export interface t_videoModel {
|
||||
'aspectRatio'?: string | null;
|
||||
'audio'?: number | null;
|
||||
'durationResolutionMap'?: string | null;
|
||||
'id'?: number;
|
||||
'manufacturer'?: string | null;
|
||||
'model'?: string | null;
|
||||
'type'?: string | null;
|
||||
}
|
||||
|
||||
export interface DB {
|
||||
"_t_video_old_20260210": _t_video_old_20260210;
|
||||
"t_aiModelMap": t_aiModelMap;
|
||||
"t_assets": t_assets;
|
||||
"t_chatHistory": t_chatHistory;
|
||||
"t_config": t_config;
|
||||
"t_image": t_image;
|
||||
"t_imageConfig": t_imageConfig;
|
||||
"t_imageModel": t_imageModel;
|
||||
"t_novel": t_novel;
|
||||
"t_outline": t_outline;
|
||||
"t_project": t_project;
|
||||
@ -170,7 +228,10 @@ export interface DB {
|
||||
"t_setting": t_setting;
|
||||
"t_storyline": t_storyline;
|
||||
"t_taskList": t_taskList;
|
||||
"t_textConfig": t_textConfig;
|
||||
"t_textModel": t_textModel;
|
||||
"t_user": t_user;
|
||||
"t_video": t_video;
|
||||
"t_videoConfig": t_videoConfig;
|
||||
"t_videoModel": t_videoModel;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import runninghub from "./owned/runninghub";
|
||||
import apimart from "./owned/apimart";
|
||||
import other from "./owned/other";
|
||||
import gemini from "./owned/gemini";
|
||||
import modelScope from "./owned/modelScope";
|
||||
|
||||
const urlToBase64 = async (url: string): Promise<string> => {
|
||||
const res = await axios.get(url, { responseType: "arraybuffer" });
|
||||
@ -25,20 +26,20 @@ const modelInstance = {
|
||||
vidu: vidu,
|
||||
runninghub: runninghub,
|
||||
// apimart: apimart,
|
||||
modelScope,
|
||||
other,
|
||||
} as const;
|
||||
|
||||
export default async (input: ImageConfig, config: AIConfig) => {
|
||||
console.log("%c Line:32 🥪 config", "background:#33a5ff", config);
|
||||
const { model, apiKey, baseURL, manufacturer } = { ...config };
|
||||
if (!config || !config?.model || !config?.apiKey || !config?.manufacturer) throw new Error("请检查模型配置是否正确");
|
||||
|
||||
const manufacturerFn = modelInstance[manufacturer as keyof typeof modelInstance];
|
||||
if (!manufacturerFn) if (!manufacturerFn) throw new Error("不支持的图片厂商");
|
||||
if (manufacturer !== "other") {
|
||||
const owned = modelList.find((m) => m.model === model);
|
||||
if (!owned) throw new Error("不支持的模型");
|
||||
}
|
||||
// if (manufacturer !== "other") {
|
||||
// const owned = modelList.find((m) => m.model === model);
|
||||
// if (!owned) throw new Error("不支持的模型");
|
||||
// }
|
||||
|
||||
// 补充图片的 base64 内容类型字符串
|
||||
if (input.imageBase64 && input.imageBase64.length > 0) {
|
||||
@ -65,7 +66,6 @@ export default async (input: ImageConfig, config: AIConfig) => {
|
||||
}
|
||||
|
||||
let imageUrl = await manufacturerFn(input, { model, apiKey, baseURL });
|
||||
console.log("%c Line:68 🍷 imageUrl", "background:#4fff4B", imageUrl);
|
||||
if (!input.resType) input.resType = "b64";
|
||||
if (input.resType === "b64" && imageUrl.startsWith("http")) imageUrl = await urlToBase64(imageUrl);
|
||||
return imageUrl;
|
||||
|
||||
@ -59,13 +59,6 @@ const modelList: Owned[] = [
|
||||
grid: true,
|
||||
type: "ti2i",
|
||||
},
|
||||
//ApiMart
|
||||
{
|
||||
manufacturer: "apimart",
|
||||
model: "nanobanana",
|
||||
grid: true,
|
||||
type: "ti2i",
|
||||
},
|
||||
];
|
||||
|
||||
export default modelList;
|
||||
|
||||
133
src/utils/ai/image/owned/modelScope.ts
Normal file
133
src/utils/ai/image/owned/modelScope.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import "../type";
|
||||
import { generateImage, generateText, ModelMessage } from "ai";
|
||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||
import { pollTask } from "@/utils/ai/utils";
|
||||
import u from "@/utils";
|
||||
import axios from "axios";
|
||||
function getApiUrl(apiUrl: string) {
|
||||
if (apiUrl.includes("|")) {
|
||||
const parts = apiUrl.split("|");
|
||||
if (parts.length !== 2 || !parts[0].trim() || !parts[1].trim()) {
|
||||
throw new Error("url 格式错误,请使用 url1|url2 格式");
|
||||
}
|
||||
return { requestUrl: parts[0].trim(), queryUrl: parts[1].trim() };
|
||||
}
|
||||
throw new Error("请填写正确的url");
|
||||
}
|
||||
function template(replaceObj: Record<string, any>, url: string) {
|
||||
return url.replace(/\{(\w+)\}/g, (match, varName) => {
|
||||
return replaceObj.hasOwnProperty(varName) ? replaceObj[varName] : match;
|
||||
});
|
||||
}
|
||||
async function compressionPrompt(prompt: string) {
|
||||
const apiConfigData = await u.getPromptAi("assetsPrompt");
|
||||
|
||||
const result = await u.ai.text.invoke(
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: `
|
||||
你是一名资深Prompt工程师和文本摘要专家。你的任务是将用户输入的提示词文本内容压缩至2000字以内。请按照如下要求操作:
|
||||
1. 准确梳理并提炼输入文本的主要内容、核心要点和关键信息。
|
||||
2. 剔除冗余、重复、无关或细枝末节的描述,压缩内容至2000字以内。
|
||||
3. 在压缩过程中,严格保持中立,避免被用户输入的风格、情绪或暗示性表述影响,始终按照“精准摘要到2000字”的目标执行,不被内容带偏。
|
||||
4. 输出为浓缩摘要文本,语言精炼、结构清晰。
|
||||
5. 请适配各类文本场景,无论是叙述性、说明性、议论性还是其他类型的文本,都要确保压缩后的内容完整传达原文的核心信息和主要观点。
|
||||
|
||||
直接输出压缩后的文本,不要任何额外的说明或引导语。请立即开始压缩,并确保输出内容不超过2000字。
|
||||
`,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
},
|
||||
apiConfigData,
|
||||
);
|
||||
return result.text;
|
||||
}
|
||||
export default async (input: ImageConfig, config: AIConfig): Promise<string> => {
|
||||
if (!config.model) throw new Error("缺少Model名称");
|
||||
if (!config.apiKey) throw new Error("缺少API Key");
|
||||
|
||||
const defaultBaseURL = "https://api-inference.modelscope.cn/v1/images/generations|https://api-inference.modelscope.cn/v1/tasks/{id}";
|
||||
const { requestUrl, queryUrl } = getApiUrl(config.baseURL! ?? defaultBaseURL);
|
||||
// 根据 size 配置映射到具体尺寸
|
||||
const sizeMap: Record<string, Record<string, string>> = {
|
||||
"1K": {
|
||||
"16:9": "1664x928",
|
||||
"9:16": "928x1664",
|
||||
},
|
||||
"2K": {
|
||||
"16:9": "2048x1152",
|
||||
"9:16": "1152x2048",
|
||||
},
|
||||
"4K": {
|
||||
"16:9": "2048x1152",
|
||||
"9:16": "1152x2048",
|
||||
},
|
||||
};
|
||||
// 构建完整的提示词
|
||||
const fullPrompt = input.systemPrompt ? `${input.systemPrompt}\n\n${input.prompt}` : input.prompt;
|
||||
|
||||
let newPrompt = fullPrompt;
|
||||
if (fullPrompt.length > 2000) {
|
||||
let compressed = await compressionPrompt(fullPrompt);
|
||||
|
||||
newPrompt = compressed;
|
||||
}
|
||||
let mergedImage = input.imageBase64;
|
||||
if (mergedImage && mergedImage.length) {
|
||||
const smallImage = await u.imageTools.mergeImages(mergedImage, "5mb");
|
||||
mergedImage = [smallImage];
|
||||
}
|
||||
|
||||
const size = sizeMap[input.size]?.[input.aspectRatio] ?? "1024x1024";
|
||||
|
||||
const taskBody: Record<string, any> = {
|
||||
model: config.model,
|
||||
prompt: newPrompt,
|
||||
negative_prompt: "",
|
||||
size,
|
||||
...(mergedImage && mergedImage.length ? { image_url: mergedImage } : {}),
|
||||
};
|
||||
|
||||
const apiKey = config.apiKey.replace("Bearer ", "");
|
||||
try {
|
||||
const { data } = await axios.post(requestUrl, taskBody, { headers: { Authorization: `Bearer ${apiKey}`, "X-ModelScope-Async-Mode": "true" } });
|
||||
|
||||
if (data.task_status != "SUCCEED") throw new Error(`任务提交失败: ${data || "未知错误"}`);
|
||||
const taskId = data.task_id;
|
||||
|
||||
return await pollTask(async () => {
|
||||
const { data: queryData } = await axios.get(template({ id: taskId }, queryUrl), {
|
||||
headers: { Authorization: `Bearer ${apiKey}`, "X-ModelScope-Task-Type": "image_generation" },
|
||||
});
|
||||
|
||||
const { task_status, output_images } = queryData || {};
|
||||
|
||||
if (task_status === "FAILED") {
|
||||
return { completed: false, error: "图片生成失败" };
|
||||
}
|
||||
|
||||
if (task_status === "SUCCEED") {
|
||||
return { completed: true, url: output_images?.[0] };
|
||||
}
|
||||
|
||||
return { completed: false };
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("%c Line:90 🥪 error", "background:#93c0a4", error.response?.data?.errors?.message);
|
||||
const msg = u.error(error).message || "图片生成失败";
|
||||
throw new Error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
async function urlToBase64(url: string): Promise<string> {
|
||||
const res = await axios.get(url, { responseType: "arraybuffer" });
|
||||
const base64 = Buffer.from(res.data).toString("base64");
|
||||
const mimeType = res.headers["content-type"] || "image/png";
|
||||
return `data:${mimeType};base64,${base64}`;
|
||||
}
|
||||
@ -3,7 +3,7 @@ import { generateText, streamText, Output, stepCountIs, ModelMessage, LanguageMo
|
||||
import { wrapLanguageModel } from "ai";
|
||||
import { devToolsMiddleware } from "@ai-sdk/devtools";
|
||||
import { parse } from "best-effort-json-parser";
|
||||
import modelList from "./modelList";
|
||||
import { getModelList } from "./modelList";
|
||||
import { z } from "zod";
|
||||
import { OpenAIProvider } from "@ai-sdk/openai";
|
||||
interface AIInput<T extends Record<string, z.ZodTypeAny> | undefined = undefined> {
|
||||
@ -26,12 +26,15 @@ const buildOptions = async (input: AIInput<any>, config: AIConfig = {}) => {
|
||||
if (!config || !config?.model || !config?.apiKey || !config?.manufacturer) throw new Error("请检查模型配置是否正确");
|
||||
const { model, apiKey, baseURL, manufacturer } = { ...config };
|
||||
let owned;
|
||||
const modelList = await getModelList();
|
||||
if (manufacturer == "other") {
|
||||
owned = modelList.find((m) => m.manufacturer === manufacturer);
|
||||
} else {
|
||||
owned = modelList.find((m) => m.model === model);
|
||||
if (!owned) owned = modelList.find((m) => m.manufacturer === manufacturer);
|
||||
}
|
||||
if (!owned) throw new Error("不支持的模型或厂商");
|
||||
if (!owned) throw new Error("不支持的厂商");
|
||||
console.log("%c Line:36 🥛 owned", "background:#6ec1c2", owned);
|
||||
|
||||
const modelInstance = owned.instance({ apiKey, baseURL: baseURL!, name: "xixixi" });
|
||||
|
||||
@ -52,7 +55,7 @@ const buildOptions = async (input: AIInput<any>, config: AIConfig = {}) => {
|
||||
};
|
||||
|
||||
const output = input.output ? (outputBuilders[owned.responseFormat]?.(input.output) ?? null) : null;
|
||||
const chatModelManufacturer = ["doubao", "other", "openai"];
|
||||
const chatModelManufacturer = ["volcengine", "other", "openai"];
|
||||
const modelFn = chatModelManufacturer.includes(owned.manufacturer) ? (modelInstance as OpenAIProvider).chat(model!) : modelInstance(model!);
|
||||
return {
|
||||
config: {
|
||||
@ -76,6 +79,7 @@ const ai = Object.create({}) as {
|
||||
|
||||
ai.invoke = async (input: AIInput<any>, config: AIConfig) => {
|
||||
const options = await buildOptions(input, config);
|
||||
console.log("%c Line:81 🍧 options", "background:#93c0a4", options);
|
||||
const result = await generateText(options.config);
|
||||
if (options.responseFormat === "object" && input.output) {
|
||||
const pattern = /{[^{}]*}|{(?:[^{}]*|{[^{}]*})*}/g;
|
||||
@ -92,6 +96,7 @@ ai.invoke = async (input: AIInput<any>, config: AIConfig) => {
|
||||
|
||||
ai.stream = async (input: AIInput, config: AIConfig) => {
|
||||
const options = await buildOptions(input, config);
|
||||
console.log("%c Line:98 🍬 options", "background:#fca650", options);
|
||||
return streamText(options.config);
|
||||
};
|
||||
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { createOpenAI } from "@ai-sdk/openai";
|
||||
import { createOpenAI, OpenAIProviderSettings } from "@ai-sdk/openai";
|
||||
import { createDeepSeek } from "@ai-sdk/deepseek";
|
||||
import { createZhipu } from "zhipu-ai-provider";
|
||||
import { createQwen } from "qwen-ai-provider";
|
||||
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
||||
import { createAnthropic } from "@ai-sdk/anthropic";
|
||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||
import { createXai } from '@ai-sdk/xai';
|
||||
import { createXai } from "@ai-sdk/xai";
|
||||
import db from "@/utils/db";
|
||||
|
||||
interface Owned {
|
||||
manufacturer: string;
|
||||
@ -23,11 +24,24 @@ interface Owned {
|
||||
| typeof createAnthropic
|
||||
| typeof createOpenAICompatible;
|
||||
}
|
||||
const instanceMap = {
|
||||
deepSeek: createDeepSeek,
|
||||
volcengine: createOpenAI,
|
||||
openai: createOpenAI,
|
||||
zhipu: createZhipu,
|
||||
qwen: createQwen,
|
||||
|
||||
gemini: createGoogleGenerativeAI,
|
||||
|
||||
anthropic: createAnthropic,
|
||||
modelScope: (options: OpenAIProviderSettings) => createOpenAI({ ...options, headers: { ...options?.headers, "X-ModelScope-Async-Mode": "true" } }),
|
||||
xai: createXai,
|
||||
other: createOpenAI,
|
||||
};
|
||||
const modelList: Owned[] = [
|
||||
// DeepSeek
|
||||
{
|
||||
manufacturer: "deepseek",
|
||||
manufacturer: "deepSeek",
|
||||
model: "deepseek-chat",
|
||||
responseFormat: "schema",
|
||||
image: false,
|
||||
@ -36,7 +50,7 @@ const modelList: Owned[] = [
|
||||
tool: true,
|
||||
},
|
||||
{
|
||||
manufacturer: "deepseek",
|
||||
manufacturer: "deepSeek",
|
||||
model: "deepseek-reasoner",
|
||||
responseFormat: "schema",
|
||||
image: false,
|
||||
@ -47,7 +61,16 @@ const modelList: Owned[] = [
|
||||
|
||||
// 豆包
|
||||
{
|
||||
manufacturer: "doubao",
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seed-2-0-mini-260215",
|
||||
responseFormat: "object",
|
||||
image: true,
|
||||
think: false,
|
||||
instance: createOpenAI,
|
||||
tool: true,
|
||||
},
|
||||
{
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seed-1-8-251228",
|
||||
responseFormat: "schema",
|
||||
image: true,
|
||||
@ -56,7 +79,7 @@ const modelList: Owned[] = [
|
||||
tool: true,
|
||||
},
|
||||
{
|
||||
manufacturer: "doubao",
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seed-1-6-251015",
|
||||
responseFormat: "schema",
|
||||
image: true,
|
||||
@ -65,7 +88,7 @@ const modelList: Owned[] = [
|
||||
tool: true,
|
||||
},
|
||||
{
|
||||
manufacturer: "doubao",
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seed-1-6-lite-251015",
|
||||
responseFormat: "schema",
|
||||
image: true,
|
||||
@ -74,7 +97,7 @@ const modelList: Owned[] = [
|
||||
tool: true,
|
||||
},
|
||||
{
|
||||
manufacturer: "doubao",
|
||||
manufacturer: "volcengine",
|
||||
model: "doubao-seed-1-6-flash-250828",
|
||||
responseFormat: "schema",
|
||||
image: true,
|
||||
@ -413,7 +436,7 @@ const modelList: Owned[] = [
|
||||
tool: true,
|
||||
},
|
||||
//xai
|
||||
{
|
||||
{
|
||||
manufacturer: "xai",
|
||||
model: "grok-3",
|
||||
responseFormat: "schema",
|
||||
@ -422,7 +445,7 @@ const modelList: Owned[] = [
|
||||
instance: createXai,
|
||||
tool: true,
|
||||
},
|
||||
{
|
||||
{
|
||||
manufacturer: "xai",
|
||||
model: "grok-4",
|
||||
responseFormat: "schema",
|
||||
@ -431,7 +454,7 @@ const modelList: Owned[] = [
|
||||
instance: createXai,
|
||||
tool: true,
|
||||
},
|
||||
{
|
||||
{
|
||||
manufacturer: "xai",
|
||||
model: "grok-4.1",
|
||||
responseFormat: "schema",
|
||||
@ -444,12 +467,24 @@ const modelList: Owned[] = [
|
||||
{
|
||||
manufacturer: "other",
|
||||
model: "gpt-4.1",
|
||||
responseFormat: "schema",
|
||||
responseFormat: "object",
|
||||
image: true,
|
||||
think: false,
|
||||
instance: createOpenAI,
|
||||
tool: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const getModelList = async () => {
|
||||
const modelLists = await db("t_textModel").select("*");
|
||||
const resultInstaceList = modelLists.map((model) => {
|
||||
return {
|
||||
...model,
|
||||
tool: model.tool == 1 ? true : false,
|
||||
think: model.think == 1 ? true : false,
|
||||
image: model.image == 1 ? true : false,
|
||||
instance: instanceMap[model.manufacturer as keyof typeof instanceMap],
|
||||
};
|
||||
});
|
||||
return resultInstaceList as Owned[];
|
||||
};
|
||||
export default modelList;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import modelList from "./video/modelList";
|
||||
|
||||
import { db } from "../db";
|
||||
interface ValidateResult {
|
||||
owned: (typeof modelList)[number];
|
||||
images: string[];
|
||||
@ -80,3 +80,5 @@ export const pollTask = async (
|
||||
}
|
||||
throw new Error(`任务轮询超时,已尝试 ${maxAttempts} 次`);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -29,8 +29,8 @@ export default async (input: VideoConfig, config?: AIConfig) => {
|
||||
|
||||
const manufacturerFn = modelInstance[manufacturer as keyof typeof modelInstance];
|
||||
if (!manufacturerFn) if (!manufacturerFn) throw new Error("不支持的视频厂商");
|
||||
const owned = modelList.find((m) => m.model === model);
|
||||
if (!owned) throw new Error("不支持的模型");
|
||||
// const owned = modelList.find((m) => m.model === model);
|
||||
// if (!owned) throw new Error("不支持的模型");
|
||||
|
||||
// 补充图片的 base64 内容类型字符串
|
||||
if (input.imageBase64 && input.imageBase64.length > 0) {
|
||||
|
||||
@ -10,7 +10,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
if (!config.model) throw new Error("缺少Model名称");
|
||||
if (!config.apiKey) throw new Error("缺少API Key");
|
||||
|
||||
const { owned, images, hasStartEndType } = validateVideoConfig(input, config);
|
||||
// const { owned, images, hasStartEndType } = validateVideoConfig(input, config);
|
||||
|
||||
const defaultBaseUrl = [
|
||||
"https://generativelanguage.googleapis.com/v1beta/models/{model}:predictLongRunning",
|
||||
@ -18,8 +18,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
].join("|");
|
||||
|
||||
const [submitUrl, queryUrl] = (config.baseURL || defaultBaseUrl).split("|");
|
||||
|
||||
|
||||
|
||||
const headers = { "x-goog-api-key": config.apiKey };
|
||||
|
||||
const instance: Record<string, any> = { prompt: input.prompt };
|
||||
@ -30,17 +29,17 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
};
|
||||
|
||||
// 根据图片数量和模型能力决定图片用法
|
||||
const len = images.length;
|
||||
const hasRef = owned.type.includes("reference");
|
||||
const hasSingle = owned.type.includes("singleImage");
|
||||
|
||||
const len = input.imageBase64 ? input.imageBase64.length : 0;
|
||||
const hasRef = input.mode == "multi";
|
||||
const hasSingle = input.mode == "single";
|
||||
const hasStartEndType = input.mode === "startEnd";
|
||||
if (len === 2 && hasStartEndType) {
|
||||
instance.image = buildInlineImage(images[0]);
|
||||
parameters.lastFrame = buildInlineImage(images[1]);
|
||||
instance.image = buildInlineImage(input.imageBase64![0]);
|
||||
parameters.lastFrame = buildInlineImage(input.imageBase64![1]);
|
||||
} else if (len === 1 && (hasSingle || hasStartEndType)) {
|
||||
instance.image = buildInlineImage(images[0]);
|
||||
instance.image = buildInlineImage(input.imageBase64![0]);
|
||||
} else if (len >= 1 && len <= 3 && hasRef) {
|
||||
parameters.referenceImages = images.map((img) => ({ image: buildInlineImage(img), referenceType: "asset" }));
|
||||
parameters.referenceImages = input.imageBase64!.map((img) => ({ image: buildInlineImage(img), referenceType: "asset" }));
|
||||
}
|
||||
|
||||
const { data } = await axios.post(
|
||||
@ -53,15 +52,14 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
|
||||
return pollTask(async () => {
|
||||
const { data: status } = await axios.get(queryUrl.replace("{name}", data.name), { headers });
|
||||
|
||||
|
||||
const { done, response, error } = status;
|
||||
|
||||
|
||||
if (!done) return { completed: false };
|
||||
if (error) return { completed: false, error: `任务失败: ${error.message || JSON.stringify(error)}` };
|
||||
|
||||
const videoUri = response?.generateVideoResponse?.generatedSamples?.[0]?.video?.uri;
|
||||
|
||||
|
||||
if (!videoUri) return { completed: false, error: "未获取到视频下载地址" };
|
||||
|
||||
const videoRes = await axios.get(videoUri, { headers, responseType: "arraybuffer", maxRedirects: 5 });
|
||||
|
||||
@ -6,7 +6,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
if (!config.apiKey) throw new Error("缺少API Key");
|
||||
if (!config.baseURL) throw new Error("缺少baseURL配置");
|
||||
|
||||
const { images } = validateVideoConfig(input, config);
|
||||
// const { images } = validateVideoConfig(input, config);
|
||||
|
||||
// 解析URL配置:图生视频|文生视频|查询地址
|
||||
const defaultBaseUrl =
|
||||
@ -24,7 +24,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
const mode = modelMatch ? (modelMatch[2].toLowerCase() as "std" | "pro") : "std";
|
||||
|
||||
// 判断是图生视频还是文生视频
|
||||
const hasImage = images.length > 0;
|
||||
const hasImage = input.imageBase64 ? input.imageBase64.length > 0 : false;
|
||||
const createUrl = hasImage ? image2videoUrl : text2videoUrl;
|
||||
|
||||
// 去除图片的内容类型前缀(kling要求纯base64)
|
||||
@ -41,9 +41,9 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
|
||||
if (hasImage) {
|
||||
// 图生视频:首帧和尾帧
|
||||
body.image = stripDataUrl(images[0]);
|
||||
if (images.length > 1) {
|
||||
body.image_tail = stripDataUrl(images[1]);
|
||||
body.image = stripDataUrl(input.imageBase64![0]);
|
||||
if (input.imageBase64!.length > 1) {
|
||||
body.image_tail = stripDataUrl(input.imageBase64![1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import { pollTask, validateVideoConfig } from "@/utils/ai/utils";
|
||||
export default async (input: VideoConfig, config: AIConfig) => {
|
||||
if (!config.apiKey) throw new Error("缺少API Key");
|
||||
|
||||
const { owned, images, hasTextType } = validateVideoConfig(input, config);
|
||||
// const { owned, images, hasTextType } = validateVideoConfig(input, config);
|
||||
|
||||
const defaultBaseUrl = [
|
||||
"https://www.runninghub.cn/openapi/v2/rhart-video-s/image-to-video",
|
||||
@ -19,8 +19,8 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
].join("|");
|
||||
|
||||
const [image2videoUrl, image2videoProUrl, text2videoUrl, text2videoProUrl, queryUrl, uploadUrl] = (config.baseURL || defaultBaseUrl).split("|");
|
||||
|
||||
const isPro = owned.model === "sora-2-pro";
|
||||
const hasTextType = input.mode == "text";
|
||||
const isPro = config.model === "sora-2-pro";
|
||||
const authorization = `Bearer ${config.apiKey}`;
|
||||
|
||||
// 上传 base64 图片
|
||||
@ -65,20 +65,19 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
return { taskId: data.taskId, status: data.status, url: data.results?.[0]?.url };
|
||||
};
|
||||
|
||||
const isTextToVideo = images.length === 0 && hasTextType;
|
||||
const isTextToVideo = (!input.imageBase64 || input.imageBase64.length === 0) && hasTextType;
|
||||
const submitUrl = isTextToVideo ? (isPro ? text2videoProUrl : text2videoUrl) : isPro ? image2videoProUrl : image2videoUrl;
|
||||
|
||||
const requestBody: Record<string, unknown> = {
|
||||
prompt: input.prompt,
|
||||
duration: String(input.duration),
|
||||
aspectRatio: input.aspectRatio,
|
||||
...(isTextToVideo ? {} : { imageUrl: await uploadImage(images[0]) }),
|
||||
...(isTextToVideo ? {} : { imageUrl: await uploadImage(input.imageBase64![0]) }),
|
||||
};
|
||||
|
||||
const { taskId } = await submitTask(submitUrl, requestBody);
|
||||
|
||||
return await pollTask(async () => {
|
||||
|
||||
const { data } = await axios.post(
|
||||
queryUrl,
|
||||
{
|
||||
|
||||
@ -20,30 +20,30 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
const hasImages = input.imageBase64 && input.imageBase64.length > 0;
|
||||
|
||||
// 根据是否有图片,查找匹配的模型配置
|
||||
const customOwned = modelList.find((m) => {
|
||||
if (m.manufacturer !== "vidu") return false;
|
||||
if (m.model !== config.model) return false;
|
||||
if (hasImages) {
|
||||
return m.type.some((t) => t !== "text");
|
||||
} else {
|
||||
return m.type.includes("text");
|
||||
}
|
||||
});
|
||||
// const customOwned = modelList.find((m) => {
|
||||
// if (m.manufacturer !== "vidu") return false;
|
||||
// if (m.model !== config.model) return false;
|
||||
// if (hasImages) {
|
||||
// return m.type.some((t) => t !== "text");
|
||||
// } else {
|
||||
// return m.type.includes("text");
|
||||
// }
|
||||
// });
|
||||
|
||||
if (!customOwned) {
|
||||
throw new Error(`未找到匹配的模型配置: ${config.model}`);
|
||||
}
|
||||
// if (!customOwned) {
|
||||
// throw new Error(`未找到匹配的模型配置: ${config.model}`);
|
||||
// }
|
||||
|
||||
// 使用统一校验函数
|
||||
const { owned, images } = validateVideoConfig(input, config, customOwned);
|
||||
// const { owned, images } = validateVideoConfig(input, config, customOwned);
|
||||
|
||||
// 判断生成类型
|
||||
const genType: "text" | "image" = images.length === 0 ? "text" : "image";
|
||||
const genType: "text" | "image" = input.imageBase64 && input.imageBase64.length === 0 ? "text" : "image";
|
||||
|
||||
// 校验宽高比(仅文生视频需要)
|
||||
if (genType === "text" && owned.aspectRatio.length > 0 && !owned.aspectRatio.includes(input.aspectRatio as `${number}:${number}`)) {
|
||||
throw new Error(`模型 ${owned.model} 不支持宽高比 ${input.aspectRatio},支持的宽高比:${owned.aspectRatio.join("、")}`);
|
||||
}
|
||||
// // 校验宽高比(仅文生视频需要)
|
||||
// if (genType === "text" && owned.aspectRatio.length > 0 && !owned.aspectRatio.includes(input.aspectRatio as `${number}:${number}`)) {
|
||||
// throw new Error(`模型 ${owned.model} 不支持宽高比 ${input.aspectRatio},支持的宽高比:${owned.aspectRatio.join("、")}`);
|
||||
// }
|
||||
|
||||
// 创建任务
|
||||
let taskId: string;
|
||||
@ -51,13 +51,13 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
if (genType === "text") {
|
||||
// 文生视频
|
||||
const requestBody: Record<string, unknown> = {
|
||||
model: owned.model,
|
||||
model: config.model,
|
||||
prompt: input.prompt,
|
||||
duration: input.duration,
|
||||
resolution: input.resolution,
|
||||
aspect_ratio: input.aspectRatio,
|
||||
};
|
||||
if (owned.audio && input.audio !== undefined) {
|
||||
if (input.audio && input.audio !== undefined) {
|
||||
requestBody.audio = input.audio;
|
||||
}
|
||||
|
||||
@ -71,15 +71,15 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
} else {
|
||||
// 图生视频
|
||||
const requestBody: Record<string, unknown> = {
|
||||
model: owned.model,
|
||||
images: images,
|
||||
model: config.model,
|
||||
images: input.imageBase64,
|
||||
duration: input.duration,
|
||||
resolution: input.resolution,
|
||||
};
|
||||
if (input.prompt) {
|
||||
requestBody.prompt = input.prompt;
|
||||
}
|
||||
if (owned.audio && input.audio !== undefined) {
|
||||
if (input.audio && input.audio !== undefined) {
|
||||
requestBody.audio = input.audio;
|
||||
}
|
||||
|
||||
|
||||
@ -5,11 +5,11 @@ import { pollTask, validateVideoConfig } from "@/utils/ai/utils";
|
||||
export default async (input: VideoConfig, config: AIConfig) => {
|
||||
if (!config.apiKey) throw new Error("缺少API Key");
|
||||
|
||||
const { owned, images, hasStartEndType } = validateVideoConfig(input, config);
|
||||
|
||||
// const { owned, images, hasStartEndType } = validateVideoConfig(input, config);
|
||||
const hasStartEndType = input.mode === "startEnd";
|
||||
const authorization = "Bearer " + config.apiKey.replace(/^Bearer\s*/i, "").trim();
|
||||
const baseUrl = config.baseURL ?? "https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks";
|
||||
|
||||
const images = input.imageBase64 || [];
|
||||
// 判断是否为首尾帧模式(需要两张图且类型支持首尾帧)
|
||||
const isStartEndMode = images.length === 2 && hasStartEndType;
|
||||
|
||||
@ -35,7 +35,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
};
|
||||
|
||||
// 仅当模型支持音频时才添加 generate_audio 字段
|
||||
if (owned.audio) {
|
||||
if (input?.audio) {
|
||||
requestBody.generate_audio = input.audio ?? false;
|
||||
}
|
||||
// 创建视频生成任务
|
||||
|
||||
@ -39,8 +39,10 @@ const getSizeFromConfig = (resolution: string, aspectRatio: string): string => {
|
||||
export default async (input: VideoConfig, config: AIConfig) => {
|
||||
if (!config.apiKey) throw new Error("缺少API Key");
|
||||
|
||||
const { owned, images, hasStartEndType, hasTextType } = validateVideoConfig(input, config);
|
||||
|
||||
// const { owned, images, hasStartEndType, hasTextType } = validateVideoConfig(input, config);
|
||||
const hasStartEndType = input.mode === "startEnd";
|
||||
const hasTextType = input.mode === "text";
|
||||
const images = input.imageBase64 || [];
|
||||
const defaultBaseUrl = [
|
||||
"https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis",
|
||||
"https://dashscope.aliyuncs.com/api/v1/services/aigc/image2video/video-synthesis",
|
||||
@ -49,7 +51,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
|
||||
const [i2vUrl, kf2vUrl, queryUrl] = (config.baseURL || defaultBaseUrl).split("|");
|
||||
|
||||
const types = owned.type;
|
||||
// const types = owned.type;
|
||||
const authorization = `Bearer ${config.apiKey}`;
|
||||
|
||||
// 确定端点和构建请求体
|
||||
@ -69,7 +71,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
duration: input.duration,
|
||||
},
|
||||
};
|
||||
} else if (types.includes("singleImage")) {
|
||||
} else if (input.mode == 'single' && images.length === 1) {
|
||||
// 图生视频
|
||||
submitUrl = i2vUrl;
|
||||
body = {
|
||||
@ -84,7 +86,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
},
|
||||
};
|
||||
// audio参数仅部分模型支持
|
||||
if (owned.audio && input.audio !== undefined) {
|
||||
if (input.audio && input.audio !== undefined) {
|
||||
body.parameters.audio = input.audio;
|
||||
}
|
||||
} else if (hasStartEndType) {
|
||||
@ -95,9 +97,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
first_frame_url: images[0],
|
||||
};
|
||||
// 尾帧处理
|
||||
if (types.includes("startEndRequired")) {
|
||||
inputObj.last_frame_url = images[1];
|
||||
} else if ((types.includes("endFrameOptional") || types.includes("startFrameOptional")) && images.length >= 2) {
|
||||
if (hasStartEndType && images.length >= 2) {
|
||||
inputObj.last_frame_url = images[1];
|
||||
}
|
||||
body = {
|
||||
@ -109,7 +109,7 @@ export default async (input: VideoConfig, config: AIConfig) => {
|
||||
},
|
||||
};
|
||||
} else {
|
||||
throw new Error(`不支持的视频生成类型: ${types.join(", ")}`);
|
||||
throw new Error(`不支持的视频生成类型: ${hasStartEndType ? "startEnd" : hasTextType ? "text" : "single"}`);
|
||||
}
|
||||
|
||||
// 提交任务
|
||||
|
||||
@ -6,6 +6,7 @@ interface VideoConfig {
|
||||
savePath: string;
|
||||
imageBase64?: string[];
|
||||
audio?: boolean;
|
||||
mode: "startEnd" | "multi" | "single" | "text";
|
||||
}
|
||||
|
||||
interface AIConfig {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user