Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 39 additions & 11 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/helpers/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const apiPaths: Record<string, Path<IdParams | never>> = {
tokens_list: "/v0/tokens",
tokens_delete_by_id: ({ id }: IdParams): string => `/v0/tokens/${id}`,

zones_list: "/v0/zones",

vms_instances_list: "/v0/vms/instances",
vms_logs_list: "/v0/vms/logs",
vms_replace: "/v0/vms/replace",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { registerTokens } from "./lib/tokens.ts";
import { registerUpgrade } from "./lib/upgrade.ts";
import { registerVM } from "./lib/vm.ts";
import { registerScale } from "./lib/scale/index.tsx";
import { registerZones } from "./lib/zones.tsx";

const program = new Command();

Expand All @@ -50,6 +51,7 @@ await registerScale(program);
registerClusters(program);
registerMe(program);
registerVM(program);
await registerZones(program);
Comment thread
sigmachirality marked this conversation as resolved.

// (development commands)
registerDev(program);
Expand Down
38 changes: 31 additions & 7 deletions src/lib/buy/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ export function _registerBuy(program: Command) {
.command("buy")
.description("Place a buy order")
.showHelpAfterError()
.requiredOption("-t, --type <type>", "Type of GPU", "h100i")
.option(
"-t, --type <type>",
"Type of GPU",
)
.option(
"-n, --accelerators <quantity>",
"Number of GPUs to purchase",
Expand Down Expand Up @@ -97,7 +100,22 @@ export function _registerBuy(program: Command) {
"--standing",
"Places a standing order. Default behavior is to place an order that auto-cancels if it can't be filled immediately.",
)
.option("-c, --cluster <cluster>", "Send into a specific cluster")
.option(
"-z, --zone <zone>",
"Send into a specific zone. If provided, \`-t\`/`--type` will be ignored.",
)
Comment thread
sigmachirality marked this conversation as resolved.
.option(
"-c, --cluster <cluster>",
"Send into a specific cluster (deprecated, alias for --zone). If provided, \`-t\`/`--type` will be ignored.",
)
.hook("preAction", (command) => {
const { type, zone, cluster } = command.opts();
if (!type && !zone && !cluster) {
console.error(chalk.yellow("Must specify either --type or --zone"));
command.help();
process.exit(1);
}
})
.configureHelp({
optionDescription: (option) => {
if (option.flags === "-h, --help") {
Expand Down Expand Up @@ -135,10 +153,16 @@ Examples:
* 4. If --yes isn't provided, ask for confirmation
* 5. Place order
*/
if (options.quote) {
render(<QuoteComponent options={options} />);
// Normalize zone/cluster: prioritize zone over cluster for backward compatibility
const normalizedOptions = {
...options,
cluster: options.zone || options.cluster,
};
Comment thread
sigmachirality marked this conversation as resolved.

if (normalizedOptions.quote) {
render(<QuoteComponent options={normalizedOptions} />);
} else {
render(<QuoteAndBuy options={options} />);
render(<QuoteAndBuy options={normalizedOptions} />);
}
});
}
Expand Down Expand Up @@ -251,7 +275,7 @@ export function QuoteAndBuy(props: { options: SfBuyOptions }) {
props.options;

setOrderProps({
type,
type: type ?? "h100i", // We still need to pass something even if --zone is provided
price: pricePerGpuHour,
size: accelerators / GPUS_PER_NODE,
startAt,
Expand Down Expand Up @@ -739,7 +763,7 @@ async function getQuoteFromParsedSfBuyOptions(options: SfBuyOptions) {
);

return await getQuote({
instanceType: options.type,
instanceType: options.type ?? "h100i", // We still need to pass something even if --zone is provided
quantity,
minStartTime: startsAt,
maxStartTime: startsAt,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/posthog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const trackEvent = ({
}
};

type FeatureFlags = "procurements";
type FeatureFlags = "procurements" | "zones";

/**
* Checks if a feature is enabled for the current user.
Expand Down
28 changes: 18 additions & 10 deletions src/lib/scale/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,24 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) {
React.ReactNode
>();

const clusterName = props.zone || props.cluster;
Comment thread
sigmachirality marked this conversation as resolved.

const nodesRequired = useMemo(
() => acceleratorsToNodes(props.accelerators),
[props.accelerators],
);

const colocationStrategy = useMemo(() => {
if (props.cluster && props.colocationStrategy === "pinned") {
return { type: "pinned" as const, cluster_name: props.cluster };
if (clusterName && props.colocationStrategy === "pinned") {
return { type: "pinned" as const, cluster_name: clusterName };
}
return {
type: props.colocationStrategy as Exclude<
ColocationStrategyName,
"pinned"
>,
};
}, [props.cluster, props.colocationStrategy]);
}, [clusterName, props.colocationStrategy]);

const [isQuoting, setIsQuoting] = useState(false);
const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] =
Expand All @@ -128,7 +130,7 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) {
maxStartTime: "NOW",
minDurationSeconds: quoteMinutes * 60,
maxDurationSeconds: quoteMinutes * 60 + 3600,
cluster: props.cluster,
cluster: clusterName,
});
setIsQuoting(false);

Expand Down Expand Up @@ -158,7 +160,7 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) {
nodesRequired,
type: props.type,
pricePerGpuHourInCents: limitPricePerGpuHourInCents,
cluster: props.cluster,
cluster: clusterName,
colocationStrategy,
});
} else {
Expand Down Expand Up @@ -206,7 +208,7 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) {
nodesRequired,
type: props.type,
pricePerGpuHourInCents: displayedPricePerGpuHourInCents,
cluster: props.cluster,
cluster: clusterName,
colocationStrategy,
});
};
Expand Down Expand Up @@ -298,10 +300,16 @@ $ sf scale create -n 8 --horizon '30m'
parseAccelerators,
)
.option("-t, --type <type>", "Specify node type", "h100i")
.addOption(
new Option(
"-z, --zone <zone>",
"Only buy on the specified zone. If provided, \`-t\`/`--type` will be ignored.",
).implies({ colocationStrategy: "pinned" as const }),
Comment thread
sigmachirality marked this conversation as resolved.
)
.addOption(
new Option(
"-c, --cluster <cluster>",
"Only buy on the specified cluster. If provided, \`-t\`/`--type` will be ignored.",
"Only buy on the specified cluster (deprecated, alias for --zone). If provided, \`-t\`/`--type` will be ignored.",
).implies({ colocationStrategy: "pinned" as const }),
)
.addOption(
Expand Down Expand Up @@ -330,10 +338,10 @@ $ sf scale create -n 8 --horizon '30m'
)
.option("-y, --yes", "Automatically confirm the command.")
.hook("preAction", (command) => {
const { colocationStrategy, cluster } = command.opts();
if (colocationStrategy === "pinned" && !cluster) {
const { colocationStrategy, zone, cluster } = command.opts();
if (colocationStrategy === "pinned" && !(zone || cluster)) {
console.error(
"Invalid colocation strategy: `-c`/`--cluster` is required when using `pinned` colocation strategy.",
"Invalid colocation strategy: `-z`/`--zone` or `-c`/`--cluster` is required when using `pinned` colocation strategy.",
);
command.help();
process.exit(1);
Expand Down
Loading