diff --git a/App.axaml b/app.axaml
similarity index 100%
rename from App.axaml
rename to app.axaml
diff --git a/App.axaml.cs b/app.axaml.cs
similarity index 100%
rename from App.axaml.cs
rename to app.axaml.cs
diff --git a/Assets/avalonia-logo.ico b/assets/avalonia-logo.ico
similarity index 100%
rename from Assets/avalonia-logo.ico
rename to assets/avalonia-logo.ico
diff --git a/finder2e_foundry_converter.csproj b/finder2e_foundry_converter.csproj
index 7dbfc62..7edd38b 100644
--- a/finder2e_foundry_converter.csproj
+++ b/finder2e_foundry_converter.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/LICENSE b/license
similarity index 100%
rename from LICENSE
rename to license
diff --git a/Program.cs b/main.cs
similarity index 100%
rename from Program.cs
rename to main.cs
diff --git a/Models/ImageItem.cs b/models/imageitem.cs
similarity index 100%
rename from Models/ImageItem.cs
rename to models/imageitem.cs
diff --git a/README.md b/readme.md
similarity index 100%
rename from README.md
rename to readme.md
diff --git a/services/illmservice.cs b/services/illmservice.cs
new file mode 100644
index 0000000..9373fd4
--- /dev/null
+++ b/services/illmservice.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace finder2e_foundry_converter.Services
+{
+ public interface ILlmService
+ {
+ Task> GetModelsAsync(string baseUrl);
+ Task GenerateResponseAsync(string baseUrl, string model, string text, List imagePaths);
+ }
+}
diff --git a/Services/LlmService.cs b/services/lmstudioservice.cs
similarity index 98%
rename from Services/LlmService.cs
rename to services/lmstudioservice.cs
index 71231c8..ef7ad3d 100644
--- a/Services/LlmService.cs
+++ b/services/lmstudioservice.cs
@@ -8,7 +8,7 @@ using System.Text.Json.Serialization;
namespace finder2e_foundry_converter.Services
{
- public class LlmService
+ public class LmStudioService : ILlmService
{
private readonly HttpClient _httpClient = new HttpClient();
diff --git a/services/ollamaservice.cs b/services/ollamaservice.cs
new file mode 100644
index 0000000..684d19a
--- /dev/null
+++ b/services/ollamaservice.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Net.Http.Json;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Text.Json.Serialization;
+using System.Linq;
+
+namespace finder2e_foundry_converter.Services
+{
+ public class OllamaService : ILlmService
+ {
+ private readonly HttpClient _httpClient = new HttpClient();
+
+ public async Task> GetModelsAsync(string baseUrl)
+ {
+ try
+ {
+ var response = await _httpClient.GetFromJsonAsync($"{baseUrl.TrimEnd('/')}/api/tags");
+ if (response?.Models == null) return new List();
+
+ return response.Models.Select(m => m.Name).ToList();
+ }
+ catch
+ {
+ return new List { "Error: Ollama not found" };
+ }
+ }
+
+ public async Task GenerateResponseAsync(string baseUrl, string model, string text, List imagePaths)
+ {
+ try
+ {
+ var images = new List();
+ foreach (var path in imagePaths)
+ {
+ if (string.IsNullOrWhiteSpace(path)) continue;
+ var bytes = await System.IO.File.ReadAllBytesAsync(path);
+ images.Add(Convert.ToBase64String(bytes));
+ }
+
+ var requestBody = new
+ {
+ model = model,
+ messages = new[]
+ {
+ new { role = "user", content = text, images = images.Count > 0 ? images.ToArray() : null }
+ },
+ stream = false
+ };
+
+ var response = await _httpClient.PostAsJsonAsync($"{baseUrl.TrimEnd('/')}/api/chat", requestBody);
+ var body = await response.Content.ReadAsStringAsync();
+
+ using var doc = JsonDocument.Parse(body);
+ if (doc.RootElement.TryGetProperty("error", out var error))
+ {
+ return $"Error: {error.GetString()}";
+ }
+
+ if (doc.RootElement.TryGetProperty("message", out var message))
+ {
+ return message.GetProperty("content").GetString() ?? "No content returned";
+ }
+
+ return "No response returned.";
+ }
+ catch (Exception ex)
+ {
+ return $"Error: {ex.Message}";
+ }
+ }
+
+ private class OllamaModelsResponse
+ {
+ [JsonPropertyName("models")]
+ public List? Models { get; set; }
+ }
+
+ private class OllamaModelData
+ {
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+ }
+ }
+}
diff --git a/ViewLocator.cs b/viewlocator.cs
similarity index 100%
rename from ViewLocator.cs
rename to viewlocator.cs
diff --git a/ViewModels/MainWindowViewModel.cs b/viewmodels/mainwindowviewmodel.cs
similarity index 70%
rename from ViewModels/MainWindowViewModel.cs
rename to viewmodels/mainwindowviewmodel.cs
index 8cb72fa..6fb022b 100644
--- a/ViewModels/MainWindowViewModel.cs
+++ b/viewmodels/mainwindowviewmodel.cs
@@ -14,18 +14,38 @@ namespace finder2e_foundry_converter.ViewModels
{
public partial class MainWindowViewModel : ViewModelBase
{
- private readonly LlmService _llmService = new LlmService();
+ private readonly LmStudioService _lmStudioService = new();
+ private readonly OllamaService _ollamaService = new();
+
+ private ILlmService CurrentService => SelectedProvider == "Ollama" ? (ILlmService)_ollamaService : _lmStudioService;
[ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(CurrentAddress))]
private string _lmStudioAddress = "http://localhost:1234";
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(CurrentAddress))]
+ private string _ollamaAddress = "http://localhost:11434";
+
+ public string CurrentAddress
+ {
+ get => SelectedProvider == "Ollama" ? OllamaAddress : LmStudioAddress;
+ set
+ {
+ if (SelectedProvider == "Ollama") OllamaAddress = value;
+ else LmStudioAddress = value;
+ OnPropertyChanged(nameof(CurrentAddress));
+ }
+ }
+
[ObservableProperty]
private bool _isAddressVisible = false;
[ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(CurrentAddress))]
private string _selectedProvider = "LM Studio";
- public ObservableCollection Providers { get; } = new() { "LM Studio" };
+ public ObservableCollection Providers { get; } = new() { "LM Studio", "Ollama" };
[ObservableProperty]
private string? _selectedModel;
@@ -52,6 +72,13 @@ namespace finder2e_foundry_converter.ViewModels
_ = StartModelRefreshLoop();
}
+ partial void OnSelectedProviderChanged(string value)
+ {
+ Models.Clear();
+ SelectedModel = null;
+ _ = RefreshModels();
+ }
+
[RelayCommand]
private void ToggleAddress()
{
@@ -61,13 +88,7 @@ namespace finder2e_foundry_converter.ViewModels
[RelayCommand]
private async Task RefreshModels()
{
- if (SelectedProvider != "LM Studio")
- {
- IsErrorVisible = false;
- return;
- }
-
- var models = await _llmService.GetModelsAsync(LmStudioAddress);
+ var models = await CurrentService.GetModelsAsync(CurrentAddress);
Models.Clear();
foreach (var m in models)
@@ -85,7 +106,7 @@ namespace finder2e_foundry_converter.ViewModels
}
else
{
- ErrorText = models.FirstOrDefault() ?? "Error connecting to LM Studio";
+ ErrorText = models.FirstOrDefault() ?? $"Error connecting to {SelectedProvider}";
IsErrorVisible = true;
IsAddressVisible = true; // Auto-show on failure
}
@@ -101,7 +122,7 @@ namespace finder2e_foundry_converter.ViewModels
}
StatusMessage = "Generating...";
- var result = await _llmService.GenerateResponseAsync(LmStudioAddress, SelectedModel, Description, ImageItems.Select(i => i.Path).ToList());
+ var result = await CurrentService.GenerateResponseAsync(CurrentAddress, SelectedModel, Description, ImageItems.Select(i => i.Path).ToList());
StatusMessage = result;
}
diff --git a/ViewModels/ViewModelBase.cs b/viewmodels/viewmodelbase.cs
similarity index 100%
rename from ViewModels/ViewModelBase.cs
rename to viewmodels/viewmodelbase.cs
diff --git a/Views/MainWindow.axaml b/views/mainwindow.axaml
similarity index 92%
rename from Views/MainWindow.axaml
rename to views/mainwindow.axaml
index 8207248..e43b243 100644
--- a/Views/MainWindow.axaml
+++ b/views/mainwindow.axaml
@@ -6,7 +6,7 @@
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="600"
x:Class="finder2e_foundry_converter.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
- Icon="/Assets/avalonia-logo.ico"
+ Icon="/assets/avalonia-logo.ico"
Title="Finder2e Foundry Converter"
Width="500" Height="600"
DragDrop.AllowDrop="True">
@@ -30,8 +30,8 @@
-
-
+
+
diff --git a/Views/MainWindow.axaml.cs b/views/mainwindow.axaml.cs
similarity index 100%
rename from Views/MainWindow.axaml.cs
rename to views/mainwindow.axaml.cs