patchMerge deep-merges src into dst with the same shape as the previous mergo.WithOverride behaviour — scalars and slices replace; nested struct-maps (e.g. pii_detection, parameters) recurse so unknown sibling keys the editor doesn't model survive — EXCEPT that any path in mapLeaves is replaced whol
(dst, src map[string]any, mapLeaves map[string]struct{}, prefix string)
| 169 | // replaced wholesale, and removed when the patch sets it empty, so deletions |
| 170 | // inside a map field persist to disk. |
| 171 | func patchMerge(dst, src map[string]any, mapLeaves map[string]struct{}, prefix string) { |
| 172 | for k, sv := range src { |
| 173 | path := k |
| 174 | if prefix != "" { |
| 175 | path = prefix + "." + k |
| 176 | } |
| 177 | if _, isLeaf := mapLeaves[path]; isLeaf { |
| 178 | if m, ok := sv.(map[string]any); ok && len(m) == 0 { |
| 179 | delete(dst, k) // emptied map field -> drop it from the YAML |
| 180 | } else { |
| 181 | dst[k] = sv |
| 182 | } |
| 183 | continue |
| 184 | } |
| 185 | // Recurse into struct-like nesting so dst-only sibling keys survive. |
| 186 | if sm, ok := sv.(map[string]any); ok { |
| 187 | if dm, ok2 := dst[k].(map[string]any); ok2 { |
| 188 | patchMerge(dm, sm, mapLeaves, path) |
| 189 | continue |
| 190 | } |
| 191 | } |
| 192 | dst[k] = sv |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // EditYAML replaces the YAML for an installed model, with optional rename |
| 197 | // support. ml may be nil; when set, EditYAML calls ml.ShutdownModel(oldName) |