using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; namespace AI.Client { public class AiClient { private readonly HttpClient _httpClient; private readonly string _baseUrl; public AiClient(string baseUrl = "http://localhost:8080/api") { _baseUrl = baseUrl.TrimEnd('/'); _httpClient = new HttpClient(); _httpClient.Timeout = TimeSpan.FromMinutes(15); // Increased to 15 minutes for long generations } // --- CORE COMMANDS --- public async Task> GetInstancesAsync() { return await GetJsonAsync>("/admin/instances"); } public async Task SendPromptAsync(string text, string instanceId = null) { var payload = new { prompt = text, blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/inject", payload); } public async Task NewChatAsync(string instanceId = null) { var payload = new { blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/new_chat", payload); } public async Task ToggleUrlContextAsync(bool enabled, string instanceId = null) { var payload = new { enabled = enabled, blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/toggle_url_context", payload); } public async Task SetResolutionAsync(string level, string instanceId = null) { var payload = new { level = level, blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/resolution", payload); } public async Task DeleteTurnAsync(int index, string instanceId = null) { var payload = new { index = index, blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/delete", payload); } // --- ADVANCED COMMANDS --- public async Task SetSettingsAsync(string instanceId, float? temp, float? topP, string model, string sysInst, string thinkingLevel) { // Use a dictionary to avoid sending null values which might confuse the backend var payload = new Dictionary(); if (!string.IsNullOrEmpty(instanceId)) payload["instance_id"] = instanceId; if (temp.HasValue) payload["temperature"] = temp.Value; if (topP.HasValue) payload["top_p"] = topP.Value; if (!string.IsNullOrEmpty(model)) payload["model"] = model; if (!string.IsNullOrEmpty(sysInst)) payload["system_instructions"] = sysInst; if (!string.IsNullOrEmpty(thinkingLevel)) payload["thinking_level"] = thinkingLevel; payload["blocking"] = true; return await PostJsonAsync("/admin/action/settings", payload); } public async Task GetSettingsAsync(string instanceId = null) { var payload = new { blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/get_settings", payload); } public async Task GetChatHistoryAsync(string instanceId = null, bool forceSync = false) { var payload = new { blocking = true, instance_id = instanceId, force_sync = forceSync }; return await PostJsonAsync("/admin/action/get_chat_history", payload); } public async Task CreateShardAsync(string instanceId = null) { var payload = new { blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/create_shard", payload); } public async Task InjectShardAsync(string shardData, string instanceId = null) { var payload = new { shard_data = shardData, blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/inject_shard", payload); } public async Task InjectDocAsync(string filename, string content, string instanceId = null) { var payload = new { filename = filename, content = content, blocking = true, instance_id = instanceId }; return await PostJsonAsync("/admin/action/inject_doc", payload); } public async Task SetOpenAISystemInstructionsAsync(string instructions) { var payload = new { instructions = instructions }; return await PostJsonAsync("/admin/openai/system_instructions", payload); } // --- INTERNAL HELPERS --- private async Task GetJsonAsync(string endpoint) { try { var response = await _httpClient.GetAsync(_baseUrl + endpoint); response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync(); var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; return JsonSerializer.Deserialize(responseString, options); } catch (Exception ex) { throw new Exception($"API GET Failed: {ex.Message}", ex); } } private async Task PostJsonAsync(string endpoint, object data) { try { var json = JsonSerializer.Serialize(data); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(_baseUrl + endpoint, content); response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync(); var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; return JsonSerializer.Deserialize(responseString, options); } catch (Exception ex) { throw new Exception($"API POST Failed: {ex.Message}", ex); } } } // --- DATA MODELS --- public class AiResponse { public string Status { get; set; } public string Task_Id { get; set; } public string Instance_Id { get; set; } public string Mode { get; set; } public string Output { get; set; } public string Error { get; set; } } public class InstanceState { public string last_seen { get; set; } public string url { get; set; } public int turn_count { get; set; } public bool is_busy { get; set; } } public class AiSettings { public float? Temperature { get; set; } public float? Top_P { get; set; } public string Model { get; set; } public string Thinking_Level { get; set; } public string System_Instructions { get; set; } public string Resolution { get; set; } public bool? Url_Context { get; set; } } public class AiChatTurn { public string Role { get; set; } public string Text { get; set; } } }