mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-06-16 13:34:04 +08:00
fix(usage): reduce price input step to 0.0001 for sub-cent precision (#2793)
The step was 0.01, preventing input of prices like DeepSeek's cache read cost ($0.0028/million tokens). Extract step value to a constant and apply to all four price fields. Closes #2503
This commit is contained in:
committed by
GitHub
Unverified
parent
5ae9c2605d
commit
6e8ee094f1
@@ -16,6 +16,8 @@ interface PricingEditModalProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const PRICE_INPUT_STEP = "0.0001";
|
||||
|
||||
export function PricingEditModal({
|
||||
open,
|
||||
model,
|
||||
@@ -151,7 +153,7 @@ export function PricingEditModal({
|
||||
<Input
|
||||
id="inputCost"
|
||||
type="number"
|
||||
step="0.01"
|
||||
step={PRICE_INPUT_STEP}
|
||||
min="0"
|
||||
value={formData.inputCost}
|
||||
onChange={(e) =>
|
||||
@@ -168,7 +170,7 @@ export function PricingEditModal({
|
||||
<Input
|
||||
id="outputCost"
|
||||
type="number"
|
||||
step="0.01"
|
||||
step={PRICE_INPUT_STEP}
|
||||
min="0"
|
||||
value={formData.outputCost}
|
||||
onChange={(e) =>
|
||||
@@ -188,7 +190,7 @@ export function PricingEditModal({
|
||||
<Input
|
||||
id="cacheReadCost"
|
||||
type="number"
|
||||
step="0.01"
|
||||
step={PRICE_INPUT_STEP}
|
||||
min="0"
|
||||
value={formData.cacheReadCost}
|
||||
onChange={(e) =>
|
||||
@@ -208,7 +210,7 @@ export function PricingEditModal({
|
||||
<Input
|
||||
id="cacheCreationCost"
|
||||
type="number"
|
||||
step="0.01"
|
||||
step={PRICE_INPUT_STEP}
|
||||
min="0"
|
||||
value={formData.cacheCreationCost}
|
||||
onChange={(e) =>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { PricingEditModal } from "@/components/usage/PricingEditModal";
|
||||
import type { ModelPricing } from "@/types/usage";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, options?: string | { defaultValue?: string }) =>
|
||||
typeof options === "string" ? options : options?.defaultValue ?? key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("sonner", () => ({
|
||||
toast: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/query/usage", () => ({
|
||||
useUpdateModelPricing: () => ({
|
||||
mutateAsync: vi.fn(),
|
||||
isPending: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/common/FullScreenPanel", () => ({
|
||||
FullScreenPanel: ({
|
||||
children,
|
||||
footer,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
footer?: React.ReactNode;
|
||||
}) => (
|
||||
<div>
|
||||
{children}
|
||||
{footer}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
const model: ModelPricing = {
|
||||
modelId: "deepseek-v4",
|
||||
displayName: "DeepSeek V4",
|
||||
inputCostPerMillion: "1",
|
||||
outputCostPerMillion: "3",
|
||||
cacheReadCostPerMillion: "0.0028",
|
||||
cacheCreationCostPerMillion: "0",
|
||||
};
|
||||
|
||||
const PRICE_FIELDS = [
|
||||
{ id: "inputCost", label: "输入成本" },
|
||||
{ id: "outputCost", label: "输出成本" },
|
||||
{ id: "cacheReadCost", label: "缓存读取成本" },
|
||||
{ id: "cacheCreationCost", label: "缓存写入成本" },
|
||||
] as const;
|
||||
|
||||
describe("PricingEditModal", () => {
|
||||
it("all price inputs have step=0.0001", () => {
|
||||
render(<PricingEditModal open model={model} onClose={() => {}} />);
|
||||
|
||||
for (const { id } of PRICE_FIELDS) {
|
||||
const input = screen.getByLabelText(/每百万 tokens/ as unknown as string, {
|
||||
selector: `#${id}`,
|
||||
}) as HTMLInputElement;
|
||||
expect(input).toHaveAttribute("step", "0.0001");
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts precise cache read cost like 0.0028", () => {
|
||||
render(<PricingEditModal open model={model} onClose={() => {}} />);
|
||||
|
||||
const cacheReadInput = document.getElementById(
|
||||
"cacheReadCost",
|
||||
) as HTMLInputElement;
|
||||
expect(cacheReadInput.value).toBe("0.0028");
|
||||
expect(cacheReadInput.checkValidity()).toBe(true);
|
||||
});
|
||||
|
||||
it("allows user to input sub-cent prices via change event", () => {
|
||||
render(
|
||||
<PricingEditModal open model={model} onClose={() => {}} isNew />,
|
||||
);
|
||||
|
||||
const cacheReadInput = document.getElementById(
|
||||
"cacheReadCost",
|
||||
) as HTMLInputElement;
|
||||
|
||||
fireEvent.change(cacheReadInput, { target: { value: "0.0015" } });
|
||||
expect(cacheReadInput.value).toBe("0.0015");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user