mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
feat(openclaw): add default model button and fix sessionsApi export
- Add "Enable as Default" button to OpenClaw provider cards - Place enable button before add/remove button for better UX - Disable remove button when provider is set as default - Add sessionsApi re-export from api barrel file to fix white screen - Add i18n keys for default model feature (zh/en/ja)
This commit is contained in:
@@ -198,6 +198,7 @@ function App() {
|
||||
switchProvider,
|
||||
deleteProvider,
|
||||
saveUsageScript,
|
||||
setAsDefaultModel,
|
||||
} = useProviderActions(activeApp);
|
||||
|
||||
const disableOmoMutation = useDisableCurrentOmo();
|
||||
@@ -670,6 +671,9 @@ function App() {
|
||||
activeApp === "claude" ? handleOpenTerminal : undefined
|
||||
}
|
||||
onCreate={() => setIsAddOpen(true)}
|
||||
onSetAsDefault={
|
||||
activeApp === "openclaw" ? setAsDefaultModel : undefined
|
||||
}
|
||||
/>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Terminal,
|
||||
TestTube2,
|
||||
Trash2,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -36,6 +37,9 @@ interface ProviderActionsProps {
|
||||
isAutoFailoverEnabled?: boolean;
|
||||
isInFailoverQueue?: boolean;
|
||||
onToggleFailover?: (enabled: boolean) => void;
|
||||
// OpenClaw: default model
|
||||
isDefaultModel?: boolean;
|
||||
onSetAsDefault?: () => void;
|
||||
}
|
||||
|
||||
export function ProviderActions({
|
||||
@@ -58,6 +62,9 @@ export function ProviderActions({
|
||||
isAutoFailoverEnabled = false,
|
||||
isInFailoverQueue = false,
|
||||
onToggleFailover,
|
||||
// OpenClaw: default model
|
||||
isDefaultModel = false,
|
||||
onSetAsDefault,
|
||||
}: ProviderActionsProps) {
|
||||
const { t } = useTranslation();
|
||||
const iconButtonClass = "h-8 w-8 p-1";
|
||||
@@ -120,10 +127,12 @@ export function ProviderActions({
|
||||
if (isAdditiveMode) {
|
||||
if (isInConfig) {
|
||||
return {
|
||||
disabled: false,
|
||||
disabled: isDefaultModel === true,
|
||||
variant: "secondary" as const,
|
||||
className:
|
||||
className: cn(
|
||||
"bg-orange-100 text-orange-600 hover:bg-orange-200 dark:bg-orange-900/50 dark:text-orange-400 dark:hover:bg-orange-900/70",
|
||||
isDefaultModel && "opacity-40 cursor-not-allowed",
|
||||
),
|
||||
icon: <Minus className="h-4 w-4" />,
|
||||
text: t("provider.removeFromConfig", { defaultValue: "移除" }),
|
||||
};
|
||||
@@ -191,6 +200,26 @@ export function ProviderActions({
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{appId === "openclaw" && isInConfig && onSetAsDefault && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant={isDefaultModel ? "secondary" : "default"}
|
||||
onClick={isDefaultModel ? undefined : onSetAsDefault}
|
||||
disabled={isDefaultModel}
|
||||
className={cn(
|
||||
"w-[4.5rem] px-2.5",
|
||||
isDefaultModel
|
||||
? "bg-gray-200 text-muted-foreground dark:bg-gray-700 opacity-60 cursor-not-allowed"
|
||||
: "bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700",
|
||||
)}
|
||||
>
|
||||
<Zap className="h-4 w-4" />
|
||||
{isDefaultModel
|
||||
? t("provider.isDefault", { defaultValue: "默认" })
|
||||
: t("provider.setAsDefault", { defaultValue: "启用" })}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
size="sm"
|
||||
variant={buttonState.variant}
|
||||
|
||||
@@ -48,6 +48,9 @@ interface ProviderCardProps {
|
||||
isInFailoverQueue?: boolean; // 是否在故障转移队列中
|
||||
onToggleFailover?: (enabled: boolean) => void; // 切换故障转移队列
|
||||
activeProviderId?: string; // 代理当前实际使用的供应商 ID(用于故障转移模式下标注绿色边框)
|
||||
// OpenClaw: default model
|
||||
isDefaultModel?: boolean;
|
||||
onSetAsDefault?: () => void;
|
||||
}
|
||||
|
||||
const extractApiUrl = (provider: Provider, fallbackText: string) => {
|
||||
@@ -108,6 +111,9 @@ export function ProviderCard({
|
||||
isInFailoverQueue = false,
|
||||
onToggleFailover,
|
||||
activeProviderId,
|
||||
// OpenClaw: default model
|
||||
isDefaultModel,
|
||||
onSetAsDefault,
|
||||
}: ProviderCardProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -386,6 +392,9 @@ export function ProviderCard({
|
||||
isAutoFailoverEnabled={isAutoFailoverEnabled}
|
||||
isInFailoverQueue={isInFailoverQueue}
|
||||
onToggleFailover={onToggleFailover}
|
||||
// OpenClaw: default model
|
||||
isDefaultModel={isDefaultModel}
|
||||
onSetAsDefault={onSetAsDefault}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import type { Provider } from "@/types";
|
||||
import type { AppId } from "@/lib/api";
|
||||
import { providersApi } from "@/lib/api/providers";
|
||||
import { openclawApi } from "@/lib/api/openclaw";
|
||||
import { useDragSort } from "@/hooks/useDragSort";
|
||||
import { ProviderCard } from "@/components/providers/ProviderCard";
|
||||
import { ProviderEmptyState } from "@/components/providers/ProviderEmptyState";
|
||||
@@ -51,6 +52,7 @@ interface ProviderListProps {
|
||||
isProxyRunning?: boolean; // 代理服务运行状态
|
||||
isProxyTakeover?: boolean; // 代理接管模式(Live配置已被接管)
|
||||
activeProviderId?: string; // 代理当前实际使用的供应商 ID(用于故障转移模式下标注绿色边框)
|
||||
onSetAsDefault?: (provider: Provider) => void; // OpenClaw: set as default model
|
||||
}
|
||||
|
||||
export function ProviderList({
|
||||
@@ -71,6 +73,7 @@ export function ProviderList({
|
||||
isProxyRunning = false,
|
||||
isProxyTakeover = false,
|
||||
activeProviderId,
|
||||
onSetAsDefault,
|
||||
}: ProviderListProps) {
|
||||
const { t } = useTranslation();
|
||||
const { sortedProviders, sensors, handleDragEnd } = useDragSort(
|
||||
@@ -105,6 +108,22 @@ export function ProviderList({
|
||||
[appId, opencodeLiveIds, openclawLiveIds],
|
||||
);
|
||||
|
||||
// OpenClaw: query default model to determine which provider is default
|
||||
const { data: openclawDefaultModel } = useQuery({
|
||||
queryKey: ["openclawDefaultModel"],
|
||||
queryFn: () => openclawApi.getDefaultModel(),
|
||||
enabled: appId === "openclaw",
|
||||
});
|
||||
|
||||
const isProviderDefaultModel = useCallback(
|
||||
(providerId: string): boolean => {
|
||||
if (appId !== "openclaw" || !openclawDefaultModel?.primary) return false;
|
||||
return openclawDefaultModel.primary.startsWith(providerId + "/");
|
||||
},
|
||||
[appId, openclawDefaultModel],
|
||||
);
|
||||
|
||||
// 故障转移相关
|
||||
const { data: isAutoFailoverEnabled } = useAutoFailoverEnabled(appId);
|
||||
const { data: failoverQueue } = useFailoverQueue(appId);
|
||||
const addToQueue = useAddToFailoverQueue();
|
||||
@@ -253,6 +272,11 @@ export function ProviderList({
|
||||
handleToggleFailover(provider.id, enabled)
|
||||
}
|
||||
activeProviderId={activeProviderId}
|
||||
// OpenClaw: default model
|
||||
isDefaultModel={isProviderDefaultModel(provider.id)}
|
||||
onSetAsDefault={
|
||||
onSetAsDefault ? () => onSetAsDefault(provider) : undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -365,6 +389,9 @@ interface SortableProviderCardProps {
|
||||
isInFailoverQueue: boolean;
|
||||
onToggleFailover: (enabled: boolean) => void;
|
||||
activeProviderId?: string;
|
||||
// OpenClaw: default model
|
||||
isDefaultModel?: boolean;
|
||||
onSetAsDefault?: () => void;
|
||||
}
|
||||
|
||||
function SortableProviderCard({
|
||||
@@ -392,6 +419,8 @@ function SortableProviderCard({
|
||||
isInFailoverQueue,
|
||||
onToggleFailover,
|
||||
activeProviderId,
|
||||
isDefaultModel,
|
||||
onSetAsDefault,
|
||||
}: SortableProviderCardProps) {
|
||||
const {
|
||||
setNodeRef,
|
||||
@@ -441,6 +470,9 @@ function SortableProviderCard({
|
||||
isInFailoverQueue={isInFailoverQueue}
|
||||
onToggleFailover={onToggleFailover}
|
||||
activeProviderId={activeProviderId}
|
||||
// OpenClaw: default model
|
||||
isDefaultModel={isDefaultModel}
|
||||
onSetAsDefault={onSetAsDefault}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,12 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
import { toast } from "sonner";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { providersApi, settingsApi, openclawApi, type AppId } from "@/lib/api";
|
||||
import type { Provider, UsageScript } from "@/types";
|
||||
import type {
|
||||
Provider,
|
||||
UsageScript,
|
||||
OpenClawProviderConfig,
|
||||
OpenClawDefaultModel,
|
||||
} from "@/types";
|
||||
import type { OpenClawSuggestedDefaults } from "@/config/openclawProviderPresets";
|
||||
import {
|
||||
useAddProviderMutation,
|
||||
@@ -218,12 +223,54 @@ export function useProviderActions(activeApp: AppId) {
|
||||
[activeApp, queryClient, t],
|
||||
);
|
||||
|
||||
// Set provider as default model (OpenClaw only)
|
||||
const setAsDefaultModel = useCallback(
|
||||
async (provider: Provider) => {
|
||||
const config = provider.settingsConfig as OpenClawProviderConfig;
|
||||
if (!config.models || config.models.length === 0) {
|
||||
toast.error(
|
||||
t("notifications.openclawNoModels", {
|
||||
defaultValue: "该供应商没有配置模型",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const model: OpenClawDefaultModel = {
|
||||
primary: `${provider.id}/${config.models[0].id}`,
|
||||
fallbacks: config.models.slice(1).map((m) => `${provider.id}/${m.id}`),
|
||||
};
|
||||
|
||||
try {
|
||||
await openclawApi.setDefaultModel(model);
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["openclawDefaultModel"],
|
||||
});
|
||||
toast.success(
|
||||
t("notifications.openclawDefaultModelSet", {
|
||||
defaultValue: "已设为默认模型",
|
||||
}),
|
||||
{ closeButton: true },
|
||||
);
|
||||
} catch (error) {
|
||||
const detail =
|
||||
extractErrorMessage(error) ||
|
||||
t("notifications.openclawDefaultModelSetFailed", {
|
||||
defaultValue: "设置默认模型失败",
|
||||
});
|
||||
toast.error(detail);
|
||||
}
|
||||
},
|
||||
[queryClient, t],
|
||||
);
|
||||
|
||||
return {
|
||||
addProvider,
|
||||
updateProvider,
|
||||
switchProvider,
|
||||
deleteProvider,
|
||||
saveUsageScript,
|
||||
setAsDefaultModel,
|
||||
isLoading:
|
||||
addProviderMutation.isPending ||
|
||||
updateProviderMutation.isPending ||
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
"addOpenCodeProvider": "Add OpenCode Provider",
|
||||
"addToConfig": "Add",
|
||||
"removeFromConfig": "Remove",
|
||||
"setAsDefault": "Enable",
|
||||
"isDefault": "Default",
|
||||
"inConfig": "Added",
|
||||
"addProviderHint": "Fill in the information to quickly switch providers in the list.",
|
||||
"editClaudeProvider": "Edit Claude Code Provider",
|
||||
@@ -163,7 +165,10 @@
|
||||
"settingsSaveFailed": "Failed to save settings: {{error}}",
|
||||
"openAIChatFormatHint": "This provider uses OpenAI Chat format and requires the proxy service to be enabled",
|
||||
"openLinkFailed": "Failed to open link",
|
||||
"openclawModelsRegistered": "Models have been registered to /model list"
|
||||
"openclawModelsRegistered": "Models have been registered to /model list",
|
||||
"openclawDefaultModelSet": "Set as default model",
|
||||
"openclawDefaultModelSetFailed": "Failed to set default model",
|
||||
"openclawNoModels": "No models configured"
|
||||
},
|
||||
"confirm": {
|
||||
"deleteProvider": "Delete Provider",
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
"addOpenCodeProvider": "OpenCode プロバイダーを追加",
|
||||
"addToConfig": "追加",
|
||||
"removeFromConfig": "削除",
|
||||
"setAsDefault": "有効化",
|
||||
"isDefault": "デフォルト",
|
||||
"inConfig": "追加済み",
|
||||
"addProviderHint": "一覧にすばやく切り替えられるよう、ここに情報を入力してください。",
|
||||
"editClaudeProvider": "Claude Code プロバイダーを編集",
|
||||
@@ -163,7 +165,10 @@
|
||||
"settingsSaveFailed": "設定の保存に失敗しました: {{error}}",
|
||||
"openAIChatFormatHint": "このプロバイダーは OpenAI Chat フォーマットを使用しており、プロキシサービスの有効化が必要です",
|
||||
"openLinkFailed": "リンクを開けませんでした",
|
||||
"openclawModelsRegistered": "モデルが /model リストに登録されました"
|
||||
"openclawModelsRegistered": "モデルが /model リストに登録されました",
|
||||
"openclawDefaultModelSet": "デフォルトモデルに設定しました",
|
||||
"openclawDefaultModelSetFailed": "デフォルトモデルの設定に失敗しました",
|
||||
"openclawNoModels": "モデルが設定されていません"
|
||||
},
|
||||
"confirm": {
|
||||
"deleteProvider": "プロバイダーを削除",
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
"addOpenCodeProvider": "添加 OpenCode 供应商",
|
||||
"addToConfig": "添加",
|
||||
"removeFromConfig": "移除",
|
||||
"setAsDefault": "启用",
|
||||
"isDefault": "默认",
|
||||
"inConfig": "已添加",
|
||||
"addProviderHint": "填写信息后即可在列表中快速切换供应商。",
|
||||
"editClaudeProvider": "编辑 Claude Code 供应商",
|
||||
@@ -163,7 +165,10 @@
|
||||
"settingsSaveFailed": "保存设置失败:{{error}}",
|
||||
"openAIChatFormatHint": "此供应商使用 OpenAI Chat 格式,需要开启代理服务才能正常使用",
|
||||
"openLinkFailed": "链接打开失败",
|
||||
"openclawModelsRegistered": "模型已注册到 /model 列表"
|
||||
"openclawModelsRegistered": "模型已注册到 /model 列表",
|
||||
"openclawDefaultModelSet": "已设为默认模型",
|
||||
"openclawDefaultModelSetFailed": "设置默认模型失败",
|
||||
"openclawNoModels": "该供应商没有配置模型"
|
||||
},
|
||||
"confirm": {
|
||||
"deleteProvider": "删除供应商",
|
||||
|
||||
@@ -8,6 +8,7 @@ export { usageApi } from "./usage";
|
||||
export { vscodeApi } from "./vscode";
|
||||
export { proxyApi } from "./proxy";
|
||||
export { openclawApi } from "./openclaw";
|
||||
export { sessionsApi } from "./sessions";
|
||||
export * as configApi from "./config";
|
||||
export type { ProviderSwitchEvent } from "./providers";
|
||||
export type { Prompt } from "./prompts";
|
||||
|
||||
@@ -187,6 +187,9 @@ export const useSwitchProviderMutation = (appId: AppId) => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["openclawLiveProviderIds"],
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["openclawDefaultModel"],
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user