2026-04-26 10:55:01 +08:00

106 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import express from "express";
import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { tool, jsonSchema } from "ai";
const router = express.Router();
// 检查语言模型
export default router.post(
"/",
validateFields({
modelName: z.string(),
type: z.enum(["text", "video", "image"]),
id: z.string(),
}),
async (req, res) => {
const { modelName, type, id } = req.body;
try {
const requestFn: Record<string, { fnName: string; modelData?: any }> = {
text: { fnName: "textRequest" },
image: {
fnName: "imageRequest",
modelData: {
prompt:
"一张16:9比例的图片完美等分为2x2四宫格布局各区域无缝衔接\n左上宫格一只可爱的猫毛发蓬松眼睛明亮姿态俏皮\n右上宫格一只友善的狗金毛犬表情愉悦摇着尾巴\n左下宫格一头健壮的牛田园背景目光温和皮毛光泽\n右下宫格一匹骏马姿态优雅鬃毛飘逸肌肉健美\n风格要求四个宫格风格统一色彩鲜艳饱和高清画质细节清晰锐利专业插画风格线条干净统一的左上方光源柔和阴影和谐配色卡通/半写实风格,宫格间用白色或浅灰细线分隔", //图片提示词
referenceList: [], //输入的图片提示词
size: "1K", // 图片尺寸
aspectRatio: "16:9",
},
},
video: { fnName: "videoRequest", modelData: {} },
} as const;
const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first();
if (!vendorConfigData) return res.status(500).send(error("未找到该供应商配置"));
if (!vendorConfigData.models) return res.status(500).send(error("未找到模型列表"));
const modelList = await u.vendor.getModelList(vendorConfigData.id!);
const selectedModel = modelList.find((i: any) => i.modelName == modelName);
if (type == "video") {
requestFn["video"].modelData = {
model: modelName,
duration: selectedModel.durationResolutionMap[0].duration[0],
resolution: selectedModel.durationResolutionMap[0].resolution[0],
aspectRatio: "16:9",
prompt:
"A shirtless middle-aged man with a horse head is standing in a supermarket, carefully comparing two identical bottles of shampoo for 3 seconds, then suddenly bursts into tears, drops to his knees dramatically, a flock of pigeons explodes out of nowhere from behind him, the supermarket lights flicker, an old grandma nearby continues shopping completely unbothered, the horse head man instantly stops crying, puts both shampoo bottles back, and moonwalks away disappearing into the vegetable section. Security camera footage style, slightly grainy, 5 seconds.",
referenceList: [],
audio: false,
mode: "text",
};
}
const reqConfig = requestFn[type as "text" | "video" | "image"];
const getWeatherTool = tool({
description: "Get the weather in a location",
inputSchema: jsonSchema<{ location: string }>(
z
.object({
location: z.string().describe("The location to get the weather for"),
})
.toJSONSchema(),
),
execute: async ({ location }) => {
return {
location,
temperature: 72 + Math.floor(Math.random() * 21) - 10,
};
},
});
if (type == "text") {
const { textStream } = await u.Ai.Text(`${id}:${modelName}`).stream({
prompt: "请调用工具获取火星的天气,并回答我多少气温",
tools: { getWeatherTool },
});
let fullResponse = "";
for await (const chunk of textStream) {
fullResponse += chunk;
}
if (!fullResponse) return res.status(500).send(error("模型未返回结果"));
res.status(200).send(success(fullResponse));
} else {
const aiTypeFn = {
image: "Image",
video: "Video",
} as const;
const reqFn = await u.Ai[aiTypeFn[type as "image" | "video"]](`${id}:${modelName}`).run({
...reqConfig.modelData,
});
await reqFn.save(type == "video" ? "test.mp4" : "testImage.jpg");
const resultUrl = await u.oss.getFileUrl(type == "video" ? "test.mp4" : "testImage.jpg");
res.status(200).send(success(resultUrl));
}
} catch (err) {
console.error(err);
const msg = u.error(err).message;
console.error(msg);
res.status(500).send(error(msg));
}
},
);