package prediction import ( "fmt" "github.com/rose_cat707/luminary/internal/model" ) type ParamType string const ( ParamString ParamType = "string" ParamInt ParamType = "int" ParamFloat ParamType = "float" ) type ParamDef struct { Name string `json:"name"` Label string `json:"label"` Type ParamType `json:"type"` Required bool `json:"required"` Default any `json:"default,omitempty"` } var Txt2ImgParams = []ParamDef{ {Name: "prompt", Label: "正向提示词", Type: ParamString, Required: true}, {Name: "negative_prompt", Label: "负向提示词", Type: ParamString, Default: ""}, {Name: "seed", Label: "随机种子", Type: ParamInt, Default: -1}, {Name: "width", Label: "宽度", Type: ParamInt, Default: 1024}, {Name: "height", Label: "高度", Type: ParamInt, Default: 1024}, {Name: "steps", Label: "采样步数", Type: ParamInt, Default: 20}, {Name: "cfg_scale", Label: "CFG", Type: ParamFloat, Default: 7.0}, } var Img2ImgParams = []ParamDef{ {Name: "prompt", Label: "正向提示词", Type: ParamString, Required: true}, {Name: "negative_prompt", Label: "负向提示词", Type: ParamString, Default: ""}, {Name: "image", Label: "输入图 URL", Type: ParamString, Required: true}, {Name: "seed", Label: "随机种子", Type: ParamInt, Default: -1}, {Name: "steps", Label: "采样步数", Type: ParamInt, Default: 20}, {Name: "cfg_scale", Label: "CFG", Type: ParamFloat, Default: 7.0}, {Name: "denoise", Label: "重绘幅度", Type: ParamFloat, Default: 0.75}, } func ParamsForWorkflowType(wt model.WorkflowType) []ParamDef { switch wt { case model.WorkflowImg2Img: return Img2ImgParams default: return Txt2ImgParams } } func openAPIType(pt ParamType) string { switch pt { case ParamInt: return "integer" case ParamFloat: return "number" default: return "string" } } // OpenAPISchemaForWorkflow builds a Replicate-compatible schema with components.schemas.Input. func OpenAPISchemaForWorkflow(wt model.WorkflowType, title, description string, overrides map[string]any) map[string]any { defs := ParamsForWorkflowType(wt) properties := make(map[string]any, len(defs)) required := make([]string, 0, len(defs)) for i, d := range defs { prop := map[string]any{ "type": openAPIType(d.Type), "title": d.Label, "description": d.Label, "x-order": i, } if def, ok := effectiveDefault(d, overrides); ok { prop["default"] = def } properties[d.Name] = prop if d.Required { required = append(required, d.Name) } } inputSchema := map[string]any{ "type": "object", "properties": properties, } if len(required) > 0 { inputSchema["required"] = required } return map[string]any{ "openapi": "3.0.0", "info": map[string]any{ "title": title, "version": "1.0.0", "description": description, }, "components": map[string]any{ "schemas": map[string]any{ "Input": inputSchema, }, }, } } func RequiredBindings(wt model.WorkflowType) []string { switch wt { case model.WorkflowImg2Img: return []string{"prompt", "image"} default: return []string{"prompt"} } } func ValidateInput(wt model.WorkflowType, input map[string]any) (map[string]any, error) { return ValidateInputWithDefaults(wt, input, nil) } func coerceValue(d ParamDef, v any) (any, error) { switch d.Type { case ParamString: s, ok := v.(string) if !ok { return nil, fmt.Errorf("expected string") } if d.Required && s == "" { return nil, fmt.Errorf("cannot be empty") } return s, nil case ParamInt: switch n := v.(type) { case float64: return int(n), nil case int: return n, nil case int64: return int(n), nil default: return nil, fmt.Errorf("expected integer") } case ParamFloat: switch n := v.(type) { case float64: return n, nil case int: return float64(n), nil case int64: return float64(n), nil default: return nil, fmt.Errorf("expected number") } default: return v, nil } }