import { useTranslation } from "react-i18next"; import { useState } from "react"; import { FormLabel } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { Plus, Trash2, ChevronDown, ChevronRight } from "lucide-react"; import { ApiKeySection } from "./shared"; import { openclawApiProtocols } from "@/config/openclawProviderPresets"; import type { ProviderCategory, OpenClawModel } from "@/types"; interface OpenClawFormFieldsProps { // Base URL baseUrl: string; onBaseUrlChange: (value: string) => void; // API Key apiKey: string; onApiKeyChange: (value: string) => void; category?: ProviderCategory; shouldShowApiKeyLink: boolean; websiteUrl: string; isPartner?: boolean; partnerPromotionKey?: string; // API Protocol api: string; onApiChange: (value: string) => void; // Models models: OpenClawModel[]; onModelsChange: (models: OpenClawModel[]) => void; } export function OpenClawFormFields({ baseUrl, onBaseUrlChange, apiKey, onApiKeyChange, category, shouldShowApiKeyLink, websiteUrl, isPartner, partnerPromotionKey, api, onApiChange, models, onModelsChange, }: OpenClawFormFieldsProps) { const { t } = useTranslation(); const [expandedModels, setExpandedModels] = useState>( {}, ); // Toggle advanced section for a model const toggleModelAdvanced = (index: number) => { setExpandedModels((prev) => ({ ...prev, [index]: !prev[index] })); }; // Add a new model entry const handleAddModel = () => { onModelsChange([ ...models, { id: "", name: "", contextWindow: undefined, maxTokens: undefined, cost: undefined, }, ]); }; // Remove a model entry const handleRemoveModel = (index: number) => { const newModels = [...models]; newModels.splice(index, 1); onModelsChange(newModels); // Clean up expanded state setExpandedModels((prev) => { const updated = { ...prev }; delete updated[index]; return updated; }); }; // Update model field const handleModelChange = ( index: number, field: keyof OpenClawModel, value: unknown, ) => { const newModels = [...models]; newModels[index] = { ...newModels[index], [field]: value }; onModelsChange(newModels); }; // Update model cost const handleCostChange = ( index: number, costField: "input" | "output" | "cacheRead" | "cacheWrite", value: string, ) => { const newModels = [...models]; const numValue = parseFloat(value); const currentCost = newModels[index].cost || { input: 0, output: 0 }; newModels[index] = { ...newModels[index], cost: { ...currentCost, [costField]: isNaN(numValue) ? undefined : numValue, }, }; onModelsChange(newModels); }; return ( <> {/* API Protocol Selector */}
{t("openclaw.apiProtocol", { defaultValue: "API 协议", })}

{t("openclaw.apiProtocolHint", { defaultValue: "选择与供应商 API 兼容的协议类型。大多数供应商使用 OpenAI Completions 格式。", })}

{/* Base URL */}
{t("openclaw.baseUrl", { defaultValue: "API 端点" })} onBaseUrlChange(e.target.value)} placeholder="https://api.example.com/v1" />

{t("openclaw.baseUrlHint", { defaultValue: "供应商的 API 端点地址。", })}

{/* API Key */} {/* Models Editor */}
{t("openclaw.models", { defaultValue: "模型列表" })}
{models.length === 0 ? (

{t("openclaw.noModels", { defaultValue: "暂无模型配置。点击添加模型来配置可用模型。", })}

) : (
{models.map((model, index) => (
{/* Model ID and Name row */}
handleModelChange(index, "id", e.target.value) } placeholder={t("openclaw.modelIdPlaceholder", { defaultValue: "claude-3-sonnet", })} />
handleModelChange(index, "name", e.target.value) } placeholder={t("openclaw.modelNamePlaceholder", { defaultValue: "Claude 3 Sonnet", })} />
{/* Context Window, Max Tokens and Reasoning row */}
handleModelChange( index, "contextWindow", e.target.value ? parseInt(e.target.value) : undefined, ) } placeholder="200000" />
handleModelChange( index, "maxTokens", e.target.value ? parseInt(e.target.value) : undefined, ) } placeholder="32000" />
handleModelChange(index, "reasoning", checked) } /> {model.reasoning ? t("openclaw.reasoningOn", { defaultValue: "启用" }) : t("openclaw.reasoningOff", { defaultValue: "关闭", })}
{/* Spacer for alignment with delete button */}
{/* Basic Cost row */}
handleCostChange(index, "input", e.target.value) } placeholder="3" />
handleCostChange(index, "output", e.target.value) } placeholder="15" />
{/* Spacer for alignment */}
{/* Advanced Options (Collapsible) */} toggleModelAdvanced(index)} >
handleCostChange(index, "cacheRead", e.target.value) } placeholder="0.3" />
handleCostChange( index, "cacheWrite", e.target.value, ) } placeholder="3.75" />
{/* Spacer for alignment */}

{t("openclaw.cacheCostHint", { defaultValue: "缓存价格用于计算 Prompt Caching 的成本。如不使用缓存可留空。", })}

))}
)}

{t("openclaw.modelsHint", { defaultValue: "配置该供应商支持的模型。模型 ID 用于 API 调用,显示名称用于界面展示。", })}

); }