Spaces:
Running
Running
Amlan-109
feat: Initial commit of LocalAI Amlan Edition with premium branding and personalization
750bbe6 | package main | |
| import ( | |
| "context" | |
| "encoding/json" | |
| "fmt" | |
| "os" | |
| "strings" | |
| "github.com/ghodss/yaml" | |
| "github.com/mudler/LocalAI/core/gallery/importers" | |
| ) | |
| func formatTextContent(text string) string { | |
| return formatTextContentWithIndent(text, 4, 6) | |
| } | |
| // formatTextContentWithIndent formats text content with specified base and list item indentation | |
| func formatTextContentWithIndent(text string, baseIndent int, listItemIndent int) string { | |
| var formattedLines []string | |
| lines := strings.Split(text, "\n") | |
| for _, line := range lines { | |
| trimmed := strings.TrimRight(line, " \t\r") | |
| if trimmed == "" { | |
| // Keep empty lines as empty (no indentation) | |
| formattedLines = append(formattedLines, "") | |
| } else { | |
| // Preserve relative indentation from yaml.Marshal output | |
| // Count existing leading spaces to preserve relative structure | |
| leadingSpaces := len(trimmed) - len(strings.TrimLeft(trimmed, " \t")) | |
| trimmedStripped := strings.TrimLeft(trimmed, " \t") | |
| var totalIndent int | |
| if strings.HasPrefix(trimmedStripped, "-") { | |
| // List items: use listItemIndent (ignore existing leading spaces) | |
| totalIndent = listItemIndent | |
| } else { | |
| // Regular lines: use baseIndent + preserve relative indentation | |
| // This handles both top-level keys (leadingSpaces=0) and nested properties (leadingSpaces>0) | |
| totalIndent = baseIndent + leadingSpaces | |
| } | |
| indentStr := strings.Repeat(" ", totalIndent) | |
| formattedLines = append(formattedLines, indentStr+trimmedStripped) | |
| } | |
| } | |
| formattedText := strings.Join(formattedLines, "\n") | |
| // Remove any trailing spaces from the formatted description | |
| formattedText = strings.TrimRight(formattedText, " \t") | |
| return formattedText | |
| } | |
| // generateYAMLEntry generates a YAML entry for a model using the specified anchor | |
| func generateYAMLEntry(model ProcessedModel, quantization string) string { | |
| modelConfig, err := importers.DiscoverModelConfig("https://huggingface.co/"+model.ModelID, json.RawMessage(`{ "quantization": "`+quantization+`"}`)) | |
| if err != nil { | |
| panic(err) | |
| } | |
| // Extract model name from ModelID | |
| parts := strings.Split(model.ModelID, "/") | |
| modelName := model.ModelID | |
| if len(parts) > 0 { | |
| modelName = strings.ToLower(parts[len(parts)-1]) | |
| } | |
| // Remove common suffixes | |
| modelName = strings.ReplaceAll(modelName, "-gguf", "") | |
| modelName = strings.ReplaceAll(modelName, "-q4_k_m", "") | |
| modelName = strings.ReplaceAll(modelName, "-q4_k_s", "") | |
| modelName = strings.ReplaceAll(modelName, "-q3_k_m", "") | |
| modelName = strings.ReplaceAll(modelName, "-q2_k", "") | |
| description := model.ReadmeContent | |
| if description == "" { | |
| description = fmt.Sprintf("AI model: %s", modelName) | |
| } | |
| // Clean up description to prevent YAML linting issues | |
| description = cleanTextContent(description) | |
| formattedDescription := formatTextContent(description) | |
| configFile := formatTextContent(modelConfig.ConfigFile) | |
| filesYAML, _ := yaml.Marshal(modelConfig.Files) | |
| // Files section: list items need 4 spaces (not 6), since files: is at 2 spaces | |
| files := formatTextContentWithIndent(string(filesYAML), 4, 4) | |
| // Build metadata sections | |
| var metadataSections []string | |
| // Add license if present | |
| if model.License != "" { | |
| metadataSections = append(metadataSections, fmt.Sprintf(` license: "%s"`, model.License)) | |
| } | |
| // Add tags if present | |
| if len(model.Tags) > 0 { | |
| tagsYAML, _ := yaml.Marshal(model.Tags) | |
| tagsFormatted := formatTextContentWithIndent(string(tagsYAML), 4, 4) | |
| tagsFormatted = strings.TrimRight(tagsFormatted, "\n") | |
| metadataSections = append(metadataSections, fmt.Sprintf(" tags:\n%s", tagsFormatted)) | |
| } | |
| // Add icon if present | |
| if model.Icon != "" { | |
| metadataSections = append(metadataSections, fmt.Sprintf(` icon: %s`, model.Icon)) | |
| } | |
| // Build the metadata block | |
| metadataBlock := "" | |
| if len(metadataSections) > 0 { | |
| metadataBlock = strings.Join(metadataSections, "\n") + "\n" | |
| } | |
| yamlTemplate := "" | |
| yamlTemplate = `- name: "%s" | |
| url: "github:mudler/LocalAI/gallery/virtual.yaml@master" | |
| urls: | |
| - https://huggingface.co/%s | |
| description: | | |
| %s%s | |
| overrides: | |
| %s | |
| files: | |
| %s` | |
| // Trim trailing newlines from formatted sections to prevent extra blank lines | |
| formattedDescription = strings.TrimRight(formattedDescription, "\n") | |
| configFile = strings.TrimRight(configFile, "\n") | |
| files = strings.TrimRight(files, "\n") | |
| // Add newline before metadata block if present | |
| if metadataBlock != "" { | |
| metadataBlock = "\n" + strings.TrimRight(metadataBlock, "\n") | |
| } | |
| return fmt.Sprintf(yamlTemplate, | |
| modelName, | |
| model.ModelID, | |
| formattedDescription, | |
| metadataBlock, | |
| configFile, | |
| files, | |
| ) | |
| } | |
| // generateYAMLForModels generates YAML entries for selected models and appends to index.yaml | |
| func generateYAMLForModels(ctx context.Context, models []ProcessedModel, quantization string) error { | |
| // Generate YAML entries for each model | |
| var yamlEntries []string | |
| for _, model := range models { | |
| fmt.Printf("Generating YAML entry for model: %s\n", model.ModelID) | |
| // Generate YAML entry | |
| yamlEntry := generateYAMLEntry(model, quantization) | |
| yamlEntries = append(yamlEntries, yamlEntry) | |
| } | |
| // Prepend to index.yaml (write at the top) | |
| if len(yamlEntries) > 0 { | |
| indexPath := getGalleryIndexPath() | |
| fmt.Printf("Prepending YAML entries to %s...\n", indexPath) | |
| // Read current content | |
| content, err := os.ReadFile(indexPath) | |
| if err != nil { | |
| return fmt.Errorf("failed to read %s: %w", indexPath, err) | |
| } | |
| existingContent := string(content) | |
| yamlBlock := strings.Join(yamlEntries, "\n") | |
| // Check if file starts with "---" | |
| var newContent string | |
| if strings.HasPrefix(existingContent, "---\n") { | |
| // File starts with "---", prepend new entries after it | |
| restOfContent := strings.TrimPrefix(existingContent, "---\n") | |
| // Ensure proper spacing: "---\n" + new entries + "\n" + rest of content | |
| newContent = "---\n" + yamlBlock + "\n" + restOfContent | |
| } else if strings.HasPrefix(existingContent, "---") { | |
| // File starts with "---" but no newline after | |
| restOfContent := strings.TrimPrefix(existingContent, "---") | |
| newContent = "---\n" + yamlBlock + "\n" + strings.TrimPrefix(restOfContent, "\n") | |
| } else { | |
| // No "---" at start, prepend new entries at the very beginning | |
| // Trim leading whitespace from existing content | |
| existingContent = strings.TrimLeft(existingContent, " \t\n\r") | |
| newContent = yamlBlock + "\n" + existingContent | |
| } | |
| // Write back to file | |
| err = os.WriteFile(indexPath, []byte(newContent), 0644) | |
| if err != nil { | |
| return fmt.Errorf("failed to write %s: %w", indexPath, err) | |
| } | |
| fmt.Printf("Successfully prepended %d models to %s\n", len(yamlEntries), indexPath) | |
| } | |
| return nil | |
| } | |