BioGTK  6.7.0
A .NET library & program for annotating, editing various microscopy imaging formats using Bioformats supported images. Including whole slide, pyramidal, and series.
Loading...
Searching...
No Matches
AI.cs
1using BioGTK;
2using GLib;
3using Gtk;
4using Mapsui.Widgets;
5using Pango;
6using System;
7using System.Collections.Generic;
8using System.IO;
9using System.Net.Http.Json;
10using System.Net.NetworkInformation;
11using System.Text;
12using System.Text.Json;
13using System.Text.Json.Nodes;
14using System.Text.Json.Serialization;
15using System;
16using System.Collections.Generic;
17using System.Net.Http;
18using System.Threading;
19using System.Threading.Tasks;
20using Application = Gtk.Application;
21using Task = System.Threading.Tasks.Task;
22using System.Text.RegularExpressions;
23using System.Text.Encodings.Web;
24using System;
25using System.Collections.Generic;
26
27namespace BioGTK
28{
29 public class AI : Window
30 {
31 private static HttpClient client = new HttpClient();
32
33 #region Properties
34
36 private Builder _builder;
37 private string pred = "";
38 private List<string> preds = new List<string>();
39 public static bool onTab = false;
40 public static bool useBioformats = true;
41 public static bool headless = false;
42 public static bool resultInNewTab = false;
43 int line = 0;
44#pragma warning disable 649
45 [Builder.Object]
46 private TextView textBox;
47 [Builder.Object]
48 private TextView consoleBox;
49 [Builder.Object]
50 private Button sendBut;
51#pragma warning restore 649
52
53 #endregion
54
55 #region Constructors / Destructors
56
59 public static AI Create()
60 {
61 Builder builder = new Builder(new FileStream(System.IO.Path.GetDirectoryName(Environment.ProcessPath) + "/Glade/AI.glade", FileMode.Open));
62 return new AI(builder, builder.GetObject("aiConsole").Handle);
63 }
64
68 protected AI(Builder builder, IntPtr handle) : base(handle)
69 {
70 _builder = builder;
71 builder.Autoconnect(this);
73 App.ApplyStyles(this);
74 this.ShowAll();
75 }
76
77 // -----------------------------------------------------
78 // DTOs that match the OpenAI‑compatible payload format
79 // -----------------------------------------------------
80 public sealed record ChatMessage(
81 [property: JsonPropertyName("role")] string Role,
82 [property: JsonPropertyName("content")] string Content);
83
84 public sealed record ChatRequest(
85 [property: JsonPropertyName("model")] string Model,
86 [property: JsonPropertyName("messages")] IReadOnlyList<ChatMessage> Messages,
87 [property: JsonPropertyName("temperature")] double? Temperature = null,
88 [property: JsonPropertyName("max_tokens")] int? MaxTokens = null);
89
90 public sealed record ChatChoice(
91 [property: JsonPropertyName("message")] ChatMessage Message,
92 [property: JsonPropertyName("index")] int Index,
93 [property: JsonPropertyName("finish_reason")] string FinishReason);
94
95 public sealed record ChatResponse(
96 [property: JsonPropertyName("id")] string Id,
97 [property: JsonPropertyName("object")] string Object,
98 [property: JsonPropertyName("created")] long Created,
99 [property: JsonPropertyName("model")] string Model,
100 [property: JsonPropertyName("choices")] IReadOnlyList<ChatChoice> Choices);
101
102 public sealed class OllamaChatService
103 {
104 private readonly HttpClient _httpClient;
105 private readonly JsonSerializerOptions _jsonOptions;
106
107 public OllamaChatService(HttpClient httpClient, string baseUrl = "http://localhost:11434")
108 {
109 _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
110 _httpClient.Timeout = TimeSpan.FromMinutes(60);
111 if (_httpClient.BaseAddress == null)
112 _httpClient.BaseAddress = new Uri(baseUrl);
113
114 // ✅ Use attributes for naming instead of a broken SnakeCase policy
115 _jsonOptions = new JsonSerializerOptions
116 {
117 PropertyNamingPolicy = null, // respect [JsonPropertyName] attributes
118 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
119 WriteIndented = false
120 };
121 }
122
123 public async Task<string> GetResponseAsync(
124 string systemPrompt,
125 string userPrompt,
126 CancellationToken cancellationToken = default)
127 {
128 var request = new ChatRequest(
129 Model: "gpt-oss",
130 Messages: new[]
131 {
132 new ChatMessage("system", systemPrompt),
133 new ChatMessage("user", userPrompt)
134 });
135
136 var json = JsonSerializer.Serialize(request, _jsonOptions);
137 using var content = new StringContent(json, Encoding.UTF8, "application/json");
138
139 using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30));
140 using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
141 cancellationToken, timeoutCts.Token);
142
143 HttpResponseMessage httpResponse;
144 try
145 {
146 httpResponse = await _httpClient.PostAsync(
147 "/v1/chat/completions",
148 content,
149 linkedCts.Token)
150 .ConfigureAwait(false);
151 }
152 catch (Exception e) when (!cancellationToken.IsCancellationRequested)
153 {
154 throw new Exception();
155 }
156
157 if (!httpResponse.IsSuccessStatusCode)
158 {
159 var rawError = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
160 throw new HttpRequestException(
161 $"Server returned {(int)httpResponse.StatusCode} – {httpResponse.ReasonPhrase}. Body: {rawError}");
162 }
163
164 var chatResponse = await httpResponse.Content
165 .ReadFromJsonAsync<ChatResponse>(_jsonOptions, linkedCts.Token)
166 .ConfigureAwait(false);
167
168 var firstChoice = chatResponse?.Choices?.Count > 0 ? chatResponse.Choices[0] : null;
169 if (firstChoice?.Message?.Content == null)
170 throw new InvalidOperationException("The response does not contain a usable message.");
171
172 return firstChoice.Message.Content;
173 }
174 }
175
176
177 // -----------------------------------------------------
178 // Your UI‑layer method (the one you originally called)
179 // -----------------------------------------------------
180 public async Task StartAsync(string args, CancellationToken uiCancel = default)
181 {
182
183 try
184 {
185 var ollama = new OllamaChatService(client);
186 string assistantReply = await ollama.GetResponseAsync(
187 systemPrompt: "You are a C# programmer",
188 userPrompt: args,
189 cancellationToken: uiCancel);
190
191 TextIter tr = textBox.Buffer.StartIter;
192 textBox.Buffer.Insert(ref tr, assistantReply);
193 }
194 catch (TimeoutException tex)
195 {
196 Console.WriteLine($"⌛ Timeout: {tex.Message}");
197 }
198 catch (HttpRequestException hrex)
199 {
200 Console.WriteLine($"❌ Server error: {hrex.Message}");
201 }
202 catch (OperationCanceledException) when (uiCancel.IsCancellationRequested)
203 {
204 Console.WriteLine("⚪ Request cancelled by the user.");
205 }
206 catch (Exception ex)
207 {
208 Console.WriteLine($"❌ Unexpected error: {ex.Message}");
209 }
210 }
211
213 protected void SetupHandlers()
214 {
215 sendBut.ButtonPressEvent += SendBut_ButtonPressEvent;
216 }
217
218 private void SendBut_ButtonPressEvent(object o, ButtonPressEventArgs args)
219 {
220 StartAsync(consoleBox.Buffer.Text);
221 }
222 #endregion
223 }
224}
static AI Create()
Definition AI.cs:59
AI(Builder builder, IntPtr handle)
Specialised constructor for use only by derived class.
Definition AI.cs:68
void SetupHandlers()
Sets up the handlers.
Definition AI.cs:213
Definition ML.cs:17