From b1b007f0584efe2b6a553fb8996df6a997d56298 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sun, 14 Dec 2025 12:31:17 +0300 Subject: Fix: dynamic model update based on api --- props_table.go | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) (limited to 'props_table.go') diff --git a/props_table.go b/props_table.go index dd359f4..1861ca3 100644 --- a/props_table.go +++ b/props_table.go @@ -130,21 +130,48 @@ func makePropsTable(props map[string]float32) *tview.Table { addListPopupRow("Set log level", logLevels, GetLogLevel(), func(option string) { setLogLevel(option) }) + // Helper function to get model list for a given API + getModelListForAPI := func(api string) []string { + var list []string + if strings.Contains(api, "api.deepseek.com/") { + list = []string{chatBody.Model, "deepseek-chat", "deepseek-reasoner"} + } else if strings.Contains(api, "openrouter.ai") { + list = ORFreeModels + } else { + list = LocalModels + } + // Ensure current chatBody.Model is in the list + if len(list) > 0 && !slices.Contains(list, chatBody.Model) { + list = slices.Insert(list, 0, chatBody.Model) + } + return list + } + + var modelRowIndex int // will be set before model row is added + // Prepare API links dropdown - insert current API at the beginning apiLinks := slices.Insert(cfg.ApiLinks, 0, cfg.CurrentAPI) addListPopupRow("Select an api", apiLinks, cfg.CurrentAPI, func(option string) { cfg.CurrentAPI = option + // Update model list based on new API + newModelList := getModelListForAPI(cfg.CurrentAPI) + modelCellID := fmt.Sprintf("listpopup_%d", modelRowIndex) + if data := cellData[modelCellID]; data != nil { + data.Options = newModelList + } + // Ensure chatBody.Model is in the new list; if not, set to first available model + if len(newModelList) > 0 && !slices.Contains(newModelList, chatBody.Model) { + chatBody.Model = newModelList[0] + // Update the displayed cell text + if cell := table.GetCell(modelRowIndex, 1); cell != nil { + cell.SetText(chatBody.Model) + } + } }) - var modelList []string - // INFO: modelList is chosen based on current api link - if strings.Contains(cfg.CurrentAPI, "api.deepseek.com/") { - modelList = []string{chatBody.Model, "deepseek-chat", "deepseek-reasoner"} - } else if strings.Contains(cfg.CurrentAPI, "opentouter.ai") { - modelList = ORFreeModels - } else { // would match on localhost but what if llama.cpp served non localy? - modelList = LocalModels - } + // Prepare model list dropdown + modelRowIndex = row + modelList := getModelListForAPI(cfg.CurrentAPI) addListPopupRow("Select a model", modelList, chatBody.Model, func(option string) { chatBody.Model = option }) -- cgit v1.2.3 From 090eb90ee7006715adeaf77ed78fb265b7310812 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sun, 14 Dec 2025 13:02:15 +0300 Subject: Enha: notify user if model list is empty --- props_table.go | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'props_table.go') diff --git a/props_table.go b/props_table.go index 1861ca3..8cb956f 100644 --- a/props_table.go +++ b/props_table.go @@ -132,19 +132,12 @@ func makePropsTable(props map[string]float32) *tview.Table { }) // Helper function to get model list for a given API getModelListForAPI := func(api string) []string { - var list []string if strings.Contains(api, "api.deepseek.com/") { - list = []string{chatBody.Model, "deepseek-chat", "deepseek-reasoner"} + return []string{"deepseek-chat", "deepseek-reasoner"} } else if strings.Contains(api, "openrouter.ai") { - list = ORFreeModels - } else { - list = LocalModels + return ORFreeModels } - // Ensure current chatBody.Model is in the list - if len(list) > 0 && !slices.Contains(list, chatBody.Model) { - list = slices.Insert(list, 0, chatBody.Model) - } - return list + return LocalModels } var modelRowIndex int // will be set before model row is added @@ -256,6 +249,20 @@ func makePropsTable(props map[string]float32) *tview.Table { if cellData[listPopupCellID] != nil && cellData[listPopupCellID].Type == CellTypeListPopup { data := cellData[listPopupCellID] if onChange, ok := data.OnChange.(func(string)); ok && data.Options != nil { + // Check for empty options list + if len(data.Options) == 0 { + // Get label for context + labelCell := table.GetCell(selectedRow, 0) + label := "item" + if labelCell != nil { + label = labelCell.Text + } + logger.Warn("empty options list for", "label", label) + if err := notifyUser("Empty list", "No options available for " + label); err != nil { + logger.Error("failed to send notification", "error", err) + } + return + } // Create a list primitive apiList := tview.NewList().ShowSecondaryText(false). SetSelectedBackgroundColor(tcell.ColorGray) -- cgit v1.2.3 From 37b88e7879461d4bc668ff54fb19ef9870b2ba5c Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sun, 14 Dec 2025 13:23:59 +0300 Subject: Fix: empty model list --- props_table.go | 67 +++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 15 deletions(-) (limited to 'props_table.go') diff --git a/props_table.go b/props_table.go index 8cb956f..9408469 100644 --- a/props_table.go +++ b/props_table.go @@ -50,6 +50,7 @@ func makePropsTable(props map[string]float32) *tview.Table { row++ // Store cell data for later use in selection functions cellData := make(map[string]*CellData) + var modelCellID string // will be set for the model selection row // Helper function to add a checkbox-like row addCheckboxRow := func(label string, initialValue bool, onChange func(bool)) { table.SetCell(row, 0, @@ -148,22 +149,30 @@ func makePropsTable(props map[string]float32) *tview.Table { cfg.CurrentAPI = option // Update model list based on new API newModelList := getModelListForAPI(cfg.CurrentAPI) - modelCellID := fmt.Sprintf("listpopup_%d", modelRowIndex) - if data := cellData[modelCellID]; data != nil { - data.Options = newModelList + if modelCellID != "" { + if data := cellData[modelCellID]; data != nil { + data.Options = newModelList + } } // Ensure chatBody.Model is in the new list; if not, set to first available model if len(newModelList) > 0 && !slices.Contains(newModelList, chatBody.Model) { chatBody.Model = newModelList[0] - // Update the displayed cell text - if cell := table.GetCell(modelRowIndex, 1); cell != nil { - cell.SetText(chatBody.Model) + // Update the displayed cell text - need to find model row + // Search for model row by label + for r := 0; r < table.GetRowCount(); r++ { + if cell := table.GetCell(r, 0); cell != nil && cell.Text == "Select a model" { + if valueCell := table.GetCell(r, 1); valueCell != nil { + valueCell.SetText(chatBody.Model) + } + break + } } } }) // Prepare model list dropdown modelRowIndex = row + modelCellID = fmt.Sprintf("listpopup_%d", modelRowIndex) modelList := getModelListForAPI(cfg.CurrentAPI) addListPopupRow("Select a model", modelList, chatBody.Model, func(option string) { chatBody.Model = option @@ -248,17 +257,45 @@ func makePropsTable(props map[string]float32) *tview.Table { listPopupCellID := fmt.Sprintf("listpopup_%d", selectedRow) if cellData[listPopupCellID] != nil && cellData[listPopupCellID].Type == CellTypeListPopup { data := cellData[listPopupCellID] - if onChange, ok := data.OnChange.(func(string)); ok && data.Options != nil { + if onChange, ok := data.OnChange.(func(string)); ok { + // Get label for context + labelCell := table.GetCell(selectedRow, 0) + label := "item" + if labelCell != nil { + label = labelCell.Text + } + + // For model selection, always compute fresh options from current API + if label == "Select a model" { + freshOptions := getModelListForAPI(cfg.CurrentAPI) + data.Options = freshOptions + // Also update the cell data map + cellData[listPopupCellID].Options = freshOptions + } + + // Handle nil options + if data.Options == nil { + logger.Error("options list is nil for", "label", label) + if err := notifyUser("Configuration error", "Options list is nil for " + label); err != nil { + logger.Error("failed to send notification", "error", err) + } + return + } + // Check for empty options list if len(data.Options) == 0 { - // Get label for context - labelCell := table.GetCell(selectedRow, 0) - label := "item" - if labelCell != nil { - label = labelCell.Text + logger.Warn("empty options list for", "label", label, "api", cfg.CurrentAPI, "localModelsLen", len(LocalModels), "orModelsLen", len(ORFreeModels)) + message := "No options available for " + label + if label == "Select a model" { + if strings.Contains(cfg.CurrentAPI, "openrouter.ai") { + message = "No OpenRouter models available. Check token and connection." + } else if strings.Contains(cfg.CurrentAPI, "api.deepseek.com") { + message = "DeepSeek models should be available. Please report bug." + } else { + message = "No llama.cpp models loaded. Ensure llama.cpp server is running with models." + } } - logger.Warn("empty options list for", "label", label) - if err := notifyUser("Empty list", "No options available for " + label); err != nil { + if err := notifyUser("Empty list", message); err != nil { logger.Error("failed to send notification", "error", err) } return @@ -266,7 +303,7 @@ func makePropsTable(props map[string]float32) *tview.Table { // Create a list primitive apiList := tview.NewList().ShowSecondaryText(false). SetSelectedBackgroundColor(tcell.ColorGray) - apiList.SetTitle("Select an API").SetBorder(true) + apiList.SetTitle("Select " + label).SetBorder(true) for i, api := range data.Options { if api == cell.Text { apiList.SetCurrentItem(i) -- cgit v1.2.3 From 82ffc364d34f4906ef4c4c1bd4bd202d393a46bc Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sun, 14 Dec 2025 13:34:26 +0300 Subject: Chore: remove unused ticker --- props_table.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'props_table.go') diff --git a/props_table.go b/props_table.go index 9408469..dfbace8 100644 --- a/props_table.go +++ b/props_table.go @@ -140,9 +140,7 @@ func makePropsTable(props map[string]float32) *tview.Table { } return LocalModels } - var modelRowIndex int // will be set before model row is added - // Prepare API links dropdown - insert current API at the beginning apiLinks := slices.Insert(cfg.ApiLinks, 0, cfg.CurrentAPI) addListPopupRow("Select an api", apiLinks, cfg.CurrentAPI, func(option string) { @@ -169,7 +167,6 @@ func makePropsTable(props map[string]float32) *tview.Table { } } }) - // Prepare model list dropdown modelRowIndex = row modelCellID = fmt.Sprintf("listpopup_%d", modelRowIndex) @@ -276,7 +273,7 @@ func makePropsTable(props map[string]float32) *tview.Table { // Handle nil options if data.Options == nil { logger.Error("options list is nil for", "label", label) - if err := notifyUser("Configuration error", "Options list is nil for " + label); err != nil { + if err := notifyUser("Configuration error", "Options list is nil for "+label); err != nil { logger.Error("failed to send notification", "error", err) } return -- cgit v1.2.3 From 35851647a191f779943530591610a9b22ffaeff9 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Tue, 16 Dec 2025 21:52:03 +0300 Subject: Fix: remove dulicate from apilinks list --- props_table.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'props_table.go') diff --git a/props_table.go b/props_table.go index dfbace8..774ea32 100644 --- a/props_table.go +++ b/props_table.go @@ -141,8 +141,14 @@ func makePropsTable(props map[string]float32) *tview.Table { return LocalModels } var modelRowIndex int // will be set before model row is added - // Prepare API links dropdown - insert current API at the beginning - apiLinks := slices.Insert(cfg.ApiLinks, 0, cfg.CurrentAPI) + // Prepare API links dropdown - ensure current API is first, avoid duplicates + apiLinks := make([]string, 0, len(cfg.ApiLinks)+1) + apiLinks = append(apiLinks, cfg.CurrentAPI) + for _, api := range cfg.ApiLinks { + if api != cfg.CurrentAPI { + apiLinks = append(apiLinks, api) + } + } addListPopupRow("Select an api", apiLinks, cfg.CurrentAPI, func(option string) { cfg.CurrentAPI = option // Update model list based on new API -- cgit v1.2.3 From 67ea1aef0dafb9dc6f82e009cc1ecc613f71e520 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Fri, 19 Dec 2025 11:06:22 +0300 Subject: Feat: two agent types; WebAgentB impl --- props_table.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'props_table.go') diff --git a/props_table.go b/props_table.go index 774ea32..ae225d8 100644 --- a/props_table.go +++ b/props_table.go @@ -161,6 +161,7 @@ func makePropsTable(props map[string]float32) *tview.Table { // Ensure chatBody.Model is in the new list; if not, set to first available model if len(newModelList) > 0 && !slices.Contains(newModelList, chatBody.Model) { chatBody.Model = newModelList[0] + cfg.CurrentModel = chatBody.Model // Update the displayed cell text - need to find model row // Search for model row by label for r := 0; r < table.GetRowCount(); r++ { @@ -179,6 +180,7 @@ func makePropsTable(props map[string]float32) *tview.Table { modelList := getModelListForAPI(cfg.CurrentAPI) addListPopupRow("Select a model", modelList, chatBody.Model, func(option string) { chatBody.Model = option + cfg.CurrentModel = chatBody.Model }) // Role selection dropdown addListPopupRow("Write next message as", listRolesWithUser(), cfg.WriteNextMsgAs, func(option string) { -- cgit v1.2.3 From ba3330ee54bcab5cfde470f8e465fc9ed1c6cb2c Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sat, 20 Dec 2025 14:21:40 +0300 Subject: Fix: model load if llama.cpp started after gf-lt --- props_table.go | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'props_table.go') diff --git a/props_table.go b/props_table.go index ae225d8..0c49056 100644 --- a/props_table.go +++ b/props_table.go @@ -5,11 +5,14 @@ import ( "slices" "strconv" "strings" + "sync" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) +var _ = sync.RWMutex{} + // Define constants for cell types const ( CellTypeCheckbox = "checkbox" @@ -138,6 +141,10 @@ func makePropsTable(props map[string]float32) *tview.Table { } else if strings.Contains(api, "openrouter.ai") { return ORFreeModels } + // Assume local llama.cpp + refreshLocalModelsIfEmpty() + localModelsMu.RLock() + defer localModelsMu.RUnlock() return LocalModels } var modelRowIndex int // will be set before model row is added -- cgit v1.2.3