From 7feae0a99d1bd7db62a0752a98f56361ba742c9e Mon Sep 17 00:00:00 2001 From: Sladuca Date: Mon, 27 Jan 2025 16:06:47 -0800 Subject: [PATCH 1/3] fix bad quote behavior --- src/helpers/units.ts | 4 ++-- src/lib/buy/index.tsx | 51 ++++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/helpers/units.ts b/src/helpers/units.ts index fe2646f..17507ec 100644 --- a/src/helpers/units.ts +++ b/src/helpers/units.ts @@ -22,9 +22,9 @@ export function roundStartDate(startDate: Date): Date { const startEpoch = dateToEpoch(startDate); if (startEpoch <= now + 1) { return epochToDate(now + 1); - } else { - return epochToDate(roundEpochUpToHour(startEpoch)); } + + return epochToDate(roundEpochUpToHour(startEpoch)); } export function computeApproximateDurationSeconds( diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index 5f471e0..a7eb772 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -61,11 +61,11 @@ export function registerBuy(program: Command) { export function parseStart(start?: string) { if (!start) { - return "NOW"; + return "NOW" as const; } if (start === "NOW" || start === "now") { - return "NOW"; + return "NOW" as const; } const parsed = parseDate(start); @@ -166,6 +166,7 @@ async function buyOrderAction(options: SfBuyOptions) { } } + function QuoteAndBuy(props: { options: SfBuyOptions }) { const [orderProps, setOrderProps] = useState(null); @@ -176,26 +177,27 @@ function QuoteAndBuy(props: { options: SfBuyOptions }) { let pricePerGpuHour: number | null = parsePricePerGpuHour( props.options.price ); + + let startAt = parseStart(props.options.start); + let duration = parseDuration(props.options.duration); + let endsAt = dayjs(startAt).add(duration, "seconds").toDate(); if (!pricePerGpuHour) { const quote = await getQuoteFromParsedSfBuyOptions(props.options); if (!quote) { - pricePerGpuHour = await getAggressivePricePerHour(props.options.type); - } else { - pricePerGpuHour = getPricePerGpuHourFromQuote(quote); + return logAndQuit("no quote found for the desired order."); } - } - const duration = parseDuration(props.options.duration); - const startDate = parseStartAsDate(props.options.start); - const endsAt = roundEndDate( - dayjs(startDate).add(duration, "seconds").toDate() - ).toDate(); + pricePerGpuHour = getPricePerGpuHourFromQuote(quote); + startAt = quote.start_at === "NOW" ? "NOW" as const : parseStartAsDate(quote.start_at); + endsAt = dayjs(quote.end_at); + duration = dayjs(endsAt).diff(dayjs(startAt), "seconds"); + } setOrderProps({ type: props.options.type, price: pricePerGpuHour, size: parseAccelerators(props.options.accelerators), - startAt: startDate, + startAt, endsAt, colocate: props.options.colocate, }); @@ -574,12 +576,27 @@ export function getPricePerGpuHourFromQuote(quote: NonNullable) { return quote.price / GPUS_PER_NODE / quote.quantity / durationHours; } +function parseAndRoundStart(start: string | undefined) { + const parsed = parseStart(start); + if (parsed === "NOW") { + return parsed; + } + + if (dayjs(parsed).isBefore(dayjs().add(1, "minute"))) { + return "NOW" as const; + } + + return roundStartDate(parsed); +} async function getQuoteFromParsedSfBuyOptions(options: SfBuyOptions) { + const startsAt = parseAndRoundStart(options.start); + const durationSeconds = parseDuration(options.duration); + const quantity = parseAccelerators(options.accelerators); return await getQuote({ instanceType: options.type, - quantity: parseAccelerators(options.accelerators), - startsAt: parseStart(options.start), - durationSeconds: parseDuration(options.duration), + quantity, + startsAt, + durationSeconds, }); } @@ -600,9 +617,9 @@ export async function getQuote(options: QuoteOptions) { quantity: options.quantity, duration: options.durationSeconds, min_start_date: - options.startsAt === "NOW" ? "NOW" : options.startsAt.toISOString(), + options.startsAt === "NOW" ? "NOW" as const: options.startsAt.toISOString(), max_start_date: - options.startsAt === "NOW" ? "NOW" : options.startsAt.toISOString(), + options.startsAt === "NOW" ? "NOW" as const : options.startsAt.toISOString(), }, }, // timeout after 600 seconds From e4458471ea896c1f37110ee1abbb525c253ebc46 Mon Sep 17 00:00:00 2001 From: Sladuca Date: Mon, 27 Jan 2025 16:30:09 -0800 Subject: [PATCH 2/3] fix type error --- src/lib/buy/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index a7eb772..e275f14 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -189,7 +189,7 @@ function QuoteAndBuy(props: { options: SfBuyOptions }) { pricePerGpuHour = getPricePerGpuHourFromQuote(quote); startAt = quote.start_at === "NOW" ? "NOW" as const : parseStartAsDate(quote.start_at); - endsAt = dayjs(quote.end_at); + endsAt = dayjs(quote.end_at).toDate(); duration = dayjs(endsAt).diff(dayjs(startAt), "seconds"); } @@ -327,7 +327,7 @@ function BuyOrder(props: BuyOrderProps) { ); async function submitOrder() { - const endsAt = roundEndDate(props.endsAt); + const endsAt = props.endsAt; const startAt = props.startAt === "NOW" ? parseStartAsDate(props.startAt) : props.startAt; const realDurationInHours = @@ -342,7 +342,7 @@ function BuyOrder(props: BuyOrderProps) { realDurationInHours ), startsAt: props.startAt, - endsAt: endsAt.toDate(), + endsAt, colocateWith: props.colocate || [], numberNodes: props.size, }); @@ -352,7 +352,7 @@ function BuyOrder(props: BuyOrderProps) { const [resultMessage, setResultMessage] = useState(null); const handleSubmit = useCallback( (submitValue: boolean) => { - const endsAt = roundEndDate(props.endsAt); + const endsAt = props.endsAt; const startAt = props.startAt === "NOW" ? parseStartAsDate(props.startAt) From 4b4840018f09b400c557e0dbf2ae347f2408c473 Mon Sep 17 00:00:00 2001 From: Sladuca Date: Mon, 27 Jan 2025 16:32:28 -0800 Subject: [PATCH 3/3] tweak verbage --- src/lib/buy/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index e275f14..eb97ba8 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -184,7 +184,7 @@ function QuoteAndBuy(props: { options: SfBuyOptions }) { if (!pricePerGpuHour) { const quote = await getQuoteFromParsedSfBuyOptions(props.options); if (!quote) { - return logAndQuit("no quote found for the desired order."); + return logAndQuit("No quote found for the desired order. Try with a different start date, duration, or price."); } pricePerGpuHour = getPricePerGpuHourFromQuote(quote);