feat: add model selection synchronization to main config in TUI
This commit is contained in:
parent
74a145c291
commit
545b7afe41
4 changed files with 99 additions and 0 deletions
|
|
@ -8,6 +8,7 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -149,6 +150,78 @@ func (p *Provider) UsersForScheme(schemeName string) []User {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SyncSelectedModelToMainConfig syncs the currently selected model to ~/.picoclaw/config.json
|
||||||
|
// Adds/replaces a "tui-prefer" model entry and sets it as the default model.
|
||||||
|
// Preserves all other existing fields in the config file unchanged.
|
||||||
|
func SyncSelectedModelToMainConfig(scheme Scheme, user User, modelID string) error {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
home = "."
|
||||||
|
}
|
||||||
|
mainConfigPath := filepath.Join(home, ".picoclaw", "config.json")
|
||||||
|
|
||||||
|
var cfg map[string]interface{}
|
||||||
|
if data, err := os.ReadFile(mainConfigPath); err == nil {
|
||||||
|
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||||
|
cfg = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cfg = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := cfg["agents"]; !ok {
|
||||||
|
cfg["agents"] = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
agents, ok := cfg["agents"].(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
if _, ok := agents["defaults"]; !ok {
|
||||||
|
agents["defaults"] = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
defaults, ok := agents["defaults"].(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
defaults["model"] = "tui-prefer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tuiModel := map[string]interface{}{
|
||||||
|
"model_name": "tui-prefer",
|
||||||
|
"model": modelID,
|
||||||
|
"api_key": user.Key,
|
||||||
|
"api_base": scheme.BaseURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
modelList := []interface{}{}
|
||||||
|
if ml, ok := cfg["model_list"].([]interface{}); ok {
|
||||||
|
modelList = ml
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for i, m := range modelList {
|
||||||
|
if entry, ok := m.(map[string]interface{}); ok {
|
||||||
|
if name, ok := entry["model_name"].(string); ok && name == "tui-prefer" {
|
||||||
|
modelList[i] = tuiModel
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
modelList = append(modelList, tuiModel)
|
||||||
|
}
|
||||||
|
cfg["model_list"] = modelList
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(cfg, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(mainConfigPath), 0o700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(mainConfigPath, data, 0o600)
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *TUIConfig) CurrentModelLabel() string {
|
func (cfg *TUIConfig) CurrentModelLabel() string {
|
||||||
cur := cfg.Provider.Current
|
cur := cfg.Provider.Current
|
||||||
if cur.Model == "" {
|
if cur.Model == "" {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
app := ui.New(cfg, configPath)
|
app := ui.New(cfg, configPath)
|
||||||
|
// Bind model selection hook to sync to main config
|
||||||
|
app.OnModelSelected = func(scheme tuicfg.Scheme, user tuicfg.User, modelID string) {
|
||||||
|
_ = tuicfg.SyncSelectedModelToMainConfig(scheme, user, modelID)
|
||||||
|
}
|
||||||
if err := app.Run(); err != nil {
|
if err := app.Run(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "picoclaw-launcher-tui: %v\n", err)
|
fmt.Fprintf(os.Stderr, "picoclaw-launcher-tui: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ type App struct {
|
||||||
headerModelTV *tview.TextView
|
headerModelTV *tview.TextView
|
||||||
modalOpen map[string]bool
|
modalOpen map[string]bool
|
||||||
|
|
||||||
|
// OnModelSelected is called when a model is selected in the UI.
|
||||||
|
// Can be nil to disable.
|
||||||
|
OnModelSelected func(scheme tuicfg.Scheme, user tuicfg.User, modelID string)
|
||||||
|
|
||||||
modelCache map[string][]modelEntry
|
modelCache map[string][]modelEntry
|
||||||
modelCacheMu sync.RWMutex
|
modelCacheMu sync.RWMutex
|
||||||
refreshMu sync.Mutex
|
refreshMu sync.Mutex
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,24 @@ func (a *App) newModelsPage(schemeName, userName, baseURL string) tview.Primitiv
|
||||||
Model: modelIDs[row],
|
Model: modelIDs[row],
|
||||||
}
|
}
|
||||||
a.save()
|
a.save()
|
||||||
|
|
||||||
|
// Trigger model selected callback if set
|
||||||
|
if a.OnModelSelected != nil && a.cfg.Model.Type == "provider" {
|
||||||
|
scheme := a.cfg.Provider.SchemeByName(schemeName)
|
||||||
|
if scheme == nil {
|
||||||
|
a.goBack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var user tuicfg.User
|
||||||
|
for _, u := range a.cfg.Provider.Users {
|
||||||
|
if u.Scheme == schemeName && u.Name == userName {
|
||||||
|
user = u
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.OnModelSelected(*scheme, user, modelIDs[row])
|
||||||
|
}
|
||||||
|
|
||||||
a.goBack()
|
a.goBack()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue