Files
finder2e_foundry_converter/main.go
T

264 lines
6.9 KiB
Go

package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
)
func main() {
myApp := app.New()
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")
// LM Studio Integration
modelSelect := widget.NewSelect([]string{"Loading models..."}, nil)
modelSelect.PlaceHolder = "Select Model"
refreshModels := func() {
if providerSelect.Selected != "LM Studio" {
return
}
resp, err := http.Get("http://localhost:1234/v1/models")
if err != nil {
modelSelect.Options = []string{"Error: LM Studio not found"}
modelSelect.Refresh()
return
}
defer resp.Body.Close()
var result struct {
Data []struct {
ID string `json:"id"`
} `json:"data"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
modelSelect.Options = []string{"Error decoding models"}
modelSelect.Refresh()
return
}
var models []string
for _, m := range result.Data {
models = append(models, m.ID)
}
if len(models) == 0 {
models = []string{"No models loaded in LM Studio"}
}
modelSelect.Options = models
modelSelect.Refresh()
}
// Initial fetch
go refreshModels()
textEntry := widget.NewMultiLineEntry()
textEntry.SetPlaceHolder("Enter description here...")
imagePathsContainer := container.NewVBox()
addImageEntry := func(path string) {
newEntry := widget.NewEntry()
newEntry.SetText(path)
imagePathsContainer.Add(newEntry)
imagePathsContainer.Refresh()
}
isImage := func(ext string) bool {
switch ext {
case ".png", ".jpg", ".jpeg", ".webp":
return true
}
return false
}
imagePickerButton := widget.NewButton("Select Image", func() {
fd := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil || reader == nil {
return
}
defer reader.Close()
addImageEntry(reader.URI().Path())
}, myWindow)
fd.SetFilter(storage.NewExtensionFileFilter([]string{".png", ".jpg", ".jpeg", ".webp"}))
fd.Show()
})
// Drag and Drop support
myWindow.SetOnDropped(func(pos fyne.Position, uris []fyne.URI) {
for _, u := range uris {
if isImage(u.Extension()) {
addImageEntry(u.Path())
}
}
})
// Paste support (Ctrl+V)
pasteShortcut := &desktop.CustomShortcut{KeyName: fyne.KeyV, Modifier: fyne.KeyModifierShortcutDefault}
myWindow.Canvas().AddShortcut(pasteShortcut, func(shortcut fyne.Shortcut) {
content := myWindow.Clipboard().Content()
if content == "" {
return
}
// Handle file URLs (sometimes copied from file managers)
path := strings.TrimPrefix(content, "file://")
path = strings.TrimSpace(path)
ext := strings.ToLower(filepath.Ext(path))
if isImage(ext) {
// Check if it's a valid local path
if _, err := os.Stat(path); err == nil {
addImageEntry(path)
}
}
})
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.OnTapped = 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)
// Prepare messages
type MessageContent struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
ImageURL *struct {
URL string `json:"url"`
} `json:"image_url,omitempty"`
}
var content []MessageContent
content = append(content, MessageContent{Type: "text", Text: textEntry.Text})
for _, obj := range imagePathsContainer.Objects {
if entry, ok := obj.(*widget.Entry); ok && entry.Text != "" {
imgData, err := os.ReadFile(entry.Text)
if err != nil {
fmt.Printf("Error reading image %s: %v\n", entry.Text, err)
continue
}
base64Img := base64.StdEncoding.EncodeToString(imgData)
mimeType := "image/png"
if strings.HasSuffix(strings.ToLower(entry.Text), ".jpg") || strings.HasSuffix(strings.ToLower(entry.Text), ".jpeg") {
mimeType = "image/jpeg"
} else if strings.HasSuffix(strings.ToLower(entry.Text), ".webp") {
mimeType = "image/webp"
}
content = append(content, MessageContent{
Type: "image_url",
ImageURL: &struct {
URL string `json:"url"`
}{
URL: fmt.Sprintf("data:%s;base64,%s", mimeType, base64Img),
},
})
}
}
requestBody, _ := json.Marshal(map[string]interface{}{
"model": modelSelect.Selected,
"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)))
if err != nil {
fmt.Printf("Error calling LM Studio: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var chatResp struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
Error struct {
Message string `json:"message"`
} `json:"error"`
}
if err := json.Unmarshal(body, &chatResp); err != nil {
fmt.Printf("Error decoding response: %v\n", err)
fmt.Println("Raw response:", string(body))
return
}
if chatResp.Error.Message != "" {
fmt.Printf("LM Studio Error: %s\n", chatResp.Error.Message)
return
}
if len(chatResp.Choices) > 0 {
fmt.Println("\nLM Studio Response:")
fmt.Println(chatResp.Choices[0].Message.Content)
} else {
fmt.Println("No response choices returned.")
}
}
}
scrollContainer := container.NewVScroll(imagePathsContainer)
scrollContainer.SetMinSize(fyne.NewSize(0, 150))
content := container.NewVBox(
widget.NewLabel("Provider:"),
providerSelect,
widget.NewLabel("Model:"),
container.NewBorder(nil, nil, nil, widget.NewButton("Refresh", refreshModels), modelSelect),
widget.NewLabel("Description:"),
textEntry,
widget.NewLabel("Images:"),
scrollContainer,
imagePickerButton,
generateButton,
)
myWindow.SetContent(content)
myWindow.Resize(fyne.NewSize(500, 500))
myWindow.ShowAndRun()
}