Files
ttstd-infinite-canvas/web/src/lib/model-protocols.ts

125 lines
7.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.
export type ModelProtocol =
| "chat-completion"
| "openai-response"
| "openai-image"
| "grok-image"
| "volcengine-ark-image"
| "volcengine-jimeng-image"
| "openai-audio"
| "async-audio"
| "newapi"
| "newapi-channel-1"
| "newapi-channel-2"
| "xai-video"
| "volcengine-ark-video"
| "volcengine-jimeng-video"
| "gemini-veo"
| "novita-video";
export type ProtocolCapability = "text" | "image" | "video" | "audio";
export type ModelProtocolDefinition = {
value: ModelProtocol;
label: string;
capability: ProtocolCapability;
create: string;
contentType: string;
poll?: string;
media: string;
};
export const MODEL_PROTOCOLS: ModelProtocolDefinition[] = [
{ value: "chat-completion", label: "OpenAI Chat Completions", capability: "text", create: "POST /v1/chat/completions", contentType: "application/json", media: "文本与多模态消息" },
{ value: "openai-response", label: "OpenAI Responses", capability: "text", create: "POST /v1/responses", contentType: "application/json", media: "文本与多模态输入" },
{ value: "openai-image", label: "OpenAI Images", capability: "image", create: "POST /v1/images/generations", contentType: "application/json / multipart", media: "生成、编辑与参考图" },
{ value: "grok-image", label: "Grok Images", capability: "image", create: "POST /v1/images/generations / edits", contentType: "application/json", media: "文生图与单张 URL 参考图,不支持蒙版" },
{ value: "volcengine-ark-image", label: "火山方舟图片", capability: "image", create: "POST /api/v3/images/generations", contentType: "application/json", media: "文生图与 image 参考图,不支持蒙版" },
{ value: "volcengine-jimeng-image", label: "即梦官方图片", capability: "image", create: "POST CVSync2AsyncSubmitTask", poll: "POST CVSync2AsyncGetResult", contentType: "application/json + AK/SK 签名", media: "0-14 张参考图,模型标识填写 req_key" },
{ value: "openai-audio", label: "OpenAI Audio", capability: "audio", create: "POST /v1/audio/speech", contentType: "application/json", media: "文本转语音" },
{ value: "async-audio", label: "异步音频任务", capability: "audio", create: "POST /v1/audio/tasks", poll: "GET /v1/audio/tasks/{task_id}", contentType: "application/json", media: "语音、音效与音乐生成" },
{ value: "newapi", label: "OpenAI / NewAPI Videos", capability: "video", create: "POST /v1/videos", poll: "GET /v1/videos/{task_id}", contentType: "multipart/form-data", media: "input_reference[] 参考图" },
{ value: "newapi-channel-1", label: "NewAPI 媒体任务", capability: "video", create: "POST /v1/videos", poll: "GET /v1/videos/{task_id}", contentType: "application/json", media: "图片、视频、音频公网 URL" },
{
value: "newapi-channel-2",
label: "NewAPI Video Generations",
capability: "video",
create: "POST /v1/video/generations",
poll: "GET /v1/video/generations/{task_id}",
contentType: "application/json",
media: "image_urls(首帧、尾帧、其他参考图) / video_urls / audio_urls",
},
{ value: "xai-video", label: "xAI 官方视频", capability: "video", create: "POST /v1/videos/generations", poll: "GET /v1/videos/{request_id}", contentType: "application/json", media: "单张起始图" },
{
value: "volcengine-ark-video",
label: "火山方舟视频",
capability: "video",
create: "POST /api/v3/contents/generations/tasks",
poll: "GET /api/v3/contents/generations/tasks/{task_id}",
contentType: "application/json",
media: "图片、视频、音频参考素材",
},
{ value: "volcengine-jimeng-video", label: "即梦官方视频", capability: "video", create: "POST CVSync2AsyncSubmitTask", poll: "POST CVSync2AsyncGetResult", contentType: "application/json + AK/SK 签名", media: "文本或一张首帧图,模型标识填写 req_key" },
{ value: "gemini-veo", label: "Gemini Veo", capability: "video", create: "POST /v1beta/models/{model}:predictLongRunning", poll: "GET /v1beta/{operation_name}", contentType: "application/json", media: "文本与单张起始图" },
{ value: "novita-video", label: "Novita 视频", capability: "video", create: "POST /v3/video/create", poll: "GET /v3/async/task-result?task_id={id}", contentType: "application/json", media: "文本或单张起始图" },
];
export const MODEL_PROTOCOL_OPTIONS = protocolGroups(MODEL_PROTOCOLS.filter((item) => !item.value.startsWith("volcengine-jimeng-")));
export const SYSTEM_MODEL_PROTOCOL_OPTIONS = protocolGroups(MODEL_PROTOCOLS);
function protocolGroups(protocols: ModelProtocolDefinition[]) {
return [
{ label: "文本", options: protocolOptions("text") },
{ label: "图片", options: protocolOptions("image") },
{ label: "视频", options: protocolOptions("video") },
{ label: "音频", options: protocolOptions("audio") },
].map((group) => ({ ...group, options: group.options.filter((option) => protocols.some((protocol) => protocol.value === option.value)) }));
}
export function modelProtocolDefinition(value?: string) {
return MODEL_PROTOCOLS.find((item) => item.value === value);
}
export function modelProtocolLabel(value?: string) {
return modelProtocolDefinition(value)?.label || "自动识别";
}
export function modelProtocolCapability(value?: string) {
return modelProtocolDefinition(value)?.capability;
}
export function modelProtocolSupportsTokenBilling(capability?: string, protocol?: string) {
return capability === "text" || (capability === "video" && protocol === "volcengine-ark-video");
}
export function protocolForModelCatalog(endpointTypes: string[] = [], modelType?: string): ModelProtocol | undefined {
const normalized = new Set(endpointTypes.map((value) => value.trim().toLowerCase()));
if (normalized.has("openai-chat") || normalized.has("chat-completion") || normalized.has("chat")) return "chat-completion";
if (normalized.has("openai-response") || normalized.has("responses")) return "openai-response";
if (normalized.has("openai-image") || normalized.has("image")) return "openai-image";
if (normalized.has("openai-video") || normalized.has("video")) return "newapi-channel-2";
if (normalized.has("openai-audio") || normalized.has("audio")) return "openai-audio";
if (normalized.has("grok-image")) return "grok-image";
if (normalized.has("gemini-veo") || normalized.has("gemini-video") || normalized.has("veo")) return "gemini-veo";
if (normalized.has("volcengine-ark-video") || normalized.has("volcengine-ark")) return "volcengine-ark-video";
if (modelType?.trim().toLowerCase() === "video") return "newapi";
if (modelType?.trim().toLowerCase() === "image") return "openai-image";
if (modelType?.trim().toLowerCase() === "audio") return "openai-audio";
if (modelType?.trim().toLowerCase() === "text") return "chat-completion";
return undefined;
}
export function modelProtocolSummary(value?: string) {
const protocol = modelProtocolDefinition(value);
if (!protocol) return "保存模型时选择协议后,将在这里显示实际请求方式。";
return [protocol.create, protocol.contentType, protocol.poll, protocol.media].filter(Boolean).join(" · ");
}
export function normalizeModelProtocol(value: unknown): ModelProtocol | undefined {
return typeof value === "string" && MODEL_PROTOCOLS.some((item) => item.value === value) ? (value as ModelProtocol) : undefined;
}
function protocolOptions(capability: ProtocolCapability) {
return MODEL_PROTOCOLS.filter((item) => item.capability === capability).map((item) => ({ label: `${item.label} · ${item.create.replace("POST ", "")}`, value: item.value }));
}