package service import ( "encoding/json" "fmt" "path/filepath" "regexp" "strings" ) var slotTokenRE = regexp.MustCompile(`^\$\{slot:([A-Za-z0-9_.-]+)\.([A-Za-z0-9_.-]+)\}$`) func renderRuntimeConfig(templateRaw map[string]any, templatePath string, profileRaw map[string]any, profilePath string, overlays []runtimeOverlayInput, metadata map[string]any) (map[string]any, error) { tplName, err := runtimeTemplateName(templateRaw, templatePath) if err != nil { return nil, err } instances, err := runtimeProfileInstances(profileRaw, tplName) if err != nil { return nil, err } instances, err = mergeRuntimeTemplateParams(instances, runtimeTemplateParams(templateRaw)) if err != nil { return nil, err } renderedTemplates := map[string]any{} renderedInstances := make([]any, 0, len(instances)) for _, instance := range instances { boundName, boundTemplate, renderedInstance, err := renderRuntimeSceneInstance(templateRaw, templatePath, instance) if err != nil { return nil, err } renderedTemplates[boundName] = boundTemplate renderedInstances = append(renderedInstances, renderedInstance) } root := map[string]any{ "templates": renderedTemplates, "instances": renderedInstances, } for _, key := range []string{"global", "queue"} { if value, ok := profileRaw[key]; ok { root[key] = deepCopyAny(value) } } for _, overlay := range overlays { var err error root, err = applyRuntimeOverlay(root, overlay.Raw) if err != nil { return nil, err } } if metadata != nil { root["metadata"] = buildRuntimeMetadata(tplName, templatePath, profileRaw, profilePath, overlays, root, metadata) } return root, nil } type runtimeOverlayInput struct { Name string Path string Raw map[string]any } func runtimeTemplateName(templateRaw map[string]any, templatePath string) (string, error) { name := strings.TrimSpace(firstString(templateRaw["name"], strings.TrimSuffix(filepath.Base(templatePath), filepath.Ext(templatePath)))) if name == "" { return "", fmt.Errorf("%s: template name is empty", templatePath) } return name, nil } func runtimeTemplateBody(templateRaw map[string]any) (map[string]any, error) { body := templateRaw if nested, ok := templateRaw["template"].(map[string]any); ok { body = nested } nodes, hasNodes := body["nodes"].([]any) edges, hasEdges := body["edges"].([]any) if !hasNodes || !hasEdges { return nil, fmt.Errorf("template body must contain nodes[] and edges[]") } out := map[string]any{ "nodes": deepCopyAny(nodes), "edges": deepCopyAny(edges), } if executor, ok := body["executor"]; ok { out["executor"] = deepCopyAny(executor) } return out, nil } func runtimeTemplateParams(templateRaw map[string]any) map[string]any { params, _ := templateRaw["params"].(map[string]any) if params == nil { return map[string]any{} } return cloneMap(params) } func runtimeProfileInstances(profileRaw map[string]any, tplName string) ([]map[string]any, error) { if items, ok := profileRaw["instances"].([]any); ok { out := make([]map[string]any, 0, len(items)) for _, item := range items { instance, _ := item.(map[string]any) if instance == nil { return nil, fmt.Errorf("profile.instances entries must be objects") } cloned := deepCopyMap(instance) if strings.TrimSpace(stringValue(cloned["template"])) == "" { cloned["template"] = tplName } out = append(out, cloned) } return out, nil } name := strings.TrimSpace(stringValue(profileRaw["name"])) if name == "" { return nil, fmt.Errorf("profile must contain name or instances[]") } instance := map[string]any{ "name": name, "template": tplName, } if params, ok := profileRaw["params"].(map[string]any); ok && len(params) > 0 { instance["params"] = deepCopyMap(params) } if override, ok := profileRaw["override"].(map[string]any); ok && len(override) > 0 { instance["override"] = deepCopyMap(override) } return []map[string]any{instance}, nil } func mergeRuntimeTemplateParams(instances []map[string]any, sharedParams map[string]any) ([]map[string]any, error) { if len(sharedParams) == 0 { return instances, nil } out := make([]map[string]any, 0, len(instances)) for _, item := range instances { inst := deepCopyMap(item) params, _ := inst["params"].(map[string]any) if params == nil { params = map[string]any{} } inst["params"] = deepMergeMap(sharedParams, params) out = append(out, inst) } return out, nil } func renderRuntimeSceneInstance(templateRaw map[string]any, templatePath string, instance map[string]any) (string, map[string]any, map[string]any, error) { instanceName := strings.TrimSpace(stringValue(instance["name"])) if instanceName == "" { return "", nil, nil, fmt.Errorf("scene instance name is required") } tplName, err := runtimeTemplateName(templateRaw, templatePath) if err != nil { return "", nil, nil, err } boundName := tplName + "__" + instanceName context, err := buildRuntimeBindingContext(instance) if err != nil { return "", nil, nil, err } templateBody, err := runtimeTemplateBody(templateRaw) if err != nil { return "", nil, nil, err } slotRequirements, err := runtimeSlotRequirements(templateRaw) if err != nil { return "", nil, nil, err } renderedTemplateAny, err := expandRuntimeSlotTokens(templateBody, context, slotRequirements) if err != nil { return "", nil, nil, err } renderedTemplate, _ := renderedTemplateAny.(map[string]any) renderedInstance := map[string]any{ "name": instanceName, "template": boundName, } if sceneMeta, ok := instance["scene_meta"].(map[string]any); ok && len(sceneMeta) > 0 { renderedInstance["scene_meta"] = deepCopyMap(sceneMeta) } if params, ok := instance["params"].(map[string]any); ok && len(params) > 0 { renderedInstance["params"] = deepCopyMap(params) } return boundName, renderedTemplate, renderedInstance, nil } func runtimeSlotRequirements(templateRaw map[string]any) (map[string]bool, error) { group, err := parseTemplateSlots(templateRaw) if err != nil { return nil, err } out := map[string]bool{} for _, slot := range group.Inputs { out[slot.Name] = slot.Required } for _, slot := range group.Services { out[slot.Name] = slot.Required } for _, slot := range group.Outputs { out[slot.Name] = slot.Required } return out, nil } func buildRuntimeBindingContext(instance map[string]any) (map[string]any, error) { context := map[string]any{} if sceneMeta, ok := instance["scene_meta"].(map[string]any); ok && len(sceneMeta) > 0 { context["scene"] = deepCopyMap(sceneMeta) } for _, groupName := range []string{"input_bindings", "service_bindings", "output_bindings"} { group, _ := instance[groupName].(map[string]any) for slotName, raw := range group { entry, _ := raw.(map[string]any) if entry == nil { return nil, fmt.Errorf("binding entry must be an object") } context[slotName] = resolvedRuntimeBindingValue(entry) } } return context, nil } func resolvedRuntimeBindingValue(entry map[string]any) map[string]any { if resolved, ok := entry["resolved"].(map[string]any); ok && resolved != nil { return deepCopyMap(resolved) } return deepCopyMap(entry) } func expandRuntimeSlotTokens(value any, context map[string]any, slotRequirements map[string]bool) (any, error) { switch typed := value.(type) { case map[string]any: out := make(map[string]any, len(typed)) for key, item := range typed { expanded, err := expandRuntimeSlotTokens(item, context, slotRequirements) if err != nil { return nil, err } out[key] = expanded } return out, nil case []any: out := make([]any, 0, len(typed)) for _, item := range typed { expanded, err := expandRuntimeSlotTokens(item, context, slotRequirements) if err != nil { return nil, err } out = append(out, expanded) } return out, nil case string: match := slotTokenRE.FindStringSubmatch(strings.TrimSpace(typed)) if len(match) != 3 { return typed, nil } required, known := slotRequirements[match[1]] if !known { required = true } slotValues, _ := context[match[1]].(map[string]any) if slotValues == nil { if !required { return "", nil } return nil, fmt.Errorf("required slot '%s' is not bound", match[1]) } fieldValue, ok := slotValues[match[2]] if !ok { if !required { return "", nil } return nil, fmt.Errorf("required slot field '%s.%s' is not bound", match[1], match[2]) } return deepCopyAny(fieldValue), nil default: return deepCopyAny(value), nil } } func applyRuntimeOverlay(root map[string]any, overlay map[string]any) (map[string]any, error) { out := deepCopyMap(root) for _, key := range []string{"global", "queue", "templates"} { if value, ok := overlay[key]; ok { out[key] = deepMergeAny(out[key], value) } } if rawOverride, ok := overlay["override"]; ok { if override, _ := rawOverride.(map[string]any); override != nil { instances, _ := out["instances"].([]any) mergedInstances := make([]any, 0, len(instances)) for _, item := range instances { instance, _ := item.(map[string]any) merged := deepCopyMap(instance) merged = mergeRuntimeInstancePatch(merged, override) mergedInstances = append(mergedInstances, merged) } out["instances"] = mergedInstances } } if rawPatches, ok := overlay["instance_overrides"]; ok { patches, _ := rawPatches.(map[string]any) if patches == nil { return nil, fmt.Errorf("overlay.instance_overrides must be an object") } instances, _ := out["instances"].([]any) mergedInstances := make([]any, 0, len(instances)) for _, item := range instances { instance, _ := item.(map[string]any) merged := deepCopyMap(instance) if patch, ok := patches["*"].(map[string]any); ok { merged = mergeRuntimeInstancePatch(merged, patch) } name := stringValue(merged["name"]) if patch, ok := patches[name].(map[string]any); ok { merged = mergeRuntimeInstancePatch(merged, patch) } mergedInstances = append(mergedInstances, merged) } out["instances"] = mergedInstances } if rawInstances, ok := overlay["instances"]; ok { patchList, _ := rawInstances.([]any) if patchList == nil { return nil, fmt.Errorf("overlay.instances must be an array") } instances, _ := out["instances"].([]any) byName := map[string]int{} for i, item := range instances { instance, _ := item.(map[string]any) byName[stringValue(instance["name"])] = i } for _, item := range patchList { patch, _ := item.(map[string]any) name := stringValue(patch["name"]) if patch == nil || name == "" { return nil, fmt.Errorf("overlay.instances entries must be objects with name") } idx, ok := byName[name] if !ok { return nil, fmt.Errorf("overlay instance not found in profile: %s", name) } instance, _ := instances[idx].(map[string]any) instances[idx] = mergeRuntimeInstancePatch(instance, patch) } out["instances"] = instances } return out, nil } func mergeRuntimeInstancePatch(instance map[string]any, patch map[string]any) map[string]any { merged := deepCopyMap(instance) if params, ok := patch["params"].(map[string]any); ok { existing, _ := merged["params"].(map[string]any) merged["params"] = deepMergeMap(existing, params) } if override, ok := patch["override"].(map[string]any); ok { existing, _ := merged["override"].(map[string]any) merged["override"] = deepMergeMap(existing, override) } for key, value := range patch { if key == "name" || key == "template" || key == "params" || key == "override" { continue } merged[key] = deepMergeAny(merged[key], value) } return merged } func buildRuntimeMetadata(templateName string, templatePath string, profileRaw map[string]any, profilePath string, overlays []runtimeOverlayInput, root map[string]any, metadata map[string]any) map[string]any { instanceNames := make([]string, 0) instanceDisplayNames := make([]string, 0) instances, _ := root["instances"].([]any) for _, item := range instances { instance, _ := item.(map[string]any) if name := strings.TrimSpace(stringValue(instance["name"])); name != "" { instanceNames = append(instanceNames, name) } if sceneMeta, ok := instance["scene_meta"].(map[string]any); ok { if displayName := strings.TrimSpace(stringValue(sceneMeta["display_name"])); displayName != "" { instanceDisplayNames = append(instanceDisplayNames, displayName) } } } overlayNames := make([]any, 0, len(overlays)) overlayPaths := make([]any, 0, len(overlays)) for _, overlay := range overlays { overlayNames = append(overlayNames, overlay.Name) overlayPaths = append(overlayPaths, overlay.Path) } out := map[string]any{ "template": templateName, "template_path": templatePath, "profile": strings.TrimSpace(firstString(profileRaw["name"], strings.TrimSuffix(filepath.Base(profilePath), filepath.Ext(profilePath)))), "business_name": strings.TrimSpace(stringValue(profileRaw["business_name"])), "profile_path": profilePath, "instance_names": instanceNames, "instance_display_names": instanceDisplayNames, "overlays": overlayNames, "overlay_paths": overlayPaths, } for key, value := range metadata { out[key] = deepCopyAny(value) } return out } func deepMergeMap(base map[string]any, override map[string]any) map[string]any { if base == nil { base = map[string]any{} } return deepMergeAny(base, override).(map[string]any) } func deepMergeAny(base any, override any) any { baseMap, baseIsMap := base.(map[string]any) overrideMap, overrideIsMap := override.(map[string]any) if baseIsMap && overrideIsMap { merged := deepCopyMap(baseMap) for key, value := range overrideMap { if existing, ok := merged[key]; ok { merged[key] = deepMergeAny(existing, value) } else { merged[key] = deepCopyAny(value) } } return merged } return deepCopyAny(override) } func deepCopyMap(in map[string]any) map[string]any { if in == nil { return map[string]any{} } out, _ := deepCopyAny(in).(map[string]any) return out } func deepCopyAny(value any) any { if value == nil { return nil } body, err := json.Marshal(value) if err != nil { return value } var out any if err := json.Unmarshal(body, &out); err != nil { return value } return out }