|
|
package localai |
|
|
|
|
|
import ( |
|
|
"fmt" |
|
|
"io" |
|
|
"net/http" |
|
|
"os" |
|
|
|
|
|
"github.com/labstack/echo/v4" |
|
|
"github.com/mudler/LocalAI/core/config" |
|
|
httpUtils "github.com/mudler/LocalAI/core/http/middleware" |
|
|
"github.com/mudler/LocalAI/internal" |
|
|
"github.com/mudler/LocalAI/pkg/utils" |
|
|
|
|
|
"gopkg.in/yaml.v3" |
|
|
) |
|
|
|
|
|
|
|
|
func GetEditModelPage(cl *config.ModelConfigLoader, appConfig *config.ApplicationConfig) echo.HandlerFunc { |
|
|
return func(c echo.Context) error { |
|
|
modelName := c.Param("name") |
|
|
if modelName == "" { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Model name is required", |
|
|
} |
|
|
return c.JSON(http.StatusBadRequest, response) |
|
|
} |
|
|
|
|
|
modelConfig, exists := cl.GetModelConfig(modelName) |
|
|
if !exists { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Model configuration not found", |
|
|
} |
|
|
return c.JSON(http.StatusNotFound, response) |
|
|
} |
|
|
|
|
|
modelConfigFile := modelConfig.GetModelConfigFile() |
|
|
if modelConfigFile == "" { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Model configuration file not found", |
|
|
} |
|
|
return c.JSON(http.StatusNotFound, response) |
|
|
} |
|
|
configData, err := os.ReadFile(modelConfigFile) |
|
|
if err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to read configuration file: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusInternalServerError, response) |
|
|
} |
|
|
|
|
|
|
|
|
templateData := struct { |
|
|
Title string |
|
|
ModelName string |
|
|
Config *config.ModelConfig |
|
|
ConfigJSON string |
|
|
ConfigYAML string |
|
|
BaseURL string |
|
|
Version string |
|
|
}{ |
|
|
Title: "LocalAI - Edit Model " + modelName, |
|
|
ModelName: modelName, |
|
|
Config: &modelConfig, |
|
|
ConfigYAML: string(configData), |
|
|
BaseURL: httpUtils.BaseURL(c), |
|
|
Version: internal.PrintableVersion(), |
|
|
} |
|
|
|
|
|
return c.Render(http.StatusOK, "views/model-editor", templateData) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func EditModelEndpoint(cl *config.ModelConfigLoader, appConfig *config.ApplicationConfig) echo.HandlerFunc { |
|
|
return func(c echo.Context) error { |
|
|
modelName := c.Param("name") |
|
|
if modelName == "" { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Model name is required", |
|
|
} |
|
|
return c.JSON(http.StatusBadRequest, response) |
|
|
} |
|
|
|
|
|
modelConfig, exists := cl.GetModelConfig(modelName) |
|
|
if !exists { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Existing model configuration not found", |
|
|
} |
|
|
return c.JSON(http.StatusNotFound, response) |
|
|
} |
|
|
|
|
|
|
|
|
body, err := io.ReadAll(c.Request().Body) |
|
|
if err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to read request body: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusBadRequest, response) |
|
|
} |
|
|
if len(body) == 0 { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Request body is empty", |
|
|
} |
|
|
return c.JSON(http.StatusBadRequest, response) |
|
|
} |
|
|
|
|
|
|
|
|
var req config.ModelConfig |
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal(body, &req); err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to parse YAML: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusBadRequest, response) |
|
|
} |
|
|
|
|
|
|
|
|
if req.Name == "" { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Name is required", |
|
|
} |
|
|
return c.JSON(http.StatusBadRequest, response) |
|
|
} |
|
|
|
|
|
|
|
|
if valid, _ := req.Validate(); !valid { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Validation failed", |
|
|
Details: []string{"Configuration validation failed. Please check your YAML syntax and required fields."}, |
|
|
} |
|
|
return c.JSON(http.StatusBadRequest, response) |
|
|
} |
|
|
|
|
|
|
|
|
configPath := modelConfig.GetModelConfigFile() |
|
|
if err := utils.VerifyPath(configPath, appConfig.SystemState.Model.ModelsPath); err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Model configuration not trusted: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusNotFound, response) |
|
|
} |
|
|
|
|
|
|
|
|
if err := os.WriteFile(configPath, body, 0644); err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to write configuration file: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusInternalServerError, response) |
|
|
} |
|
|
|
|
|
|
|
|
if err := cl.LoadModelConfigsFromPath(appConfig.SystemState.Model.ModelsPath, appConfig.ToConfigLoaderOptions()...); err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to reload configurations: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusInternalServerError, response) |
|
|
} |
|
|
|
|
|
|
|
|
if err := cl.Preload(appConfig.SystemState.Model.ModelsPath); err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to preload model: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusInternalServerError, response) |
|
|
} |
|
|
|
|
|
|
|
|
response := ModelResponse{ |
|
|
Success: true, |
|
|
Message: fmt.Sprintf("Model '%s' updated successfully", modelName), |
|
|
Filename: configPath, |
|
|
Config: req, |
|
|
} |
|
|
return c.JSON(200, response) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func ReloadModelsEndpoint(cl *config.ModelConfigLoader, appConfig *config.ApplicationConfig) echo.HandlerFunc { |
|
|
return func(c echo.Context) error { |
|
|
|
|
|
if err := cl.LoadModelConfigsFromPath(appConfig.SystemState.Model.ModelsPath, appConfig.ToConfigLoaderOptions()...); err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to reload configurations: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusInternalServerError, response) |
|
|
} |
|
|
|
|
|
|
|
|
if err := cl.Preload(appConfig.SystemState.Model.ModelsPath); err != nil { |
|
|
response := ModelResponse{ |
|
|
Success: false, |
|
|
Error: "Failed to preload models: " + err.Error(), |
|
|
} |
|
|
return c.JSON(http.StatusInternalServerError, response) |
|
|
} |
|
|
|
|
|
|
|
|
response := ModelResponse{ |
|
|
Success: true, |
|
|
Message: "Model configurations reloaded successfully", |
|
|
} |
|
|
return c.JSON(http.StatusOK, response) |
|
|
} |
|
|
} |
|
|
|