From 62bfd8fd52afd4aab7d347a16d08477307fd1133 Mon Sep 17 00:00:00 2001 From: kei5uke Date: Fri, 15 May 2026 10:38:22 +0200 Subject: [PATCH 1/2] feat: multiple gpu options --- .../components/price-estimator/LabCard.vue | 1 + .../price-estimator/MachineModal.vue | 28 +++++++++++++++++-- .../stores/priceEstimatorStore.ts | 24 ++++++++++++---- .../components/price-estimator/types/index.ts | 2 ++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/docs/.vitepress/theme/components/price-estimator/LabCard.vue b/docs/.vitepress/theme/components/price-estimator/LabCard.vue index b7d9ed602a..24e2c89c22 100644 --- a/docs/.vitepress/theme/components/price-estimator/LabCard.vue +++ b/docs/.vitepress/theme/components/price-estimator/LabCard.vue @@ -21,6 +21,7 @@ const computeHeaders = ref([ { title: "CPU cores", align: "start", sortable: true, key: "core_count" }, { title: "Memory [GB]", align: "start", sortable: true, key: "ram" }, { title: "GPU", align: "start", sortable: true, key: "gpu" }, + { title: "GPU count", align: "start", sortable: true, key: "gpuCount" }, { title: "Subscription", align: "start", sortable: true, key: "subscription" }, { title: "Price / month", align: "start", sortable: true, key: "monthlyPrice" }, { title: "Price / year", align: "start", sortable: true, key: "yearlyPrice" }, diff --git a/docs/.vitepress/theme/components/price-estimator/MachineModal.vue b/docs/.vitepress/theme/components/price-estimator/MachineModal.vue index 32241bf317..bcc6cd68de 100644 --- a/docs/.vitepress/theme/components/price-estimator/MachineModal.vue +++ b/docs/.vitepress/theme/components/price-estimator/MachineModal.vue @@ -20,6 +20,7 @@ const formData = ref({ name: undefined, machine_type: undefined, gpu: undefined, + gpuCount: 1, subscription: undefined, }) @@ -62,11 +63,15 @@ const getComputePriceMonth = computed((): string | number => { }) const getGpuPriceYear = computed((): string | number => { - if (!formData.value.gpu) { + const gpu = formData.value.gpu + const gpuCount = formData.value.gpuCount + + if (!gpu || !gpuCount) { return 0 } - const price = priceEstimatorStore.catalogue.gpuPrices.find((item: PriceListItem) => item["service.unit"] === formData.value.gpu && item["service.level"] === "ONDEMAND") - return price ? Number(price["price.nok.ex.vat"]).toFixed(2) : 0 + const price = priceEstimatorStore.catalogue.gpuPrices.find((item: PriceListItem) => item["service.unit"] === gpu && item["service.level"] === "ONDEMAND") + + return price ? (Number(price["price.nok.ex.vat"]) * gpuCount).toFixed(2) : 0 }) const getGpuPriceMonth = computed((): string | number => { @@ -90,6 +95,13 @@ const getGpus = computed(() => { }) }) +const gpuCount = computed({ + get: () => (formData.value.gpu ? formData.value.gpuCount : undefined), + set: (val) => { + if (formData.value.gpu) formData.value.gpuCount = val + }, +}) + const close = () => { emit("close") } @@ -110,6 +122,10 @@ const save = () => { const machineWithGpu = formData.value.machine_type const subscription = formData.value.subscription const gpu = formData.value.gpu + var gpuCount + if (gpu) { + gpuCount = formData.value.gpuCount + } if (props.editData) { priceEstimatorStore.editComputeInLab(props.labId, props.editData.id, { @@ -119,6 +135,7 @@ const save = () => { ram: ram, subscription: subscription!, gpu: gpu, + gpuCount: gpuCount, }) } else { // Add new compute @@ -129,6 +146,7 @@ const save = () => { ram: ram, subscription: subscription!, gpu: gpu, + gpuCount: gpuCount, }) } @@ -148,6 +166,7 @@ onMounted(() => { } else { formData.value.machine_type = props.editData.machine_type formData.value.gpu = props.editData.gpu + formData.value.gpuCount = props.editData.gpuCount } } else { formData.value.id = props.computeId @@ -197,6 +216,9 @@ onMounted(() => { + + + diff --git a/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts b/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts index 1d37b54d0c..7e132a3557 100644 --- a/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts +++ b/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts @@ -72,6 +72,7 @@ export const priceEstimatorStore = reactive({ ram: comp.ram, subscription: comp.subscription, gpu: comp.gpu, + gpuCount: comp.gpuCount, }) } } @@ -412,7 +413,7 @@ export const priceEstimatorStore = reactive({ /* Compute helpers */ - getComputePriceFromCatalogue(machineType: string, type: string, machineWithGpu?: string) { + getComputePriceFromCatalogue(machineType: string, type: string, machineWithGpu?: string, gpuCount: number = 1) { let totalYearlyPrice = 0 let totalMonthlyPrice = 0 let mainMachineTypePrice: number | undefined @@ -443,7 +444,7 @@ export const priceEstimatorStore = reactive({ if (machineWithGpu) { const gpuPrice = this.catalogue.gpuPrices.find((p) => p["service.unit"] === machineWithGpu && p["service.level"] === "ONDEMAND") if (gpuPrice) { - gpuYearly = gpuPrice["price.nok.ex.vat"] + gpuYearly = gpuPrice["price.nok.ex.vat"] * gpuCount totalYearlyPrice += gpuYearly totalMonthlyPrice = totalMonthlyPrice + Number(gpuYearly / 12) } @@ -454,18 +455,19 @@ export const priceEstimatorStore = reactive({ } }, - addComputeToLab(labId: number, payload: { name: string; machine_type: string; core_count: number; ram: number; subscription: string; gpu?: string }) { + addComputeToLab(labId: number, payload: { name: string; machine_type: string; core_count: number; ram: number; subscription: string; gpu?: string; gpuCount?: number }) { const lab = this.labs.find((l) => l.id === labId) if (!lab) return const compId = lab.selectedCompute?.length || 0 - const prices = this.getComputePriceFromCatalogue(payload.machine_type, payload.subscription, payload.gpu) + const prices = this.getComputePriceFromCatalogue(payload.machine_type, payload.subscription, payload.gpu, payload.gpuCount) const newCompute: ComputeUnit = { id: compId, name: payload.name, machine_type: payload.machine_type, core_count: payload.core_count, gpu: payload.gpu, + gpuCount: payload.gpuCount, ram: payload.ram, subscription: payload.subscription as SubscriptionType, monthlyPrice: prices.monthlyPrice, @@ -477,7 +479,11 @@ export const priceEstimatorStore = reactive({ this.saveStateToLocal() }, - editComputeInLab(labId: number, computeId: number, payload: { name: string; machine_type: string; core_count: number; ram: number; subscription: string; gpu?: string }) { + editComputeInLab( + labId: number, + computeId: number, + payload: { name: string; machine_type: string; core_count: number; ram: number; subscription: string; gpu?: string; gpuCount?: number }, + ) { const lab = this.labs.find((l) => l.id === labId) if (!lab || !lab.selectedCompute) return @@ -491,6 +497,7 @@ export const priceEstimatorStore = reactive({ machine_type: payload.machine_type, core_count: payload.core_count, gpu: payload.gpu, + gpuCount: payload.gpuCount, ram: payload.ram, subscription: payload.subscription as SubscriptionType, monthlyPrice: prices.monthlyPrice, @@ -645,6 +652,12 @@ export const priceEstimatorStore = reactive({ if (labData.compute) { for (const comp of labData.compute) { + var gpuCount + if (comp.gpu) { + gpuCount = Number(comp.gpu.split(".")[-1]) + console.log(gpuCount) + } + this.addComputeToLab(newLabId, { name: comp.name, machine_type: comp.machine_type, @@ -652,6 +665,7 @@ export const priceEstimatorStore = reactive({ ram: comp.ram, subscription: comp.subscription, gpu: comp.gpu, + gpuCount: gpuCount, }) } } diff --git a/docs/.vitepress/theme/components/price-estimator/types/index.ts b/docs/.vitepress/theme/components/price-estimator/types/index.ts index 4ead3f8960..ccd8793b66 100644 --- a/docs/.vitepress/theme/components/price-estimator/types/index.ts +++ b/docs/.vitepress/theme/components/price-estimator/types/index.ts @@ -28,6 +28,7 @@ export interface ComputeUnit { core_count: number ram: number gpu?: string + gpuCount?: number subscription: SubscriptionType monthlyPrice: number yearlyPrice: number @@ -87,6 +88,7 @@ export interface MachineFormData { name?: string machine_type?: string gpu?: string + gpuCount?: number subscription?: SubscriptionType } From ecd0c0ad4f04f7c936dfdb753be1b07a609a3591 Mon Sep 17 00:00:00 2001 From: kei5uke Date: Fri, 15 May 2026 16:48:29 +0200 Subject: [PATCH 2/2] feat: added max gpu count option, filter names --- .../components/price-estimator/LabCard.vue | 2 +- .../price-estimator/MachineModal.vue | 24 +++++++++++------ .../price-estimator/api/pricesApi.js | 1 - .../stores/priceEstimatorStore.ts | 27 ++++++++++--------- .../components/price-estimator/types/index.ts | 5 ++-- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/docs/.vitepress/theme/components/price-estimator/LabCard.vue b/docs/.vitepress/theme/components/price-estimator/LabCard.vue index 24e2c89c22..02b17f1aef 100644 --- a/docs/.vitepress/theme/components/price-estimator/LabCard.vue +++ b/docs/.vitepress/theme/components/price-estimator/LabCard.vue @@ -21,7 +21,7 @@ const computeHeaders = ref([ { title: "CPU cores", align: "start", sortable: true, key: "core_count" }, { title: "Memory [GB]", align: "start", sortable: true, key: "ram" }, { title: "GPU", align: "start", sortable: true, key: "gpu" }, - { title: "GPU count", align: "start", sortable: true, key: "gpuCount" }, + { title: "GPU count", align: "start", sortable: true, key: "gpu_count" }, { title: "Subscription", align: "start", sortable: true, key: "subscription" }, { title: "Price / month", align: "start", sortable: true, key: "monthlyPrice" }, { title: "Price / year", align: "start", sortable: true, key: "yearlyPrice" }, diff --git a/docs/.vitepress/theme/components/price-estimator/MachineModal.vue b/docs/.vitepress/theme/components/price-estimator/MachineModal.vue index bcc6cd68de..7539fb2a4c 100644 --- a/docs/.vitepress/theme/components/price-estimator/MachineModal.vue +++ b/docs/.vitepress/theme/components/price-estimator/MachineModal.vue @@ -20,7 +20,7 @@ const formData = ref({ name: undefined, machine_type: undefined, gpu: undefined, - gpuCount: 1, + gpu_count: 1, subscription: undefined, }) @@ -64,7 +64,7 @@ const getComputePriceMonth = computed((): string | number => { const getGpuPriceYear = computed((): string | number => { const gpu = formData.value.gpu - const gpuCount = formData.value.gpuCount + const gpuCount = formData.value.gpu_count if (!gpu || !gpuCount) { return 0 @@ -96,12 +96,20 @@ const getGpus = computed(() => { }) const gpuCount = computed({ - get: () => (formData.value.gpu ? formData.value.gpuCount : undefined), + get: () => (formData.value.gpu ? formData.value.gpu_count : undefined), set: (val) => { - if (formData.value.gpu) formData.value.gpuCount = val + if (formData.value.gpu) formData.value.gpu_count = val }, }) +const getMaxGpuCount = computed((): number => { + if (!formData.value.gpu) return 1 + + const selectedGpu = priceEstimatorStore.catalogue.availableGpus.find((item: GpuModel) => item["type"] === formData.value.gpu) + + return selectedGpu?.max ?? 1 +}) + const close = () => { emit("close") } @@ -124,7 +132,7 @@ const save = () => { const gpu = formData.value.gpu var gpuCount if (gpu) { - gpuCount = formData.value.gpuCount + gpuCount = formData.value.gpu_count } if (props.editData) { @@ -146,7 +154,7 @@ const save = () => { ram: ram, subscription: subscription!, gpu: gpu, - gpuCount: gpuCount, + gpu_count: gpuCount, }) } @@ -166,7 +174,7 @@ onMounted(() => { } else { formData.value.machine_type = props.editData.machine_type formData.value.gpu = props.editData.gpu - formData.value.gpuCount = props.editData.gpuCount + formData.value.gpu_count = props.editData.gpu_count } } else { formData.value.id = props.computeId @@ -217,7 +225,7 @@ onMounted(() => { - + diff --git a/docs/.vitepress/theme/components/price-estimator/api/pricesApi.js b/docs/.vitepress/theme/components/price-estimator/api/pricesApi.js index be3e776214..ae0c97a974 100644 --- a/docs/.vitepress/theme/components/price-estimator/api/pricesApi.js +++ b/docs/.vitepress/theme/components/price-estimator/api/pricesApi.js @@ -11,7 +11,6 @@ export default { async getPriceList() { try { const res = await axios.get(price_list_url) - // console.log("Fetched price list:", res.data) return res.data } catch (error) { console.error("Error fetching price list:", error) diff --git a/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts b/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts index 7e132a3557..1ee2405910 100644 --- a/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts +++ b/docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts @@ -72,7 +72,7 @@ export const priceEstimatorStore = reactive({ ram: comp.ram, subscription: comp.subscription, gpu: comp.gpu, - gpuCount: comp.gpuCount, + gpu_count: comp.gpu_count, }) } } @@ -110,11 +110,18 @@ export const priceEstimatorStore = reactive({ const priceListPromise = pricesApi.getPriceList().then((json: PriceListItem[]) => { this.catalogue.computePrices = json.filter((item) => item["service.group"] === "cpu").map(this.convertPricesToYearly) this.catalogue.storagePrices = json.filter((item) => item["service.family"] === "store") - this.catalogue.gpuPrices = json.filter((item) => item["service.group"] === "gpu").map(this.convertPricesToYearly) + this.catalogue.gpuPrices = json + .filter((item) => item["service.group"] === "gpu") + .map((item) => ({ ...item, "service.unit": item["service.unit"]?.replace("nvidia.", "") })) + .map(this.convertPricesToYearly) this.catalogue.labPrices = json.filter((item) => item["service.group"] === "lab") }) const gpusPromise = pricesApi.getAvailableGPUS().then((gpus: GpuModel[]) => { + gpus = gpus.map((item) => ({ + ...item, + type: item.type.replace("nvidia.", ""), + })) this.catalogue.availableGpus = gpus }) @@ -455,19 +462,19 @@ export const priceEstimatorStore = reactive({ } }, - addComputeToLab(labId: number, payload: { name: string; machine_type: string; core_count: number; ram: number; subscription: string; gpu?: string; gpuCount?: number }) { + addComputeToLab(labId: number, payload: { name: string; machine_type: string; core_count: number; ram: number; subscription: string; gpu?: string; gpu_count?: number }) { const lab = this.labs.find((l) => l.id === labId) if (!lab) return const compId = lab.selectedCompute?.length || 0 - const prices = this.getComputePriceFromCatalogue(payload.machine_type, payload.subscription, payload.gpu, payload.gpuCount) + const prices = this.getComputePriceFromCatalogue(payload.machine_type, payload.subscription, payload.gpu, payload.gpu_count) const newCompute: ComputeUnit = { id: compId, name: payload.name, machine_type: payload.machine_type, core_count: payload.core_count, gpu: payload.gpu, - gpuCount: payload.gpuCount, + gpu_count: payload.gpu_count, ram: payload.ram, subscription: payload.subscription as SubscriptionType, monthlyPrice: prices.monthlyPrice, @@ -497,7 +504,7 @@ export const priceEstimatorStore = reactive({ machine_type: payload.machine_type, core_count: payload.core_count, gpu: payload.gpu, - gpuCount: payload.gpuCount, + gpu_count: payload.gpuCount, ram: payload.ram, subscription: payload.subscription as SubscriptionType, monthlyPrice: prices.monthlyPrice, @@ -652,12 +659,6 @@ export const priceEstimatorStore = reactive({ if (labData.compute) { for (const comp of labData.compute) { - var gpuCount - if (comp.gpu) { - gpuCount = Number(comp.gpu.split(".")[-1]) - console.log(gpuCount) - } - this.addComputeToLab(newLabId, { name: comp.name, machine_type: comp.machine_type, @@ -665,7 +666,7 @@ export const priceEstimatorStore = reactive({ ram: comp.ram, subscription: comp.subscription, gpu: comp.gpu, - gpuCount: gpuCount, + gpu_count: comp.gpu_count, }) } } diff --git a/docs/.vitepress/theme/components/price-estimator/types/index.ts b/docs/.vitepress/theme/components/price-estimator/types/index.ts index ccd8793b66..3b97766937 100644 --- a/docs/.vitepress/theme/components/price-estimator/types/index.ts +++ b/docs/.vitepress/theme/components/price-estimator/types/index.ts @@ -28,7 +28,7 @@ export interface ComputeUnit { core_count: number ram: number gpu?: string - gpuCount?: number + gpu_count?: number subscription: SubscriptionType monthlyPrice: number yearlyPrice: number @@ -47,6 +47,7 @@ export interface StorageUnit { export interface GpuModel { type: string vram: number + max: number } export interface MachineType { @@ -88,7 +89,7 @@ export interface MachineFormData { name?: string machine_type?: string gpu?: string - gpuCount?: number + gpu_count?: number subscription?: SubscriptionType }