AuthorBuddy/Services/OllamaService.cs
2026-05-16 16:13:35 +02:00

218 lines
6.6 KiB
C#

using AuthorBuddy.Web.Models;
using System.Text.Json;
namespace AuthorBuddy.Web.Services;
public class OllamaService : IOllamaService
{
private readonly HttpClient _httpClient;
private string _baseUrl = "http://localhost:11434";
public OllamaService()
{
_httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(300) };
}
public void Reconfigure(string url)
{
_baseUrl = url;
}
public async Task<IEnumerable<LLM_Model>> GetLocalModelsAsync()
{
try
{
var response = await _httpClient.GetAsync($"{_baseUrl}/api/tags");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(content);
var root = doc.RootElement;
if (root.TryGetProperty("models", out var models))
{
var modelList = new List<LLM_Model>();
foreach (var model in models.EnumerateArray())
{
modelList.Add(new LLM_Model(model));
}
return modelList;
}
}
return new List<LLM_Model>();
}
catch (Exception)
{
return new List<LLM_Model>();
}
}
public async Task<string> Chat(string model, string userMessage, string? systemPrompt = null)
{
try
{
var request = new
{
model = model,
prompt = userMessage,
stream = false
};
var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}/api/generate", request);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(content);
var root = doc.RootElement;
if (root.TryGetProperty("response", out var responseText))
{
return responseText.GetString() ?? "";
}
}
return "Fehler bei der Verarbeitung.";
}
catch (Exception ex)
{
return $"Error: {ex.Message}";
}
}
public async IAsyncEnumerable<string> ChatStream(string model, string userMessage, string? systemPrompt = null)
{
var request = new
{
model = model,
prompt = userMessage,
stream = true
};
HttpResponseMessage? response = null;
try
{
response = await _httpClient.PostAsync(
$"{_baseUrl}/api/generate",
new StringContent(JsonSerializer.Serialize(request), System.Text.Encoding.UTF8, "application/json")
);
if (response.IsSuccessStatusCode)
{
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
var text = ExtractResponseText(line);
if (!string.IsNullOrEmpty(text))
{
yield return text;
}
}
}
}
finally
{
response?.Dispose();
}
}
private string ExtractResponseText(string jsonLine)
{
if (string.IsNullOrWhiteSpace(jsonLine))
return string.Empty;
try
{
using var doc = JsonDocument.Parse(jsonLine);
var root = doc.RootElement;
if (root.TryGetProperty("response", out var responseText))
{
return responseText.GetString() ?? string.Empty;
}
}
catch { }
return string.Empty;
}
public async Task<bool> DeleteModel(string modelName)
{
try
{
var request = new HttpRequestMessage(HttpMethod.Delete, $"{_baseUrl}/api/delete");
request.Content = new StringContent(JsonSerializer.Serialize(new { name = modelName }),
System.Text.Encoding.UTF8, "application/json");
var response = await _httpClient.SendAsync(request);
return response.IsSuccessStatusCode;
}
catch (Exception)
{
return false;
}
}
public async Task<bool> PullModelAsync(string modelName, IProgress<string> progress)
{
try
{
var request = new { name = modelName, stream = true };
var response = await _httpClient.PostAsync(
$"{_baseUrl}/api/pull",
new StringContent(JsonSerializer.Serialize(request), System.Text.Encoding.UTF8, "application/json")
);
if (response.IsSuccessStatusCode)
{
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
if (string.IsNullOrWhiteSpace(line)) continue;
try
{
using var doc = JsonDocument.Parse(line);
var root = doc.RootElement;
if (root.TryGetProperty("status", out var status))
{
var statusText = status.GetString() ?? "";
var msg = statusText;
if (root.TryGetProperty("completed", out var completed) &&
root.TryGetProperty("total", out var total))
{
var comp = completed.GetInt64();
var tot = total.GetInt64();
if (tot > 0)
{
var pct = (int)(comp * 100 / tot);
msg = $"{statusText}: {comp}/{tot} ({pct}%)";
}
}
progress?.Report(msg);
if (statusText == "success")
return true;
}
}
catch { }
}
}
return false;
}
catch (Exception)
{
return false;
}
}
}