|
|
package services |
|
|
|
|
|
import ( |
|
|
"context" |
|
|
"fmt" |
|
|
"sync" |
|
|
|
|
|
"github.com/mudler/LocalAI/core/config" |
|
|
"github.com/mudler/LocalAI/core/gallery" |
|
|
"github.com/mudler/LocalAI/pkg/model" |
|
|
"github.com/mudler/LocalAI/pkg/system" |
|
|
) |
|
|
|
|
|
type GalleryService struct { |
|
|
appConfig *config.ApplicationConfig |
|
|
sync.Mutex |
|
|
ModelGalleryChannel chan GalleryOp[gallery.GalleryModel, gallery.ModelConfig] |
|
|
BackendGalleryChannel chan GalleryOp[gallery.GalleryBackend, any] |
|
|
|
|
|
modelLoader *model.ModelLoader |
|
|
statuses map[string]*GalleryOpStatus |
|
|
cancellations map[string]context.CancelFunc |
|
|
} |
|
|
|
|
|
func NewGalleryService(appConfig *config.ApplicationConfig, ml *model.ModelLoader) *GalleryService { |
|
|
return &GalleryService{ |
|
|
appConfig: appConfig, |
|
|
ModelGalleryChannel: make(chan GalleryOp[gallery.GalleryModel, gallery.ModelConfig]), |
|
|
BackendGalleryChannel: make(chan GalleryOp[gallery.GalleryBackend, any]), |
|
|
modelLoader: ml, |
|
|
statuses: make(map[string]*GalleryOpStatus), |
|
|
cancellations: make(map[string]context.CancelFunc), |
|
|
} |
|
|
} |
|
|
|
|
|
func (g *GalleryService) UpdateStatus(s string, op *GalleryOpStatus) { |
|
|
g.Lock() |
|
|
defer g.Unlock() |
|
|
g.statuses[s] = op |
|
|
} |
|
|
|
|
|
func (g *GalleryService) GetStatus(s string) *GalleryOpStatus { |
|
|
g.Lock() |
|
|
defer g.Unlock() |
|
|
|
|
|
return g.statuses[s] |
|
|
} |
|
|
|
|
|
func (g *GalleryService) GetAllStatus() map[string]*GalleryOpStatus { |
|
|
g.Lock() |
|
|
defer g.Unlock() |
|
|
|
|
|
return g.statuses |
|
|
} |
|
|
|
|
|
|
|
|
func (g *GalleryService) CancelOperation(id string) error { |
|
|
g.Lock() |
|
|
defer g.Unlock() |
|
|
|
|
|
|
|
|
if status, ok := g.statuses[id]; ok && status.Cancelled { |
|
|
return fmt.Errorf("operation %q is already cancelled", id) |
|
|
} |
|
|
|
|
|
cancelFunc, exists := g.cancellations[id] |
|
|
if !exists { |
|
|
return fmt.Errorf("operation %q not found or already completed", id) |
|
|
} |
|
|
|
|
|
|
|
|
cancelFunc() |
|
|
|
|
|
|
|
|
if status, ok := g.statuses[id]; ok { |
|
|
status.Cancelled = true |
|
|
status.Processed = true |
|
|
status.Message = "cancelled" |
|
|
} else { |
|
|
|
|
|
g.statuses[id] = &GalleryOpStatus{ |
|
|
Cancelled: true, |
|
|
Processed: true, |
|
|
Message: "cancelled", |
|
|
Cancellable: false, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
delete(g.cancellations, id) |
|
|
|
|
|
return nil |
|
|
} |
|
|
|
|
|
|
|
|
func (g *GalleryService) storeCancellation(id string, cancelFunc context.CancelFunc) { |
|
|
g.Lock() |
|
|
defer g.Unlock() |
|
|
g.cancellations[id] = cancelFunc |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (g *GalleryService) StoreCancellation(id string, cancelFunc context.CancelFunc) { |
|
|
g.storeCancellation(id, cancelFunc) |
|
|
} |
|
|
|
|
|
|
|
|
func (g *GalleryService) removeCancellation(id string) { |
|
|
g.Lock() |
|
|
defer g.Unlock() |
|
|
delete(g.cancellations, id) |
|
|
} |
|
|
|
|
|
func (g *GalleryService) Start(c context.Context, cl *config.ModelConfigLoader, systemState *system.SystemState) error { |
|
|
|
|
|
var updateError func(id string, e error) |
|
|
if !g.appConfig.OpaqueErrors { |
|
|
updateError = func(id string, e error) { |
|
|
g.UpdateStatus(id, &GalleryOpStatus{Error: e, Processed: true, Message: "error: " + e.Error()}) |
|
|
} |
|
|
} else { |
|
|
updateError = func(id string, _ error) { |
|
|
g.UpdateStatus(id, &GalleryOpStatus{Error: fmt.Errorf("an error occurred"), Processed: true}) |
|
|
} |
|
|
} |
|
|
|
|
|
go func() { |
|
|
for { |
|
|
select { |
|
|
case <-c.Done(): |
|
|
return |
|
|
case op := <-g.BackendGalleryChannel: |
|
|
|
|
|
if op.Context == nil { |
|
|
op.Context, op.CancelFunc = context.WithCancel(c) |
|
|
g.storeCancellation(op.ID, op.CancelFunc) |
|
|
} else if op.CancelFunc != nil { |
|
|
g.storeCancellation(op.ID, op.CancelFunc) |
|
|
} |
|
|
err := g.backendHandler(&op, systemState) |
|
|
if err != nil { |
|
|
updateError(op.ID, err) |
|
|
} |
|
|
g.removeCancellation(op.ID) |
|
|
|
|
|
case op := <-g.ModelGalleryChannel: |
|
|
|
|
|
if op.Context == nil { |
|
|
op.Context, op.CancelFunc = context.WithCancel(c) |
|
|
g.storeCancellation(op.ID, op.CancelFunc) |
|
|
} else if op.CancelFunc != nil { |
|
|
g.storeCancellation(op.ID, op.CancelFunc) |
|
|
} |
|
|
err := g.modelHandler(&op, cl, systemState) |
|
|
if err != nil { |
|
|
updateError(op.ID, err) |
|
|
} |
|
|
g.removeCancellation(op.ID) |
|
|
} |
|
|
} |
|
|
}() |
|
|
|
|
|
return nil |
|
|
} |
|
|
|