@@ -4,13 +4,10 @@ import (
44	"fmt" 
55	"os" 
66	"path/filepath" 
7- 	"regexp" 
8- 	"strconv" 
9- 	"strings" 
107
118	"github.com/gofiber/fiber/v2" 
129	"github.com/mudler/LocalAI/core/config" 
13- 	"github.com/mudler/LocalAI/pkg/downloader " 
10+ 	"github.com/mudler/LocalAI/pkg/utils " 
1411	"gopkg.in/yaml.v3" 
1512)
1613
@@ -28,6 +25,14 @@ func GetEditModelPage(cl *config.BackendConfigLoader, appConfig *config.Applicat
2825
2926		// Load the existing configuration 
3027		configPath  :=  filepath .Join (appConfig .ModelPath , modelName + ".yaml" )
28+ 		if  err  :=  utils .InTrustedRoot (configPath , appConfig .ModelPath ); err  !=  nil  {
29+ 			response  :=  ModelResponse {
30+ 				Success : false ,
31+ 				Error :   "Model configuration not trusted: "  +  err .Error (),
32+ 			}
33+ 			return  c .Status (404 ).JSON (response )
34+ 		}
35+ 
3136		if  _ , err  :=  os .Stat (configPath ); os .IsNotExist (err ) {
3237			response  :=  ModelResponse {
3338				Success : false ,
@@ -56,7 +61,7 @@ func GetEditModelPage(cl *config.BackendConfigLoader, appConfig *config.Applicat
5661		}
5762
5863		// Render the edit page with the current configuration 
59- 		return  c .Render ("views/edit- model" , fiber.Map {
64+ 		return  c .Render ("views/model-editor " , fiber.Map {
6065			"ModelName" : modelName ,
6166			"Config" :    backendConfig ,
6267		})
@@ -73,11 +78,22 @@ func EditModelEndpoint(cl *config.BackendConfigLoader, appConfig *config.Applica
7378			})
7479		}
7580
76- 		var  req  ModelRequest 
77- 		if  err  :=  c .BodyParser (& req ); err  !=  nil  {
81+ 		// Get the raw body as YAML 
82+ 		body  :=  c .Body ()
83+ 		if  len (body ) ==  0  {
7884			response  :=  ModelResponse {
7985				Success : false ,
80- 				Error :   "Failed to parse request: "  +  err .Error (),
86+ 				Error :   "Request body is empty" ,
87+ 			}
88+ 			return  c .Status (400 ).JSON (response )
89+ 		}
90+ 
91+ 		// Unmarshal YAML directly into BackendConfig 
92+ 		var  req  config.BackendConfig 
93+ 		if  err  :=  yaml .Unmarshal (body , & req ); err  !=  nil  {
94+ 			response  :=  ModelResponse {
95+ 				Success : false ,
96+ 				Error :   "Failed to parse YAML: "  +  err .Error (),
8197			}
8298			return  c .Status (400 ).JSON (response )
8399		}
@@ -93,6 +109,14 @@ func EditModelEndpoint(cl *config.BackendConfigLoader, appConfig *config.Applica
93109
94110		// Load the existing configuration 
95111		configPath  :=  filepath .Join (appConfig .ModelPath , modelName + ".yaml" )
112+ 		if  err  :=  utils .InTrustedRoot (configPath , appConfig .ModelPath ); err  !=  nil  {
113+ 			response  :=  ModelResponse {
114+ 				Success : false ,
115+ 				Error :   "Model configuration not trusted: "  +  err .Error (),
116+ 			}
117+ 			return  c .Status (404 ).JSON (response )
118+ 		}
119+ 
96120		if  _ , err  :=  os .Stat (configPath ); os .IsNotExist (err ) {
97121			response  :=  ModelResponse {
98122				Success : false ,
@@ -111,146 +135,25 @@ func EditModelEndpoint(cl *config.BackendConfigLoader, appConfig *config.Applica
111135			return  c .Status (500 ).JSON (response )
112136		}
113137
114- 		var  existingConfig  config.BackendConfig 
115- 		if  err  :=  yaml .Unmarshal (configData , & existingConfig ); err  !=  nil  {
138+ 		var  backendConfig  config.BackendConfig 
139+ 		if  err  :=  yaml .Unmarshal (configData , & backendConfig ); err  !=  nil  {
116140			response  :=  ModelResponse {
117141				Success : false ,
118142				Error :   "Failed to parse existing configuration: "  +  err .Error (),
119143			}
120144			return  c .Status (500 ).JSON (response )
121145		}
122146
123- 		// Update the configuration with new values 
124- 		backendConfig  :=  & existingConfig 
125- 		backendConfig .Backend  =  req .Backend 
126- 		backendConfig .Description  =  req .Description 
127- 		backendConfig .Usage  =  req .Usage 
128- 		backendConfig .Model  =  req .Model 
129- 		backendConfig .MMProj  =  req .MMProj 
130- 
131- 		// Set template configuration 
132- 		if  req .ChatTemplate  !=  ""  ||  req .CompletionTemplate  !=  ""  {
133- 			backendConfig .TemplateConfig  =  config.TemplateConfig {
134- 				Chat :       req .ChatTemplate ,
135- 				Completion : req .CompletionTemplate ,
136- 			}
137- 		}
138- 
139- 		// Set system prompt 
140- 		if  req .SystemPrompt  !=  ""  {
141- 			backendConfig .SystemPrompt  =  req .SystemPrompt 
142- 		}
143- 
144- 		// Set prediction options 
145- 		if  temp , err  :=  strconv .ParseFloat (req .Temperature , 32 ); err  ==  nil  {
146- 			backendConfig .Temperature  =  & temp 
147- 		}
148- 		if  topP , err  :=  strconv .ParseFloat (req .TopP , 32 ); err  ==  nil  {
149- 			backendConfig .TopP  =  & topP 
150- 		}
151- 		if  topK , err  :=  strconv .Atoi (req .TopK ); err  ==  nil  {
152- 			backendConfig .TopK  =  & topK 
153- 		}
154- 		if  ctxSize , err  :=  strconv .Atoi (req .ContextSize ); err  ==  nil  {
155- 			backendConfig .ContextSize  =  & ctxSize 
156- 		}
157- 		if  threads , err  :=  strconv .Atoi (req .Threads ); err  ==  nil  {
158- 			backendConfig .Threads  =  & threads 
159- 		}
160- 		if  seed , err  :=  strconv .Atoi (req .Seed ); err  ==  nil  {
161- 			backendConfig .Seed  =  & seed 
162- 		}
163- 
164- 		// Set feature flags 
165- 		backendConfig .F16  =  & req .F16 
166- 		backendConfig .CUDA  =  req .CUDA 
167- 		backendConfig .Embeddings  =  & req .Embeddings 
168- 		backendConfig .Debug  =  & req .Debug 
169- 		backendConfig .MMap  =  & req .MMap 
170- 		backendConfig .MMlock  =  & req .MMlock 
171- 
172- 		// Set backend-specific configurations 
173- 		switch  req .Backend  {
174- 		case  "llama.cpp" :
175- 			if  req .LoraAdapter  !=  ""  {
176- 				backendConfig .LoraAdapter  =  req .LoraAdapter 
177- 			}
178- 			if  req .Grammar  !=  ""  {
179- 				backendConfig .Grammar  =  req .Grammar 
180- 			}
181- 		case  "diffusers" :
182- 			if  req .PipelineType  !=  ""  {
183- 				backendConfig .Diffusers .PipelineType  =  req .PipelineType 
184- 			}
185- 			if  req .SchedulerType  !=  ""  {
186- 				backendConfig .Diffusers .SchedulerType  =  req .SchedulerType 
187- 			}
188- 		case  "whisper" :
189- 			if  req .AudioPath  !=  ""  {
190- 				backendConfig .AudioPath  =  req .AudioPath 
191- 			}
192- 		case  "bark-cpp" :
193- 			if  req .Voice  !=  ""  {
194- 				backendConfig .Voice  =  req .Voice 
195- 			}
196- 		}
197- 
198147		// Set defaults 
199148		backendConfig .SetDefaults ()
200149
201- 		// Check if model file is a URL and needs downloading 
202- 		if  strings .HasPrefix (req .Model , "http" ) {
203- 			// Add to download files 
204- 			backendConfig .DownloadFiles  =  append (backendConfig .DownloadFiles , config.File {
205- 				URI : downloader .URI (req .Model ),
206- 			})
207- 		}
208- 
209- 		if  req .MMProj  !=  ""  &&  strings .HasPrefix (req .MMProj , "http" ) {
210- 			// Add MMProj to download files 
211- 			backendConfig .DownloadFiles  =  append (backendConfig .DownloadFiles , config.File {
212- 				URI : downloader .URI (req .MMProj ),
213- 			})
214- 		}
215- 
216150		// Validate the configuration 
217151		if  ! backendConfig .Validate () {
218- 			// Provide more specific validation error messages 
219- 			var  validationErrors  []string 
220- 
221- 			if  backendConfig .Backend  ==  ""  {
222- 				validationErrors  =  append (validationErrors , "Backend name is required" )
223- 			} else  {
224- 				// Check if backend name contains invalid characters 
225- 				re  :=  regexp .MustCompile (`^[a-zA-Z0-9-_\.]+$` )
226- 				if  ! re .MatchString (backendConfig .Backend ) {
227- 					validationErrors  =  append (validationErrors , "Backend name contains invalid characters (only letters, numbers, hyphens, underscores, and dots are allowed)" )
228- 				}
229- 			}
230- 
231- 			if  backendConfig .Model  ==  ""  {
232- 				validationErrors  =  append (validationErrors , "Model file/URL is required" )
233- 			}
234- 
235- 			// Check for path traversal attempts 
236- 			if  strings .Contains (backendConfig .Model , ".." ) ||  strings .Contains (backendConfig .Model , string (os .PathSeparator )) {
237- 				validationErrors  =  append (validationErrors , "Model path contains invalid characters" )
238- 			}
239- 
240- 			if  backendConfig .MMProj  !=  ""  {
241- 				if  strings .Contains (backendConfig .MMProj , ".." ) ||  strings .Contains (backendConfig .MMProj , string (os .PathSeparator )) {
242- 					validationErrors  =  append (validationErrors , "MMProj path contains invalid characters" )
243- 				}
244- 			}
245- 
246- 			if  len (validationErrors ) ==  0  {
247- 				validationErrors  =  append (validationErrors , "Configuration validation failed" )
248- 			}
249152
250153			response  :=  ModelResponse {
251154				Success : false ,
252155				Error :   "Validation failed" ,
253- 				Details : validationErrors ,
156+ 				Details : [] string { "Calling backend.Config.Validate()" } ,
254157			}
255158			return  c .Status (400 ).JSON (response )
256159		}
0 commit comments