223845b0ed
Co-authored-by: Copilot <copilot@github.com>
153 lines
3.3 KiB
TypeScript
153 lines
3.3 KiB
TypeScript
// server/utils/openai.ts
|
|
import OpenAI from "openai";
|
|
import type { BaseOptions, IImageGenerateData } from "#shared/types/openai";
|
|
|
|
const IMAGE_GENERATION_URL = "https://api.qflink.xyz/v1/images/generations";
|
|
const IMAGE_GENERATION_MODEL = "gpt-image-2";
|
|
const IMAGE_GENERATION_SIZE = "1024x1024";
|
|
|
|
interface IImageGenerationResponse {
|
|
/** 上游创建时间 */
|
|
created?: number;
|
|
/** 上游图片生成结果列表 */
|
|
data?: Array<{
|
|
/** 上游返回的修订提示词,可能为空 */
|
|
revised_prompt?: string;
|
|
/** 生成图片地址 */
|
|
url?: string;
|
|
}>;
|
|
}
|
|
|
|
/** 通用 AI 调用函数(支持文本 / 图文 / 多模态) */
|
|
export const askAI = async ({
|
|
apiKey,
|
|
model,
|
|
baseURL,
|
|
input
|
|
}: BaseOptions & {
|
|
input: OpenAI.Responses.ResponseCreateParams["input"];
|
|
}) => {
|
|
const client = new OpenAI({ apiKey, baseURL });
|
|
|
|
return await client.responses.create({
|
|
model,
|
|
input
|
|
});
|
|
};
|
|
|
|
/** 调用文本模型 */
|
|
export const askText = async (
|
|
options: BaseOptions & {
|
|
text: string;
|
|
}
|
|
) => {
|
|
const res = await askAI({
|
|
...options,
|
|
input: options.text
|
|
});
|
|
|
|
return res.output_text;
|
|
};
|
|
|
|
/**
|
|
* 调用视觉模型:输入「文本 + 图片」,输出文本
|
|
*
|
|
* 分辨率参数说明:
|
|
*
|
|
* | 值 | 含义 |
|
|
* |------|--------------------------|
|
|
* | low | 低分辨率(更快、更省成本) |
|
|
* | high | 高分辨率(更精准) |
|
|
* | auto | 自动选择 |
|
|
*
|
|
*/
|
|
export const askVision = async (
|
|
options: BaseOptions & {
|
|
text: string;
|
|
image: string;
|
|
detail?: "low" | "high" | "auto";
|
|
}
|
|
) => {
|
|
// 如果图片不是 URL,也不是 data URL,就当成 base64 处理,自动补全 data URL 前缀
|
|
const normalizeImage = (img: string) => {
|
|
if (img.startsWith("http")) return img;
|
|
if (!img.startsWith("data:")) {
|
|
return `data:image/png;base64,${img}`;
|
|
}
|
|
return img;
|
|
};
|
|
|
|
const res = await askAI({
|
|
...options,
|
|
input: [
|
|
{
|
|
role: "user",
|
|
content: [
|
|
{
|
|
type: "input_text",
|
|
text: options.text
|
|
},
|
|
{
|
|
type: "input_image",
|
|
image_url: normalizeImage(options.image),
|
|
detail: options.detail || "auto"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
});
|
|
|
|
return res.output_text;
|
|
};
|
|
|
|
/** 调用图片生成接口,完整 API Key 只在服务端使用 */
|
|
export const askImg = async ({
|
|
apiKey,
|
|
prompt
|
|
}: {
|
|
apiKey: string;
|
|
prompt: string;
|
|
}): Promise<IImageGenerateData> => {
|
|
const result = await $fetch<IImageGenerationResponse>(IMAGE_GENERATION_URL, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: {
|
|
model: IMAGE_GENERATION_MODEL,
|
|
prompt,
|
|
size: IMAGE_GENERATION_SIZE
|
|
}
|
|
});
|
|
|
|
const image = result.data?.[0];
|
|
if (!image?.url) {
|
|
throw new Error("图片生成失败");
|
|
}
|
|
|
|
return {
|
|
imageUrl: image.url,
|
|
revisedPrompt: image.revised_prompt
|
|
};
|
|
};
|
|
|
|
/** 调用流式接口 */
|
|
export const askStream = async (
|
|
options: BaseOptions & {
|
|
input: OpenAI.Responses.ResponseCreateParams["input"];
|
|
}
|
|
) => {
|
|
const client = new OpenAI({
|
|
apiKey: options.apiKey,
|
|
baseURL: options.baseURL
|
|
});
|
|
|
|
const stream = await client.responses.stream({
|
|
model: options.model,
|
|
input: options.input
|
|
});
|
|
|
|
return stream;
|
|
};
|