From 00c32d5cbec89539292fba43462e6e855e4ea7eb Mon Sep 17 00:00:00 2001 From: grimsace Date: Wed, 3 Jun 2026 13:22:44 -0500 Subject: [PATCH] added support for non-default lm studio api addresses --- .gitignore | 1 + main.go | 161 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 123 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 9882cf6..1bfc3db 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ go.work.sum TODO /binaries/ +converter diff --git a/main.go b/main.go index 0e4ae0c..6b9fc41 100644 --- a/main.go +++ b/main.go @@ -4,14 +4,17 @@ import ( "encoding/base64" "encoding/json" "fmt" + "image/color" "io" "net/http" "os" "path/filepath" "strings" + "time" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/driver/desktop" @@ -20,27 +23,81 @@ import ( ) func main() { - myApp := app.New() + myApp := app.NewWithID("finder2e_foundry_converter") myWindow := myApp.NewWindow("Finder2e Foundry Converter") - // LLM Provider Selection - providerSelect := widget.NewSelect([]string{"LM Studio"}, func(s string) { - fmt.Printf("Provider changed to: %s\n", s) - }) - providerSelect.SetSelected("LM Studio") + prefs := myApp.Preferences() // LM Studio Integration - modelSelect := widget.NewSelect([]string{"Loading models..."}, nil) + savedAddr := prefs.StringWithFallback("LMStudioAddress", "http://localhost:1234") + addrEntry := widget.NewEntry() + addrEntry.SetText(savedAddr) + addrEntry.SetPlaceHolder("LM Studio URL (e.g. http://192.168.1.10:1234)") + addrEntry.OnChanged = func(s string) { + prefs.SetString("LMStudioAddress", s) + } + + addrContainer := container.NewVBox( + widget.NewLabel("LM Studio Address:"), + addrEntry, + ) + addrContainer.Hide() + + // LLM Provider Selection + var toggleAddrBtn *widget.Button + savedProvider := prefs.StringWithFallback("SelectedProvider", "LM Studio") + providerSelect := widget.NewSelect([]string{"LM Studio"}, func(s string) { + fmt.Printf("Provider changed to: %s\n", s) + prefs.SetString("SelectedProvider", s) + if s == "LM Studio" { + toggleAddrBtn.Show() + } else { + toggleAddrBtn.Hide() + addrContainer.Hide() + } + }) + + addrManuallyOpened := false + toggleAddrBtn = widget.NewButton("Address", func() { + if addrContainer.Visible() { + addrContainer.Hide() + addrManuallyOpened = false + } else { + addrContainer.Show() + addrManuallyOpened = true + } + }) + providerSelect.SetSelected(savedProvider) + + errorLabel := canvas.NewText("Error: LM Studio not found. Retrying...", color.NRGBA{R: 255, G: 0, B: 0, A: 255}) + errorLabel.Alignment = fyne.TextAlignCenter + errorLabel.Hide() + + modelSelect := widget.NewSelect([]string{"Loading models..."}, func(s string) { + if s != "" && !strings.HasPrefix(s, "Loading") && !strings.HasPrefix(s, "Error") && !strings.HasPrefix(s, "No models") { + prefs.SetString("LastSelectedModel", s) + } + }) modelSelect.PlaceHolder = "Select Model" refreshModels := func() { if providerSelect.Selected != "LM Studio" { + fyne.Do(func() { + errorLabel.Hide() + }) return } - resp, err := http.Get("http://localhost:1234/v1/models") + + currentAddr := strings.TrimSuffix(addrEntry.Text, "/") + resp, err := http.Get(fmt.Sprintf("%s/v1/models", currentAddr)) if err != nil { - modelSelect.Options = []string{"Error: LM Studio not found"} - modelSelect.Refresh() + fyne.Do(func() { + modelSelect.Options = []string{"Error: LM Studio not found"} + modelSelect.Refresh() + errorLabel.Show() + errorLabel.Refresh() + addrContainer.Show() // Auto-show on failure + }) return } defer resp.Body.Close() @@ -51,8 +108,10 @@ func main() { } `json:"data"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - modelSelect.Options = []string{"Error decoding models"} - modelSelect.Refresh() + fyne.Do(func() { + modelSelect.Options = []string{"Error decoding models"} + modelSelect.Refresh() + }) return } @@ -63,12 +122,46 @@ func main() { if len(models) == 0 { models = []string{"No models loaded in LM Studio"} } - modelSelect.Options = models - modelSelect.Refresh() + + fyne.Do(func() { + errorLabel.Hide() + errorLabel.Refresh() + modelSelect.Options = models + modelSelect.Refresh() + + lastModel := prefs.String("LastSelectedModel") + if lastModel != "" && (modelSelect.Selected == "" || modelSelect.Selected == "Loading models...") { + for _, m := range models { + if m == lastModel { + modelSelect.SetSelected(m) + break + } + } + } + + if !addrManuallyOpened { + addrContainer.Hide() + } + }) } - // Initial fetch - go refreshModels() + // Retry logic: Refresh models every 3 seconds with countdown + go func() { + for { + refreshModels() + if errorLabel.Visible() { + for i := 3; i > 0; i-- { + fyne.Do(func() { + errorLabel.Text = fmt.Sprintf("Error: LM Studio not found. Retrying in %d...", i) + errorLabel.Refresh() + }) + time.Sleep(1 * time.Second) + } + } else { + time.Sleep(3 * time.Second) + } + } + }() textEntry := widget.NewMultiLineEntry() textEntry.SetPlaceHolder("Enter description here...") @@ -76,10 +169,12 @@ func main() { imagePathsContainer := container.NewVBox() addImageEntry := func(path string) { - newEntry := widget.NewEntry() - newEntry.SetText(path) - imagePathsContainer.Add(newEntry) - imagePathsContainer.Refresh() + fyne.Do(func() { + newEntry := widget.NewEntry() + newEntry.SetText(path) + imagePathsContainer.Add(newEntry) + imagePathsContainer.Refresh() + }) } isImage := func(ext string) bool { @@ -134,22 +229,7 @@ func main() { } }) - generateButton := widget.NewButton("Generate", func() { - if modelSelect.Selected == "" { - dialog.ShowInformation("Selection Required", "Please select a model first.", myWindow) - return - } - - if providerSelect.Selected == "LM Studio" { - fmt.Printf("Sending request to LM Studio (%s)...\n", modelSelect.Selected) - // ... (rest of the existing LM Studio generation logic remains below) - } - }) - - // (I'll keep the actual generation logic block to keep the edit clean) - // Actually, I should include it to ensure the code is complete. - // RE-IMPLEMENTING the generate button with provider check: - + generateButton := widget.NewButton("Generate", nil) generateButton.OnTapped = func() { if modelSelect.Selected == "" { dialog.ShowInformation("Selection Required", "Please select a model first.", myWindow) @@ -157,7 +237,8 @@ func main() { } if providerSelect.Selected == "LM Studio" { - fmt.Printf("Sending request to LM Studio (%s)...\n", modelSelect.Selected) + currentAddr := strings.TrimSuffix(addrEntry.Text, "/") + fmt.Printf("Sending request to LM Studio at %s (%s)...\n", currentAddr, modelSelect.Selected) // Prepare messages type MessageContent struct { @@ -202,7 +283,7 @@ func main() { "messages": []interface{}{map[string]interface{}{"role": "user", "content": content}}, }) - resp, err := http.Post("http://localhost:1234/v1/chat/completions", "application/json", strings.NewReader(string(requestBody))) + resp, err := http.Post(fmt.Sprintf("%s/v1/chat/completions", currentAddr), "application/json", strings.NewReader(string(requestBody))) if err != nil { fmt.Printf("Error calling LM Studio: %v\n", err) return @@ -246,7 +327,9 @@ func main() { content := container.NewVBox( widget.NewLabel("Provider:"), - providerSelect, + container.NewBorder(nil, nil, nil, toggleAddrBtn, providerSelect), + errorLabel, + addrContainer, widget.NewLabel("Model:"), container.NewBorder(nil, nil, nil, widget.NewButton("Refresh", refreshModels), modelSelect), widget.NewLabel("Description:"),