okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
61c43ca
main
1package app 2 3import ( 4"context" 5"fmt" 6"maps" 7"slices" 8"strings" 9 10"albedo/cli/internal/config" 11"albedo/cli/internal/daemon" 12) 13 14// providerModels is one configured provider and the model ids it offers. 15type providerModels struct { 16provider string 17models []string 18} 19 20// modelChoice is a model id and the provider that serves it. 21type modelChoice struct { 22provider string 23model string 24} 25 26// configuredModels lists every provider's models, the active provider first. 27// A provider lists its default model first, even when its catalog cannot be 28// read, as the daemon does when it resolves an agent's model. 29func configuredModels (ctx context.Context ,conn * daemon.Connection ,profiles config.Profiles ) []providerModels { 30names := slices .Sorted (maps .Keys (profiles .Providers )) 31if i := slices .Index (names ,profiles .Active );i > 0 { 32names = append ([]string {profiles .Active },slices .Delete (names ,i ,i + 1 )... ) 33 } 34listed := make ([]providerModels ,0 ,len (names )) 35for _ ,name := range names { 36settings := profiles .Providers [name ] 37models := []string {settings .Model } 38catalog ,_ := daemon .ListProfileModels (ctx ,conn ,settings ) 39for _ ,model := range catalog { 40if ! slices .Contains (models ,model .ID ) { 41models = append (models ,model .ID ) 42 } 43 } 44listed = append (listed ,providerModels {name ,slices .DeleteFunc (models ,func (id string )bool {return id == "" })}) 45 } 46return listed 47} 48 49// chooseModel resolves a model the way the daemon resolves an agent's model: 50// provider/model picks the provider, and a bare id comes from the active 51// provider, else from the one provider that offers it. Only exact ids match. 52func chooseModel (listed []providerModels ,active ,requested string ) (modelChoice ,error ) { 53for _ ,p := range listed { 54if model ,ok := strings .CutPrefix (requested ,p .provider + "/" );ok { 55if ! slices .Contains (p .models ,model ) { 56return modelChoice {},fmt .Errorf ("provider %s does not offer model %s; run albedo models to see the models you can use" ,p .provider ,model ) 57 } 58return modelChoice {p .provider ,model },nil 59 } 60 } 61var offering []string 62for _ ,p := range listed { 63if slices .Contains (p .models ,requested ) { 64offering = append (offering ,p .provider ) 65 } 66 } 67switch { 68case len (offering )== 0 : 69return modelChoice {},fmt .Errorf ("no configured provider offers model %s; run albedo models to see the models you can use" ,requested ) 70case slices .Contains (offering ,active ): 71return modelChoice {active ,requested },nil 72case len (offering )== 1 : 73return modelChoice {offering [0 ],requested },nil 74 } 75return modelChoice {},fmt .Errorf ("model %s is offered by %s; choose one with provider/model" ,requested ,strings .Join (offering ,", " )) 76} 77 78// switchModel moves an idle session to the chosen model without making it the 79// default for new sessions. 80func switchModel (ctx context.Context ,conn * daemon.Connection ,sessionID string ,choice modelChoice )error { 81configuration ,err := daemon .GetSessionConfiguration (ctx ,conn ,sessionID ) 82if err != nil { 83return err 84 } 85_ ,err = daemon .SelectModel (ctx ,conn ,sessionID , daemon.ModelSelectionRequest {Model :choice .model ,Provider :choice .provider ,ETag :configuration .ETag }) 86return err 87} 88 89func (s * Service )Models (ctx context.Context ) ([]string ,error ) { 90conn ,err := s .Connect (ctx ) 91if err != nil { 92return nil ,err 93 } 94profiles ,err := daemon .ProviderProfiles (ctx ,conn ) 95if err != nil { 96return nil ,err 97 } 98var models []string 99for _ ,p := range configuredModels (ctx ,conn ,profiles ) { 100for _ ,model := range p .models { 101models = append (models ,p .provider + "/" + model ) 102 } 103 } 104return models ,nil 105}