feat(*): 插件中心与模型协议 - 完善声明式协议运行时并增加功能开放控制
This commit is contained in:
@@ -1,125 +1,19 @@
|
||||
export type ModelProtocol =
|
||||
| "chat-completion"
|
||||
| "openai-response"
|
||||
| "openai-image"
|
||||
| "grok-image"
|
||||
| "volcengine-ark-image"
|
||||
| "volcengine-jimeng-image"
|
||||
| "gemini-image"
|
||||
| "openai-audio"
|
||||
| "async-audio"
|
||||
| "newapi"
|
||||
| "newapi-channel-1"
|
||||
| "newapi-channel-2"
|
||||
| "xai-video"
|
||||
| "volcengine-ark-video"
|
||||
| "volcengine-jimeng-video"
|
||||
| "gemini-veo"
|
||||
| "novita-video"
|
||||
| "minimax-video";
|
||||
|
||||
export type ModelProtocol = string;
|
||||
export type ProtocolCapability = "text" | "image" | "video" | "audio";
|
||||
export type ModelProtocolDefinition = { value: ModelProtocol; label: string; vendor?: string; capability: ProtocolCapability; create: string; contentType: string; poll?: string; media: string; enabled?: boolean };
|
||||
|
||||
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: "gemini-image", label: "Gemini Images", capability: "image", create: "POST /v1beta/models/{model}:generateContent", contentType: "application/json", media: "文生图、图片编辑与多张内嵌参考图" },
|
||||
{ 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: "文本或单张起始图" },
|
||||
{ value: "minimax-video", label: "MiniMax 视频", capability: "video", create: "POST /v2/video_generation", poll: "GET /v2/query/video_generation/{task_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 protocolGroups(protocols: ModelProtocolDefinition[]) {
|
||||
return (["text", "image", "video", "audio"] as ProtocolCapability[]).map((capability) => ({ label: { text: "文本", image: "图片", video: "视频", audio: "音频" }[capability], options: protocols.filter((item) => item.capability === capability && item.enabled !== false).map((item) => ({ label: `${item.label} · ${item.create.replace(/^POST /, "")}`, value: item.value })) }));
|
||||
}
|
||||
export function modelProtocolDefinition(value: string | undefined, definitions: ModelProtocolDefinition[] = []) { return definitions.find((item) => item.value === value); }
|
||||
export function modelProtocolLabel(value: string | undefined, definitions: ModelProtocolDefinition[] = []) { return modelProtocolDefinition(value, definitions)?.label || (value ? value : "未安装协议"); }
|
||||
export function modelProtocolCapability(value: string | undefined, definitions: ModelProtocolDefinition[] = []) { return modelProtocolDefinition(value, definitions)?.capability; }
|
||||
export function modelProtocolSupportsTokenBilling(capability?: string, _protocol?: string) { return capability === "text"; }
|
||||
|
||||
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[] = []): 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("gemini-image") || normalized.has("gemini-images")) return "gemini-image";
|
||||
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";
|
||||
export function protocolForModelCatalog(_endpointTypes: string[] = []): ModelProtocol | undefined {
|
||||
// A provider catalog cannot invent a protocol ID. The channel's selected
|
||||
// plugin or an explicit model configuration must supply it.
|
||||
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 }));
|
||||
}
|
||||
export function modelProtocolSummary(value: string | undefined, definitions: ModelProtocolDefinition[] = []) { const protocol = modelProtocolDefinition(value, definitions); return protocol ? [protocol.create, protocol.contentType, protocol.poll, protocol.media].filter(Boolean).join(" · ") : "当前协议未安装或尚未选择。"; }
|
||||
export function normalizeModelProtocol(value: unknown): ModelProtocol | undefined { return typeof value === "string" && value.trim() ? value.trim() : undefined; }
|
||||
|
||||
Reference in New Issue
Block a user