Compare commits

...
Author SHA1 Message Date
Tanish Manku a4ef7ffb05 Fix desktop video generation ignoring selected resolution
The desktop video promptbox built the GenerateVideoRequest without ever
setting `resolution`, so generation always omitted it and the backend
fell back to the model's default (720p) — and billed for it. The cost
preview reads the same store value and sends it, so the preview was
correct (e.g. 117 for 480p) while the charge stayed at the 720p price
(240). Populate request.resolution in buildRequest, mapping the store's
legacy display string ("480p"/"720p"/"1080p") to CommonResolution,
guarded on the model exposing resolutionOptions.

Also fix useImageCostEstimate (webapp) omitting `quality` from its
effect deps, which meant the image cost preview didn't refresh when only
the quality option changed.
2026-06-11 14:29:53 -07:00
2 changed files with 34 additions and 0 deletions
@@ -62,6 +62,7 @@ export function useImageCostEstimate(params: ImageCostParams): number | null {
params.model,
params.aspectRatio,
params.resolution,
params.quality,
params.numImages,
params.hasReferenceImages,
params.imageMediaTokenCount,
@@ -18,6 +18,7 @@ import { faCircleInfo } from "@fortawesome/pro-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { GalleryItem, GalleryModal } from "@storyteller/ui-gallery-modal";
import {
CommonResolution,
SizeIconOption,
SizeOption,
VideoModel,
@@ -58,6 +59,27 @@ type GROK_ASPECT_RATIO = "landscape" | "portrait" | "square";
const EMPTY_CHARACTERS: StoredCharacter[] = [];
// The video store keeps resolution as a legacy display string ("480p" / "720p"
// / "1080p") taken from the model's `resolutionOptions`. The generate request,
// however, needs the `CommonResolution` enum. Map here so the user's resolution
// choice is actually sent: omitting it makes the backend fall back to the
// model's default resolution (and bill for it), which is what made the charge
// disagree with the cost preview — the preview reads the same store value and
// converts it correctly. Snake_case forms are accepted too for robustness.
const RESOLUTION_STRING_TO_COMMON: Record<string, CommonResolution> = {
"480p": CommonResolution.FourEightyP,
"720p": CommonResolution.SevenTwentyP,
"1080p": CommonResolution.TenEightyP,
half_k: CommonResolution.HalfK,
one_k: CommonResolution.OneK,
two_k: CommonResolution.TwoK,
three_k: CommonResolution.ThreeK,
four_k: CommonResolution.FourK,
four_eighty_p: CommonResolution.FourEightyP,
seven_twenty_p: CommonResolution.SevenTwentyP,
ten_eighty_p: CommonResolution.TenEightyP,
};
const DEFAULT_RESOLUTIONS: SizeOption[] = [
{
tauriValue: "720p",
@@ -767,6 +789,17 @@ export const PromptBoxVideo = ({
request.duration_seconds = duration;
}
// Pass the chosen resolution when the model exposes a resolution picker.
// Guarded on `resolutionOptions` so a stale store value (left over from a
// model that did support resolution) isn't sent for one that doesn't.
if (selectedModel.resolutionOptions?.length) {
const mappedResolution =
RESOLUTION_STRING_TO_COMMON[resolution as string];
if (mappedResolution) {
request.resolution = mappedResolution;
}
}
switch (selectedModel?.tauriId) {
case "grok_video":
request.grok_aspect_ratio = getGrokAspectRatio();