diff --git a/studio/frontend/src/features/studio/sections/training-section.tsx b/studio/frontend/src/features/studio/sections/training-section.tsx index 2e1c48f444..8970da31a3 100644 --- a/studio/frontend/src/features/studio/sections/training-section.tsx +++ b/studio/frontend/src/features/studio/sections/training-section.tsx @@ -87,7 +87,18 @@ export function TrainingSection() { // vision_image_size choice in those windows. const includeVisionFields = store.isVisionModel && store.isDatasetImage !== false; - const yamlStr = serializeConfigToYaml(store, includeVisionFields); + // DeepSeek OCR ignores vision_image_size at training time (mappers.ts + // sends null), so do not emit it to YAML either; otherwise a stale + // value could later apply to a non-DeepSeek vision model. + const selectedModelLower = (store.selectedModel ?? "").toLowerCase(); + const isDeepseekOcr = + selectedModelLower.includes("deepseek") && + selectedModelLower.includes("ocr"); + const yamlStr = serializeConfigToYaml( + store, + includeVisionFields, + includeVisionFields && !isDeepseekOcr, + ); const blob = new Blob([yamlStr], { type: "text/yaml" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); diff --git a/studio/frontend/src/features/training/lib/yaml-config.ts b/studio/frontend/src/features/training/lib/yaml-config.ts index add0eb3520..8455908177 100644 --- a/studio/frontend/src/features/training/lib/yaml-config.ts +++ b/studio/frontend/src/features/training/lib/yaml-config.ts @@ -27,8 +27,28 @@ export function parseYamlConfig(text: string): BackendModelConfig { console.warn("Ignored unknown YAML keys:", unknownKeys.join(", ")); } + // YAML import means "use this config as authoritative". An absent + // vision_image_size should reset the in-memory value to Default, not + // preserve a stale one. Same-model defaults reloads (which also flow + // through the model-config mapper) skip the reset via Object.hasOwn + // in model-defaults.ts; here we forge the key so import always wins. + const trainingObj = + raw.training != null && + typeof raw.training === "object" && + !Array.isArray(raw.training) + ? { ...(raw.training as Record) } + : raw.training; + if ( + trainingObj != null && + typeof trainingObj === "object" && + !Array.isArray(trainingObj) && + !Object.hasOwn(trainingObj, "vision_image_size") + ) { + (trainingObj as Record).vision_image_size = null; + } + return { - training: (raw.training ?? undefined) as BackendModelConfig["training"], + training: trainingObj as BackendModelConfig["training"], lora: (raw.lora ?? undefined) as BackendModelConfig["lora"], logging: (raw.logging ?? undefined) as BackendModelConfig["logging"], }; @@ -41,6 +61,7 @@ export function parseYamlConfig(text: string): BackendModelConfig { export function serializeConfigToYaml( state: TrainingConfigState, includeVisionFields: boolean, + includeVisionImageSize: boolean = includeVisionFields, ): string { const lora: Record = { lora_r: state.loraRank, @@ -77,7 +98,7 @@ export function serializeConfigToYaml( lr_scheduler_type: state.lrSchedulerType, }; - if (includeVisionFields) { + if (includeVisionImageSize) { training.vision_image_size = state.visionImageSize; }