mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 08:44:41 +08:00
Merge branch 'main' into fix/openclaw-serialize-panic
# Conflicts: # src-tauri/src/proxy/providers/claude.rs # src-tauri/src/services/stream_check.rs
This commit is contained in:
+1
-1
@@ -255,7 +255,7 @@ function App() {
|
||||
deleteProvider,
|
||||
saveUsageScript,
|
||||
setAsDefaultModel,
|
||||
} = useProviderActions(activeApp);
|
||||
} = useProviderActions(activeApp, isProxyRunning);
|
||||
|
||||
const disableOmoMutation = useDisableCurrentOmo();
|
||||
const handleDisableOmo = () => {
|
||||
|
||||
@@ -42,9 +42,9 @@ export function AddProviderDialog({
|
||||
const { t } = useTranslation();
|
||||
// OpenCode and OpenClaw don't support universal providers
|
||||
const showUniversalTab = appId !== "opencode" && appId !== "openclaw";
|
||||
const [activeTab, setActiveTab] = useState<
|
||||
"app-specific" | "universal"
|
||||
>("app-specific");
|
||||
const [activeTab, setActiveTab] = useState<"app-specific" | "universal">(
|
||||
"app-specific",
|
||||
);
|
||||
const [universalFormOpen, setUniversalFormOpen] = useState(false);
|
||||
const [selectedUniversalPreset, setSelectedUniversalPreset] =
|
||||
useState<UniversalProviderPreset | null>(null);
|
||||
@@ -284,9 +284,7 @@ export function AddProviderDialog({
|
||||
{showUniversalTab ? (
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={(v) =>
|
||||
setActiveTab(v as "app-specific" | "universal")
|
||||
}
|
||||
onValueChange={(v) => setActiveTab(v as "app-specific" | "universal")}
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-2 mb-6">
|
||||
<TabsTrigger value="app-specific">
|
||||
|
||||
@@ -108,6 +108,10 @@ interface ClaudeFormFieldsProps {
|
||||
// Auth Field (ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY)
|
||||
apiKeyField: ClaudeApiKeyField;
|
||||
onApiKeyFieldChange: (field: ClaudeApiKeyField) => void;
|
||||
|
||||
// Full URL mode
|
||||
isFullUrl: boolean;
|
||||
onFullUrlChange: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export function ClaudeFormFields({
|
||||
@@ -149,6 +153,8 @@ export function ClaudeFormFields({
|
||||
onApiFormatChange,
|
||||
apiKeyField,
|
||||
onApiKeyFieldChange,
|
||||
isFullUrl,
|
||||
onFullUrlChange,
|
||||
}: ClaudeFormFieldsProps) {
|
||||
const { t } = useTranslation();
|
||||
const hasAnyAdvancedValue = !!(
|
||||
@@ -160,8 +166,7 @@ export function ClaudeFormFields({
|
||||
apiFormat !== "anthropic" ||
|
||||
apiKeyField !== "ANTHROPIC_AUTH_TOKEN"
|
||||
);
|
||||
const [advancedExpanded, setAdvancedExpanded] =
|
||||
useState(hasAnyAdvancedValue);
|
||||
const [advancedExpanded, setAdvancedExpanded] = useState(hasAnyAdvancedValue);
|
||||
|
||||
// 预设填充高级值后自动展开(仅从折叠→展开,不会自动折叠)
|
||||
useEffect(() => {
|
||||
@@ -379,6 +384,9 @@ export function ClaudeFormFields({
|
||||
: t("providerForm.apiHint")
|
||||
}
|
||||
onManageClick={() => onEndpointModalToggle(true)}
|
||||
showFullUrlToggle={true}
|
||||
isFullUrl={isFullUrl}
|
||||
onFullUrlChange={onFullUrlChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -400,10 +408,7 @@ export function ClaudeFormFields({
|
||||
|
||||
{/* 高级选项(API 格式 + 认证字段 + 模型映射) */}
|
||||
{shouldShowModelSelector && (
|
||||
<Collapsible
|
||||
open={advancedExpanded}
|
||||
onOpenChange={setAdvancedExpanded}
|
||||
>
|
||||
<Collapsible open={advancedExpanded} onOpenChange={setAdvancedExpanded}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import JsonEditor from "@/components/JsonEditor";
|
||||
import {
|
||||
@@ -175,10 +181,7 @@ export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
|
||||
}
|
||||
} else {
|
||||
toml = removeCodexTopLevelField(toml, "model_context_window");
|
||||
toml = removeCodexTopLevelField(
|
||||
toml,
|
||||
"model_auto_compact_token_limit",
|
||||
);
|
||||
toml = removeCodexTopLevelField(toml, "model_auto_compact_token_limit");
|
||||
}
|
||||
handleLocalChange(toml);
|
||||
},
|
||||
|
||||
@@ -22,6 +22,8 @@ interface CodexFormFieldsProps {
|
||||
shouldShowSpeedTest: boolean;
|
||||
codexBaseUrl: string;
|
||||
onBaseUrlChange: (url: string) => void;
|
||||
isFullUrl: boolean;
|
||||
onFullUrlChange: (value: boolean) => void;
|
||||
isEndpointModalOpen: boolean;
|
||||
onEndpointModalToggle: (open: boolean) => void;
|
||||
onCustomEndpointsChange?: (endpoints: string[]) => void;
|
||||
@@ -49,6 +51,8 @@ export function CodexFormFields({
|
||||
shouldShowSpeedTest,
|
||||
codexBaseUrl,
|
||||
onBaseUrlChange,
|
||||
isFullUrl,
|
||||
onFullUrlChange,
|
||||
isEndpointModalOpen,
|
||||
onEndpointModalToggle,
|
||||
onCustomEndpointsChange,
|
||||
@@ -93,6 +97,9 @@ export function CodexFormFields({
|
||||
onChange={onBaseUrlChange}
|
||||
placeholder={t("providerForm.codexApiEndpointPlaceholder")}
|
||||
hint={t("providerForm.codexApiHint")}
|
||||
showFullUrlToggle
|
||||
isFullUrl={isFullUrl}
|
||||
onFullUrlChange={onFullUrlChange}
|
||||
onManageClick={() => onEndpointModalToggle(true)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -164,6 +164,11 @@ export function ProviderForm({
|
||||
const [endpointAutoSelect, setEndpointAutoSelect] = useState<boolean>(
|
||||
() => initialData?.meta?.endpointAutoSelect ?? true,
|
||||
);
|
||||
const supportsFullUrl = appId === "claude" || appId === "codex";
|
||||
const [localIsFullUrl, setLocalIsFullUrl] = useState<boolean>(() => {
|
||||
if (!supportsFullUrl) return false;
|
||||
return initialData?.meta?.isFullUrl ?? false;
|
||||
});
|
||||
|
||||
const [testConfig, setTestConfig] = useState<ProviderTestConfig>(
|
||||
() => initialData?.meta?.testConfig ?? { enabled: false },
|
||||
@@ -203,6 +208,9 @@ export function ProviderForm({
|
||||
setDraftCustomEndpoints([]);
|
||||
}
|
||||
setEndpointAutoSelect(initialData?.meta?.endpointAutoSelect ?? true);
|
||||
setLocalIsFullUrl(
|
||||
supportsFullUrl ? (initialData?.meta?.isFullUrl ?? false) : false,
|
||||
);
|
||||
setTestConfig(initialData?.meta?.testConfig ?? { enabled: false });
|
||||
setProxyConfig(initialData?.meta?.proxyConfig ?? { enabled: false });
|
||||
setPricingConfig({
|
||||
@@ -214,7 +222,7 @@ export function ProviderForm({
|
||||
initialData?.meta?.pricingModelSource,
|
||||
),
|
||||
});
|
||||
}, [appId, initialData]);
|
||||
}, [appId, initialData, supportsFullUrl]);
|
||||
|
||||
const defaultValues: ProviderFormData = useMemo(
|
||||
() => ({
|
||||
@@ -1040,6 +1048,10 @@ export function ProviderForm({
|
||||
localApiKeyField !== "ANTHROPIC_AUTH_TOKEN"
|
||||
? localApiKeyField
|
||||
: undefined,
|
||||
isFullUrl:
|
||||
supportsFullUrl && category !== "official" && localIsFullUrl
|
||||
? true
|
||||
: undefined,
|
||||
};
|
||||
|
||||
await onSubmit(payload);
|
||||
@@ -1279,6 +1291,7 @@ export function ProviderForm({
|
||||
}
|
||||
|
||||
setLocalApiKeyField(preset.apiKeyField ?? "ANTHROPIC_AUTH_TOKEN");
|
||||
setLocalIsFullUrl(false);
|
||||
|
||||
form.reset({
|
||||
name: preset.nameKey ? t(preset.nameKey) : preset.name,
|
||||
@@ -1511,6 +1524,8 @@ export function ProviderForm({
|
||||
onApiFormatChange={handleApiFormatChange}
|
||||
apiKeyField={localApiKeyField}
|
||||
onApiKeyFieldChange={handleApiKeyFieldChange}
|
||||
isFullUrl={localIsFullUrl}
|
||||
onFullUrlChange={setLocalIsFullUrl}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1527,6 +1542,8 @@ export function ProviderForm({
|
||||
shouldShowSpeedTest={shouldShowSpeedTest}
|
||||
codexBaseUrl={codexBaseUrl}
|
||||
onBaseUrlChange={handleCodexBaseUrlChange}
|
||||
isFullUrl={localIsFullUrl}
|
||||
onFullUrlChange={setLocalIsFullUrl}
|
||||
isEndpointModalOpen={isCodexEndpointModalOpen}
|
||||
onEndpointModalToggle={setIsCodexEndpointModalOpen}
|
||||
onCustomEndpointsChange={
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormLabel } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Zap } from "lucide-react";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Link2, Zap } from "lucide-react";
|
||||
|
||||
interface EndpointFieldProps {
|
||||
id: string;
|
||||
@@ -13,6 +14,9 @@ interface EndpointFieldProps {
|
||||
showManageButton?: boolean;
|
||||
onManageClick?: () => void;
|
||||
manageButtonLabel?: string;
|
||||
showFullUrlToggle?: boolean;
|
||||
isFullUrl?: boolean;
|
||||
onFullUrlChange?: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export function EndpointField({
|
||||
@@ -25,18 +29,56 @@ export function EndpointField({
|
||||
showManageButton = true,
|
||||
onManageClick,
|
||||
manageButtonLabel,
|
||||
showFullUrlToggle = false,
|
||||
isFullUrl = false,
|
||||
onFullUrlChange,
|
||||
}: EndpointFieldProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const defaultManageLabel = t("providerForm.manageAndTest", {
|
||||
defaultValue: "管理和测速",
|
||||
});
|
||||
const effectiveHint =
|
||||
showFullUrlToggle && isFullUrl
|
||||
? t("providerForm.fullUrlHint", {
|
||||
defaultValue:
|
||||
"💡 请填写完整请求 URL,并且必须开启代理后使用;代理将直接使用此 URL,不拼接路径",
|
||||
})
|
||||
: hint;
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<FormLabel htmlFor={id}>{label}</FormLabel>
|
||||
{showManageButton && onManageClick && (
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<FormLabel htmlFor={id}>{label}</FormLabel>
|
||||
{showFullUrlToggle && onFullUrlChange ? (
|
||||
<div className="flex items-center gap-2 rounded-full border border-border/70 bg-muted/30 px-2.5 py-1">
|
||||
<Link2
|
||||
className={`h-3.5 w-3.5 ${
|
||||
isFullUrl ? "text-primary" : "text-muted-foreground"
|
||||
}`}
|
||||
/>
|
||||
<span
|
||||
className={`text-xs font-medium ${
|
||||
isFullUrl ? "text-foreground" : "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
{t("providerForm.fullUrlLabel", {
|
||||
defaultValue: "完整 URL",
|
||||
})}
|
||||
</span>
|
||||
<Switch
|
||||
checked={isFullUrl}
|
||||
onCheckedChange={onFullUrlChange}
|
||||
aria-label={t("providerForm.fullUrlLabel", {
|
||||
defaultValue: "完整 URL",
|
||||
})}
|
||||
className="h-5 w-9"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{showManageButton && onManageClick ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onManageClick}
|
||||
@@ -45,7 +87,7 @@ export function EndpointField({
|
||||
<Zap className="h-3.5 w-3.5" />
|
||||
{manageButtonLabel || defaultManageLabel}
|
||||
</button>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
<Input
|
||||
id={id}
|
||||
@@ -55,9 +97,11 @@ export function EndpointField({
|
||||
placeholder={placeholder}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{hint ? (
|
||||
{effectiveHint ? (
|
||||
<div className="p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700 rounded-lg">
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">{hint}</p>
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">
|
||||
{effectiveHint}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -11,5 +11,4 @@ export const TEMPLATE_TYPES = {
|
||||
GITHUB_COPILOT: "github_copilot",
|
||||
} as const;
|
||||
|
||||
export type TemplateType =
|
||||
(typeof TEMPLATE_TYPES)[keyof typeof TEMPLATE_TYPES];
|
||||
export type TemplateType = (typeof TEMPLATE_TYPES)[keyof typeof TEMPLATE_TYPES];
|
||||
|
||||
@@ -453,10 +453,10 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
||||
websiteUrl: "https://www.kimi.com/coding/docs/",
|
||||
apiKeyUrl: "https://platform.moonshot.cn/console/api-keys",
|
||||
settingsConfig: {
|
||||
npm: "@ai-sdk/openai-compatible",
|
||||
npm: "@ai-sdk/anthropic",
|
||||
name: "Kimi For Coding",
|
||||
options: {
|
||||
baseURL: "https://api.kimi.com/v1",
|
||||
baseURL: "https://api.kimi.com/coding/v1",
|
||||
apiKey: "",
|
||||
setCacheKey: true,
|
||||
},
|
||||
@@ -470,8 +470,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
||||
templateValues: {
|
||||
baseURL: {
|
||||
label: "Base URL",
|
||||
placeholder: "https://api.kimi.com/v1",
|
||||
defaultValue: "https://api.kimi.com/v1",
|
||||
placeholder: "https://api.kimi.com/coding/v1",
|
||||
defaultValue: "https://api.kimi.com/coding/v1",
|
||||
editorValue: "",
|
||||
},
|
||||
apiKey: {
|
||||
|
||||
@@ -23,7 +23,7 @@ import { openclawKeys } from "@/hooks/useOpenClaw";
|
||||
* Hook for managing provider actions (add, update, delete, switch)
|
||||
* Extracts business logic from App.tsx
|
||||
*/
|
||||
export function useProviderActions(activeApp: AppId) {
|
||||
export function useProviderActions(activeApp: AppId, isProxyRunning?: boolean) {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -140,6 +140,52 @@ export function useProviderActions(activeApp: AppId) {
|
||||
// 切换供应商
|
||||
const switchProvider = useCallback(
|
||||
async (provider: Provider) => {
|
||||
const isCopilotProvider =
|
||||
activeApp === "claude" &&
|
||||
provider.meta?.providerType === "github_copilot";
|
||||
|
||||
// Determine why this provider requires the proxy
|
||||
let proxyRequiredReason: string | null = null;
|
||||
if (!isProxyRunning && provider.category !== "official") {
|
||||
if (isCopilotProvider) {
|
||||
proxyRequiredReason = t("notifications.proxyReasonCopilot", {
|
||||
defaultValue: "使用 GitHub Copilot 作为 Claude 供应商",
|
||||
});
|
||||
} else if (
|
||||
provider.meta?.apiFormat === "openai_chat" &&
|
||||
activeApp === "claude"
|
||||
) {
|
||||
proxyRequiredReason = t("notifications.proxyReasonOpenAIChat", {
|
||||
defaultValue: "使用 OpenAI Chat 接口格式",
|
||||
});
|
||||
} else if (
|
||||
provider.meta?.apiFormat === "openai_responses" &&
|
||||
activeApp === "claude"
|
||||
) {
|
||||
proxyRequiredReason = t("notifications.proxyReasonOpenAIResponses", {
|
||||
defaultValue: "使用 OpenAI Responses 接口格式",
|
||||
});
|
||||
} else if (
|
||||
provider.meta?.isFullUrl &&
|
||||
(activeApp === "claude" || activeApp === "codex")
|
||||
) {
|
||||
proxyRequiredReason = t("notifications.proxyReasonFullUrl", {
|
||||
defaultValue: "开启了完整 URL 连接模式",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (proxyRequiredReason) {
|
||||
toast.warning(
|
||||
t("notifications.proxyRequiredForSwitch", {
|
||||
reason: proxyRequiredReason,
|
||||
defaultValue:
|
||||
"此供应商{{reason}},需要代理服务才能正常使用,请先启动代理",
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await switchProviderMutation.mutateAsync(provider.id);
|
||||
await syncClaudePlugin(provider);
|
||||
@@ -159,15 +205,15 @@ export function useProviderActions(activeApp: AppId) {
|
||||
if (
|
||||
activeApp === "claude" &&
|
||||
provider.category !== "official" &&
|
||||
(provider.meta?.apiFormat === "openai_chat" ||
|
||||
(isCopilotProvider ||
|
||||
provider.meta?.apiFormat === "openai_chat" ||
|
||||
provider.meta?.apiFormat === "openai_responses")
|
||||
) {
|
||||
// OpenAI format provider: show proxy hint
|
||||
toast.info(
|
||||
t("notifications.openAIFormatHint", {
|
||||
defaultValue:
|
||||
"此供应商使用 OpenAI 兼容格式,需要开启代理服务才能正常使用",
|
||||
}),
|
||||
isCopilotProvider
|
||||
? t("notifications.copilotProxyHint")
|
||||
: t("notifications.openAIFormatHint"),
|
||||
{
|
||||
duration: 5000,
|
||||
closeButton: true,
|
||||
@@ -193,7 +239,7 @@ export function useProviderActions(activeApp: AppId) {
|
||||
// 错误提示由 mutation 处理
|
||||
}
|
||||
},
|
||||
[switchProviderMutation, syncClaudePlugin, activeApp, t],
|
||||
[switchProviderMutation, syncClaudePlugin, activeApp, isProxyRunning, t],
|
||||
);
|
||||
|
||||
// 删除供应商
|
||||
|
||||
+10
-8
@@ -1,4 +1,9 @@
|
||||
import { useMutation, useQuery, useQueryClient, keepPreviousData } from "@tanstack/react-query";
|
||||
import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
keepPreviousData,
|
||||
} from "@tanstack/react-query";
|
||||
import {
|
||||
skillsApi,
|
||||
type SkillBackupEntry,
|
||||
@@ -108,13 +113,10 @@ export function useInstallSkill() {
|
||||
export function useUninstallSkill() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
skillKey,
|
||||
}: {
|
||||
id: string;
|
||||
skillKey: string;
|
||||
}) => skillsApi.uninstallUnified(id).then((result) => ({ ...result, skillKey })),
|
||||
mutationFn: ({ id, skillKey }: { id: string; skillKey: string }) =>
|
||||
skillsApi
|
||||
.uninstallUnified(id)
|
||||
.then((result) => ({ ...result, skillKey })),
|
||||
onSuccess: ({ skillKey }, _vars) => {
|
||||
// 直接更新 installed 缓存,移除该 skill
|
||||
queryClient.setQueryData<InstalledSkill[]>(
|
||||
|
||||
@@ -174,8 +174,13 @@
|
||||
"deleteFailed": "Failed to delete provider: {{error}}",
|
||||
"settingsSaved": "Settings saved",
|
||||
"settingsSaveFailed": "Failed to save settings: {{error}}",
|
||||
"openAIChatFormatHint": "This provider uses OpenAI Chat format and requires the proxy service to be enabled",
|
||||
"proxyRequiredForSwitch": "This provider {{reason}}, requires the proxy service to work properly. Start the proxy first.",
|
||||
"proxyReasonCopilot": "uses GitHub Copilot as a Claude provider",
|
||||
"proxyReasonOpenAIChat": "uses OpenAI Chat API format",
|
||||
"proxyReasonOpenAIResponses": "uses OpenAI Responses API format",
|
||||
"proxyReasonFullUrl": "has full URL connection mode enabled",
|
||||
"openAIFormatHint": "This provider uses OpenAI-compatible format and requires the proxy service to be enabled",
|
||||
"copilotProxyHint": "GitHub Copilot as a Claude provider always requires the local proxy; the proxy automatically selects Chat Completions or Responses based on the current model.",
|
||||
"openLinkFailed": "Failed to open link",
|
||||
"openclawModelsRegistered": "Models have been registered to /model list",
|
||||
"openclawDefaultModelSet": "Set as default model",
|
||||
@@ -743,6 +748,10 @@
|
||||
"anthropicReasoningModel": "Reasoning Model (Thinking)",
|
||||
"apiFormat": "API Format",
|
||||
"apiFormatHint": "Select the input format for the provider's API",
|
||||
"fullUrlLabel": "Full URL",
|
||||
"fullUrlEnabled": "Full URL Mode",
|
||||
"fullUrlDisabled": "Mark as Full URL",
|
||||
"fullUrlHint": "💡 Enter the full request URL. This mode requires the proxy to be enabled, and the proxy will use the URL as-is without appending a path",
|
||||
"apiFormatAnthropic": "Anthropic Messages (Native)",
|
||||
"apiFormatOpenAIChat": "OpenAI Chat Completions (Requires proxy)",
|
||||
"apiFormatOpenAIResponses": "OpenAI Responses API (Requires proxy)",
|
||||
|
||||
@@ -174,8 +174,13 @@
|
||||
"deleteFailed": "プロバイダーの削除に失敗しました: {{error}}",
|
||||
"settingsSaved": "設定を保存しました",
|
||||
"settingsSaveFailed": "設定の保存に失敗しました: {{error}}",
|
||||
"openAIChatFormatHint": "このプロバイダーは OpenAI Chat フォーマットを使用しており、プロキシサービスの有効化が必要です",
|
||||
"proxyRequiredForSwitch": "このプロバイダーは{{reason}}、プロキシサービスが必要です。先にプロキシを起動してください",
|
||||
"proxyReasonCopilot": "GitHub Copilot を Claude プロバイダーとして使用しており",
|
||||
"proxyReasonOpenAIChat": "OpenAI Chat API フォーマットを使用しており",
|
||||
"proxyReasonOpenAIResponses": "OpenAI Responses API フォーマットを使用しており",
|
||||
"proxyReasonFullUrl": "完全 URL 接続モードが有効になっており",
|
||||
"openAIFormatHint": "このプロバイダーは OpenAI 互換フォーマットを使用しており、プロキシサービスの有効化が必要です",
|
||||
"copilotProxyHint": "GitHub Copilot を Claude プロバイダーとして使用する場合、ローカルプロキシが常に必要です。プロキシは現在のモデルに応じて Chat Completions または Responses を自動的に選択します。",
|
||||
"openLinkFailed": "リンクを開けませんでした",
|
||||
"openclawModelsRegistered": "モデルが /model リストに登録されました",
|
||||
"openclawDefaultModelSet": "デフォルトモデルに設定しました",
|
||||
@@ -743,6 +748,10 @@
|
||||
"anthropicReasoningModel": "推論モデル(Thinking)",
|
||||
"apiFormat": "API フォーマット",
|
||||
"apiFormatHint": "プロバイダー API の入力フォーマットを選択",
|
||||
"fullUrlLabel": "フル URL",
|
||||
"fullUrlEnabled": "フル URL モード",
|
||||
"fullUrlDisabled": "フル URL として設定",
|
||||
"fullUrlHint": "💡 完全なリクエスト URL を入力してください。このモードはプロキシを有効にして使用する必要があり、プロキシはこの URL をそのまま使用し、パスを追加しません",
|
||||
"apiFormatAnthropic": "Anthropic Messages(ネイティブ)",
|
||||
"apiFormatOpenAIChat": "OpenAI Chat Completions(プロキシが必要)",
|
||||
"apiFormatOpenAIResponses": "OpenAI Responses API(プロキシが必要)",
|
||||
|
||||
@@ -174,8 +174,13 @@
|
||||
"deleteFailed": "删除供应商失败:{{error}}",
|
||||
"settingsSaved": "设置已保存",
|
||||
"settingsSaveFailed": "保存设置失败:{{error}}",
|
||||
"openAIChatFormatHint": "此供应商使用 OpenAI Chat 格式,需要开启代理服务才能正常使用",
|
||||
"proxyRequiredForSwitch": "此供应商{{reason}},需要代理服务才能正常使用,请先启动代理",
|
||||
"proxyReasonCopilot": "使用 GitHub Copilot 作为 Claude 供应商",
|
||||
"proxyReasonOpenAIChat": "使用 OpenAI Chat 接口格式",
|
||||
"proxyReasonOpenAIResponses": "使用 OpenAI Responses 接口格式",
|
||||
"proxyReasonFullUrl": "开启了完整 URL 连接模式",
|
||||
"openAIFormatHint": "此供应商使用 OpenAI 兼容格式,需要开启代理服务才能正常使用",
|
||||
"copilotProxyHint": "GitHub Copilot 作为 Claude 供应商时始终需要本地代理;代理会根据当前模型自动选择 Chat Completions 或 Responses。",
|
||||
"openLinkFailed": "链接打开失败",
|
||||
"openclawModelsRegistered": "模型已注册到 /model 列表",
|
||||
"openclawDefaultModelSet": "已设为默认模型",
|
||||
@@ -743,6 +748,10 @@
|
||||
"anthropicReasoningModel": "推理模型 (Thinking)",
|
||||
"apiFormat": "API 格式",
|
||||
"apiFormatHint": "选择供应商 API 的输入格式",
|
||||
"fullUrlLabel": "完整 URL",
|
||||
"fullUrlEnabled": "完整 URL 模式",
|
||||
"fullUrlDisabled": "标记为完整 URL",
|
||||
"fullUrlHint": "💡 请填写完整请求 URL,并且必须开启代理后使用;代理将直接使用此 URL,不拼接路径",
|
||||
"apiFormatAnthropic": "Anthropic Messages (原生)",
|
||||
"apiFormatOpenAIChat": "OpenAI Chat Completions (需开启代理)",
|
||||
"apiFormatOpenAIResponses": "OpenAI Responses API (需开启代理)",
|
||||
|
||||
@@ -163,6 +163,8 @@ export interface ProviderMeta {
|
||||
authBinding?: AuthBinding;
|
||||
// Claude 认证字段名
|
||||
apiKeyField?: ClaudeApiKeyField;
|
||||
// 是否将 base_url 视为完整 API 端点(代理直接使用此 URL,不拼接路径)
|
||||
isFullUrl?: boolean;
|
||||
// Prompt cache key for OpenAI-compatible endpoints (improves cache hit rate)
|
||||
promptCacheKey?: string;
|
||||
// 供应商类型(用于识别 Copilot 等特殊供应商)
|
||||
|
||||
Reference in New Issue
Block a user