Studio: tighten YAML import/save for vision_image_size
Two YAML-path asymmetries that could leak a stale image size into training: - parseYamlConfig now treats a missing training.vision_image_size as null. Without this, importing a YAML saved before this feature (or any config that omits the key) preserved whatever value the user had previously set on a different model. The model-defaults reload path still uses Object.hasOwn so same-model defaults reloads do not wipe a manual selection; only file import normalises the missing key. - handleSaveConfig now passes a DeepSeek-OCR-specific guard to serializeConfigToYaml so saved YAML matches what the API mapper actually sends. Previously a state with visionImageSize set could emit the key even though Studio ignored it at training time for DeepSeek OCR, and a later import for a non-DeepSeek vision model would activate the stale value. serializeConfigToYaml gains an optional third parameter includeVisionImageSize defaulting to includeVisionFields, preserving the existing 2-arg call signature for backwards compatibility.
This commit is contained in:
parent
eea71c9eec
commit
89ae87c11f
2 changed files with 35 additions and 3 deletions
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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<string, unknown>) }
|
||||
: raw.training;
|
||||
if (
|
||||
trainingObj != null &&
|
||||
typeof trainingObj === "object" &&
|
||||
!Array.isArray(trainingObj) &&
|
||||
!Object.hasOwn(trainingObj, "vision_image_size")
|
||||
) {
|
||||
(trainingObj as Record<string, unknown>).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<string, unknown> = {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue