fix(models): 模型目录 - 识别公共视频能力并按声明发送分辨率
This commit is contained in:
@@ -4,10 +4,9 @@ import { useState } from "react";
|
||||
|
||||
import { ChannelHeadersEditor, validateChannelHeaders } from "@/components/channel-headers-editor";
|
||||
import { WorkspaceState } from "@/components/layout/workspace-state";
|
||||
import { defaultModelCapabilityConfig } from "@/lib/model-capabilities";
|
||||
import { modelProtocolCapability, protocolForModelCatalog } from "@/lib/model-protocols";
|
||||
import { mergeFetchedChannelModelCosts } from "@/lib/channel-model-catalog";
|
||||
import { desktopLocalChannelFormState, desktopLocalChannelPayloadValue, DESKTOP_LOCAL_CHANNEL_EXAMPLE_BASE_URL } from "@/lib/desktop-local-channel";
|
||||
import { fetchChannelModels, type ChannelModelCatalogItem } from "@/services/api/image";
|
||||
import { fetchChannelModels } from "@/services/api/image";
|
||||
import {
|
||||
createModelChannel,
|
||||
defaultBaseUrlForApiFormat,
|
||||
@@ -21,8 +20,6 @@ import { ChannelModelSettings } from "./channel-video-pricing";
|
||||
import { useUserStore } from "@/stores/use-user-store";
|
||||
|
||||
type UserChannelConnection = "openai" | "gemini";
|
||||
type ChannelModelCost = NonNullable<ModelChannel["modelCosts"]>[number];
|
||||
|
||||
export function ChannelSettingsPane({ onOpenModels }: { onOpenModels: () => void }) {
|
||||
const { message } = App.useApp();
|
||||
const config = useConfigStore((state) => state.config);
|
||||
@@ -108,7 +105,7 @@ export function ChannelSettingsPane({ onOpenModels }: { onOpenModels: () => void
|
||||
return;
|
||||
}
|
||||
updateChannels(
|
||||
latestConfig.channels.map((item) => (item.id === channel.id ? { ...item, models: result.models, modelCosts: mergeFetchedModelCosts(item, result.catalog) } : item)),
|
||||
latestConfig.channels.map((item) => (item.id === channel.id ? { ...item, models: result.models, modelCosts: mergeFetchedChannelModelCosts(item, result.catalog) } : item)),
|
||||
latestConfig,
|
||||
);
|
||||
message.success(`${latestChannel.name || "当前渠道"}模型列表已更新`);
|
||||
@@ -155,7 +152,7 @@ export function ChannelSettingsPane({ onOpenModels }: { onOpenModels: () => void
|
||||
updateChannels(
|
||||
latestConfig.channels.map((channel) => {
|
||||
const fetched = resultMap.get(channel.id);
|
||||
return fetched ? { ...channel, models: fetched.models, modelCosts: mergeFetchedModelCosts(channel, fetched.catalog) } : channel;
|
||||
return fetched ? { ...channel, models: fetched.models, modelCosts: mergeFetchedChannelModelCosts(channel, fetched.catalog) } : channel;
|
||||
}),
|
||||
latestConfig,
|
||||
);
|
||||
@@ -310,25 +307,6 @@ function normalizeDefaultModel(value: string, options: string[]) {
|
||||
return options.includes(value) ? value : options[0] || "";
|
||||
}
|
||||
|
||||
function mergeFetchedModelCosts(channel: ModelChannel, catalog: ChannelModelCatalogItem[]): ChannelModelCost[] {
|
||||
const existingByModel = new Map((channel.modelCosts || []).map((cost) => [cost.model, cost]));
|
||||
const next: ChannelModelCost[] = [];
|
||||
for (const item of catalog) {
|
||||
const existing = existingByModel.get(item.id);
|
||||
const inferredProtocol = protocolForModelCatalog(item.supportedEndpointTypes);
|
||||
if (existing) {
|
||||
const inferredCapability = modelProtocolCapability(inferredProtocol);
|
||||
next.push(inferredProtocol && existing.protocol !== inferredProtocol ? { ...existing, protocol: inferredProtocol, capability: inferredCapability || existing.capability, capabilityConfig: inferredCapability === "image" || inferredCapability === "video" ? defaultModelCapabilityConfig(inferredProtocol, item.id) : undefined } : existing);
|
||||
continue;
|
||||
}
|
||||
const catalogProtocol = inferredProtocol || channel.interfaceType;
|
||||
const catalogCapability = catalogProtocol ? modelProtocolCapability(catalogProtocol) : undefined;
|
||||
if (!catalogProtocol || !catalogCapability) continue;
|
||||
next.push({ model: item.id, capability: catalogCapability, protocol: catalogProtocol, billingMode: "fixed_request", unitPriceMicrocredits: 0, capabilityConfig: catalogCapability === "image" || catalogCapability === "video" ? defaultModelCapabilityConfig(catalogProtocol, item.id) : undefined });
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function uniqueModels(models: string[]) {
|
||||
return Array.from(new Set(models.map((model) => model.trim()).filter(Boolean)));
|
||||
}
|
||||
|
||||
@@ -64,14 +64,15 @@ export function ChannelModelSettings({ channel, onChange }: { channel: ModelChan
|
||||
const protocol = cost?.protocol || defaultProtocolForModel(channel, model);
|
||||
const capability = cost?.capability || modelProtocolCapability(protocol) || "text";
|
||||
const billingMode = cost?.billingMode || "fixed_request";
|
||||
const displayName = cost?.displayName?.trim() || model;
|
||||
return (
|
||||
<div key={model} className="flex min-w-0 items-center gap-3 rounded-md bg-surface-active px-3 py-2.5 transition-colors hover:bg-surface-hover">
|
||||
<span className="grid size-8 shrink-0 place-items-center rounded-md bg-foreground/[.045] text-foreground/65">
|
||||
<Settings2 className="size-4" />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-xs font-medium" title={model}>
|
||||
{model}
|
||||
<div className="truncate text-xs font-medium" title={displayName === model ? model : `${displayName} (${model})`}>
|
||||
{displayName}
|
||||
</div>
|
||||
<div className="mt-1 flex min-w-0 flex-wrap items-center gap-1.5">
|
||||
<Tag className="mr-0 text-[var(--fs-tiny)]" bordered={false}>
|
||||
|
||||
Reference in New Issue
Block a user