okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
5924d14
main
1package daemon 2 3import ( 4"context" 5"fmt" 6"io" 7"net/http" 8"strings" 9 10"albedo/cli/internal/config" 11"albedo/cli/internal/daemon/protocol" 12) 13 14type Model struct { 15ID ,Label ,CapKey string 16Efforts ,Input []string 17Context ,MaxContext ,Output int 18Raised bool 19} 20type EffortResult struct { 21Effort ,Message ,ETag string 22Available []string 23} 24type ModelSelection struct {Provider ,Model ,Protocol ,Effort ,ETag ,DefaultETag ,ModelsETag string } 25type ModelSelectionRequest struct {Model ,Provider ,Effort ,ETag string } 26type ModelChangeRequest struct { 27Model ,Provider ,Effort ,ETag ,DefaultETag string 28MakeDefault bool 29} 30type ModelContextCapRequest struct { 31CapKey ,ETag string 32Enabled bool 33} 34 35func ListProfileModels (ctx context.Context ,conn * Connection ,profile config.Settings ) ([]Model ,error ) { 36if profile .ProfileName != "" { 37return listModels (ctx ,conn , protocol.ListModelsParams {ProviderProfile :& profile .ProfileName }) 38 } 39return ListModels (ctx ,conn ,profile .Extension ,profile .BaseURL ) 40} 41func ListModels (ctx context.Context ,conn * Connection ,provider ,endpoint string ) ([]Model ,error ) { 42params := protocol.ListModelsParams {Provider :& provider } 43if endpoint != "" { 44params .Endpoint = & endpoint 45 } 46return listModels (ctx ,conn ,params ) 47} 48func listModels (ctx context.Context ,conn * Connection ,params protocol.ListModelsParams ) ([]Model ,error ) { 49result := []Model {} 50params .Limit = new (int64 (200 )) 51err := walkPages (func (next * string ) (protocol.ModelPage ,* string ,error ) { 52params .Next = next 53var page protocol.ModelPage 54pageErr := executeRead (ctx ,conn ,operation {Name :"list models" ,BuildRequest :func (base string ,body io.Reader ) (* http.Request ,error ) { 55return protocol .NewListModelsRequest (base ,& params ) 56 },Policy :readRecovery },func (data []byte )error {return decodeRequired (data ,& page ) }) 57return page ,page .Next ,pageErr 58 },func (page protocol.ModelPage )error { 59if page .Items == nil { 60return fieldError ("models" ) 61 } 62for _ ,row := range page .Items { 63if row .ID == "" || row .CapKey == "" || row .Efforts == nil { 64return fieldError ("model" ) 65 } 66model := Model {ID :row .ID ,Label :row .Label ,CapKey :row .CapKey ,Input :row .InputModalities ,Context :int (value (row .DefaultContextTokens )),MaxContext :int (value (row .MaxContextTokens )),Output :int (value (row .MaxOutputTokens )),Raised :row .Raised } 67for _ ,effort := range row .Efforts { 68model .Efforts = append (model .Efforts ,effort .ID ) 69 } 70result = append (result ,model ) 71 } 72return nil 73 },"model page cursor" ) 74if err != nil { 75return nil ,err 76 } 77return result ,nil 78} 79 80func SelectModel (ctx context.Context ,conn * Connection ,id string ,request ModelSelectionRequest ) (ModelSelection ,error ) { 81body := map [string ]any {"model" :request .Model } 82if request .Provider != "" { 83body ["provider_profile" ]= request .Provider 84 } 85if request .Effort != "" { 86body ["effort" ]= request .Effort 87 } 88session ,err := patchSession (ctx ,conn ,id ,request .ETag ,body ) 89return ModelSelection {Provider :session .Provider ,Model :session .Model ,Protocol :session .Protocol ,Effort :session .Effort ,ETag :session .ETag },err 90} 91func ChangeModel (ctx context.Context ,conn * Connection ,id string ,request ModelChangeRequest ) (ModelSelection ,error ) { 92if request .MakeDefault && request .DefaultETag == "" { 93return ModelSelection {},fieldError ("provider settings validator" ) 94 } 95selection ,err := SelectModel (ctx ,conn ,id ,ModelSelectionRequest {Model :request .Model ,Provider :request .Provider ,Effort :request .Effort ,ETag :request .ETag }) 96if err != nil { 97return selection ,err 98 } 99if request .MakeDefault { 100var change protocol.ProviderSettingsChange 101saveErr := patchSettingsGroup (ctx ,conn ,"providers" ,request .DefaultETag ,map [string ]any {"default_profile" :selection .Provider ,"profiles" :map [string ]any {selection .Provider :map [string ]any {"model" :selection .Model ,"effort" :optionalText (selection .Effort )}}},& change ) 102selection .DefaultETag = change .Resource .ETag 103if saveErr != nil { 104return selection ,fmt .Errorf ("switched session; saving the default model: %w" ,saveErr ) 105 } 106 } 107return selection ,nil 108} 109func SetModelContextCap (ctx context.Context ,conn * Connection ,request ModelContextCapRequest ) (string ,error ) { 110if request .CapKey == "" { 111return "" ,fieldError ("model cap key" ) 112 } 113var change protocol.ModelSettingsChange 114err := patchSettingsGroup (ctx ,conn ,"models" ,request .ETag ,map [string ]any {"raised_caps" :map [string ]bool {request .CapKey :request .Enabled }},& change ) 115return change .Resource .ETag ,err 116} 117func ReadEffort (ctx context.Context ,conn * Connection ,session Session ) (EffortResult ,error ) { 118params := protocol.ListModelsParams {Model :& session .Model } 119if session .Provider != "" { 120params .ProviderProfile = & session .Provider 121 } 122models ,err := listModels (ctx ,conn ,params ) 123if err != nil { 124return EffortResult {},err 125 } 126for _ ,model := range models { 127if model .ID == session .Model { 128return EffortResult {Effort :session .Effort ,Available :model .Efforts },nil 129 } 130 } 131return EffortResult {},fmt .Errorf ("model %q has no metadata" ,session .Model ) 132} 133 134// SelectEffort clears the preference when effort is empty. 135func SelectEffort (ctx context.Context ,conn * Connection ,session Session ,effort string ) (EffortResult ,error ) { 136result ,err := patchSession (ctx ,conn ,session .ID ,session .ETag ,map [string ]any {"effort" :optionalText (effort )}) 137return EffortResult {Effort :result .Effort ,Message :"Effort saved." ,ETag :result .ETag },err 138} 139 140// ResolveSessionModel keeps a manual ID in the observed session profile, or 141// selects an explicitly named configured profile. The daemon validates the ID 142// and computes the available reasoning efforts. 143func ResolveSessionModel (profiles config.Profiles ,requested string ) (string ,string ,error ) { 144if strings .TrimSpace (requested )== "" { 145return "" ,"" ,fmt .Errorf ("enter a model ID" ) 146 } 147for name := range profiles .Providers { 148if model ,explicit := strings .CutPrefix (requested ,name + "/" );explicit { 149if model == "" { 150return "" ,"" ,fmt .Errorf ("enter a model ID after %s/" ,name ) 151 } 152return name ,model ,nil 153 } 154 } 155if _ ,configured := profiles .Providers [profiles .Active ];! configured { 156return "" ,"" ,fmt .Errorf ("the session's provider is not configured; run /login" ) 157 } 158return profiles .Active ,requested ,nil 159}