import { useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { Save, Plus, Globe } from "lucide-react"; import { FullScreenPanel } from "@/components/common/FullScreenPanel"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useUpdateModelPricing } from "@/lib/query/usage"; import { isNonNegativeDecimalString, type ModelPricing } from "@/types/usage"; import { ModelsDevPickerDialog } from "./ModelsDevPickerDialog"; interface PricingEditModalProps { open: boolean; model: ModelPricing; isNew?: boolean; onClose: () => void; } const PRICE_INPUT_STEP = "0.0001"; export function PricingEditModal({ open, model, isNew = false, onClose, }: PricingEditModalProps) { const { t } = useTranslation(); const updatePricing = useUpdateModelPricing(); const [isPickerOpen, setIsPickerOpen] = useState(false); const [formData, setFormData] = useState({ modelId: model.modelId, displayName: model.displayName, inputCost: model.inputCostPerMillion, outputCost: model.outputCostPerMillion, cacheReadCost: model.cacheReadCostPerMillion, cacheCreationCost: model.cacheCreationCostPerMillion, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); // 验证模型 ID if (isNew && !formData.modelId.trim()) { toast.error(t("usage.modelIdRequired", "模型 ID 不能为空")); return; } // 验证非负数 const values = [ formData.inputCost, formData.outputCost, formData.cacheReadCost, formData.cacheCreationCost, ]; for (const value of values) { if (!isNonNegativeDecimalString(value)) { toast.error(t("usage.invalidPrice", "价格必须为非负数")); return; } } try { await updatePricing.mutateAsync({ modelId: isNew ? formData.modelId : model.modelId, displayName: formData.displayName, inputCost: formData.inputCost, outputCost: formData.outputCost, cacheReadCost: formData.cacheReadCost, cacheCreationCost: formData.cacheCreationCost, }); toast.success( isNew ? t("usage.pricingAdded", "定价已添加") : t("usage.pricingUpdated", "定价已更新"), { closeButton: true }, ); onClose(); } catch (error) { toast.error(String(error)); } }; return ( {isNew ? ( ) : ( )} {updatePricing.isPending ? t("common.saving", "保存中...") : isNew ? t("common.add", "新增") : t("common.save", "保存")} } > {isNew && (

{t( "usage.modelsDevHint", "无需手动填写,可从 models.dev 选择模型定价", )}

)}
{isNew && (
setFormData({ ...formData, modelId: e.target.value }) } placeholder={t("usage.modelIdPlaceholder", { defaultValue: "例如: claude-3-5-sonnet-20241022", })} required />
)}
setFormData({ ...formData, displayName: e.target.value }) } placeholder={t("usage.displayNamePlaceholder", { defaultValue: "例如: Claude 3.5 Sonnet", })} required />
setFormData({ ...formData, inputCost: e.target.value }) } required />
setFormData({ ...formData, outputCost: e.target.value }) } required />
setFormData({ ...formData, cacheReadCost: e.target.value }) } required />
setFormData({ ...formData, cacheCreationCost: e.target.value }) } required />
{isNew && isPickerOpen && ( setIsPickerOpen(false)} onImported={() => { setIsPickerOpen(false); onClose(); }} /> )}
); }