Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/anabkl_sahtek.tech.json
T

1 line
49 KiB
JSON

{"repo": "anabkl/sahtek.tech", "files": {"backend/Services/GeminiService.cs": {"b8cda8ae899fdc409800c43e9435d59a9cfa698b": {"t": 1785745796.8053398, "n": 4114, "h": "360dfbe06a3251dc", "c": "using System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing Microsoft.Extensions.Logging;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private readonly HttpClient _httpClient;\n private readonly ILogger<GeminiService> _logger;\n private readonly string? _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"You are 'Sahtek', a friendly Moroccan AI assistant. Always greet back warmly in Darija if the user says hello (salam, cv, etc). Never reply with robotic fallback text.\";\n\n public GeminiService(HttpClient httpClient, ILogger<GeminiService> logger)\n {\n _httpClient = httpClient;\n _logger = logger;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\");\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n if (string.IsNullOrWhiteSpace(_apiKey))\n {\n _logger.LogError(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n throw new InvalidOperationException(\"AI service is not configured. Please contact support.\");\n }\n\n var trimmedMessage = request.Message?.Trim();\n if (string.IsNullOrWhiteSpace(trimmedMessage))\n {\n throw new InvalidOperationException(\"User message cannot be empty.\");\n }\n\n var payload = new\n {\n model = Model,\n messages = new[]\n {\n new { role = \"system\", content = SystemPrompt },\n new { role = \"user\", content = trimmedMessage }\n }\n };\n\n using var httpRequest = new HttpRequestMessage(HttpMethod.Post, Endpoint)\n {\n Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, \"application/json\")\n };\n httpRequest.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n\n HttpResponseMessage response;\n string json;\n try\n {\n response = await _httpClient.SendAsync(httpRequest, cancellationToken);\n json = await response.Content.ReadAsStringAsync(cancellationToken);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"HTTP request to DeepSeek API failed. InnerException: {Inner}\", ex.InnerException?.Message);\n throw;\n }\n\n if (!response.IsSuccessStatusCode)\n {\n _logger.LogError(\"DeepSeek API returned HTTP {Status}. Body: {Body}\", (int)response.StatusCode, json);\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n var aiText = ExtractText(json, _logger);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(string json, ILogger logger)\n {\n try\n {\n var node = JsonNode.Parse(json);\n var content = node?[\"choices\"]?[0]?[\"message\"]?[\"content\"]?.GetValue<string>();\n if (!string.IsNullOrWhiteSpace(content))\n {\n return content;\n }\n\n logger.LogError(\"DeepSeek API returned unexpected response shape. Raw JSON: {Json}\", json);\n throw new InvalidOperationException(\"DeepSeek API returned an unexpected response shape.\");\n }\n catch (Exception ex) when (ex is not InvalidOperationException)\n {\n logger.LogError(ex, \"Failed to parse DeepSeek API response. InnerException: {Inner}. Raw JSON: {Json}\",\n ex.InnerException?.Message, json);\n throw new InvalidOperationException(\"Failed to parse the AI service response.\", ex);\n }\n }\n}\n"}, "0e272237c24cbff33d5bca44829df24b73215da3": {"t": 1785756581.8657792, "n": 4114, "h": "360dfbe06a3251dc", "c": "using System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing Microsoft.Extensions.Logging;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private readonly HttpClient _httpClient;\n private readonly ILogger<GeminiService> _logger;\n private readonly string? _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"You are 'Sahtek', a friendly Moroccan AI assistant. Always greet back warmly in Darija if the user says hello (salam, cv, etc). Never reply with robotic fallback text.\";\n\n public GeminiService(HttpClient httpClient, ILogger<GeminiService> logger)\n {\n _httpClient = httpClient;\n _logger = logger;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\");\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n if (string.IsNullOrWhiteSpace(_apiKey))\n {\n _logger.LogError(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n throw new InvalidOperationException(\"AI service is not configured. Please contact support.\");\n }\n\n var trimmedMessage = request.Message?.Trim();\n if (string.IsNullOrWhiteSpace(trimmedMessage))\n {\n throw new InvalidOperationException(\"User message cannot be empty.\");\n }\n\n var payload = new\n {\n model = Model,\n messages = new[]\n {\n new { role = \"system\", content = SystemPrompt },\n new { role = \"user\", content = trimmedMessage }\n }\n };\n\n using var httpRequest = new HttpRequestMessage(HttpMethod.Post, Endpoint)\n {\n Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, \"application/json\")\n };\n httpRequest.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n\n HttpResponseMessage response;\n string json;\n try\n {\n response = await _httpClient.SendAsync(httpRequest, cancellationToken);\n json = await response.Content.ReadAsStringAsync(cancellationToken);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"HTTP request to DeepSeek API failed. InnerException: {Inner}\", ex.InnerException?.Message);\n throw;\n }\n\n if (!response.IsSuccessStatusCode)\n {\n _logger.LogError(\"DeepSeek API returned HTTP {Status}. Body: {Body}\", (int)response.StatusCode, json);\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n var aiText = ExtractText(json, _logger);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(string json, ILogger logger)\n {\n try\n {\n var node = JsonNode.Parse(json);\n var content = node?[\"choices\"]?[0]?[\"message\"]?[\"content\"]?.GetValue<string>();\n if (!string.IsNullOrWhiteSpace(content))\n {\n return content;\n }\n\n logger.LogError(\"DeepSeek API returned unexpected response shape. Raw JSON: {Json}\", json);\n throw new InvalidOperationException(\"DeepSeek API returned an unexpected response shape.\");\n }\n catch (Exception ex) when (ex is not InvalidOperationException)\n {\n logger.LogError(ex, \"Failed to parse DeepSeek API response. InnerException: {Inner}. Raw JSON: {Json}\",\n ex.InnerException?.Message, json);\n throw new InvalidOperationException(\"Failed to parse the AI service response.\", ex);\n }\n }\n}\n"}, "e67b9a378b679decd74b2ee96a0a54b2bebcf457": {"t": 1785756583.0934517, "n": 4047, "h": "a674efef793ce090", "c": "using System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing Microsoft.Extensions.Logging;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private readonly HttpClient _httpClient;\n private readonly ILogger<GeminiService> _logger;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"You are 'Sahtek', a friendly Moroccan AI assistant. Always greet back warmly in Darija if the user says hello (salam, cv, etc). Never reply with robotic fallback text.\";\n\n public GeminiService(HttpClient httpClient, ILogger<GeminiService> logger)\n {\n _httpClient = httpClient;\n _logger = logger;\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\");\n if (string.IsNullOrWhiteSpace(apiKey))\n {\n _logger.LogError(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n throw new InvalidOperationException(\"AI service is not configured. Please contact support.\");\n }\n\n var trimmedMessage = request.Message?.Trim();\n if (string.IsNullOrWhiteSpace(trimmedMessage))\n {\n throw new InvalidOperationException(\"User message cannot be empty.\");\n }\n\n var payload = new\n {\n model = Model,\n messages = new[]\n {\n new { role = \"system\", content = SystemPrompt },\n new { role = \"user\", content = trimmedMessage }\n }\n };\n\n using var httpRequest = new HttpRequestMessage(HttpMethod.Post, Endpoint)\n {\n Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, \"application/json\")\n };\n httpRequest.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", apiKey);\n\n HttpResponseMessage response;\n string json;\n try\n {\n response = await _httpClient.SendAsync(httpRequest, cancellationToken);\n json = await response.Content.ReadAsStringAsync(cancellationToken);\n }\n catch (Exception ex)\n {\n _logger.LogError(ex, \"HTTP request to DeepSeek API failed. InnerException: {Inner}\", ex.InnerException?.Message);\n throw;\n }\n\n if (!response.IsSuccessStatusCode)\n {\n _logger.LogError(\"DeepSeek API returned HTTP {Status}. Body: {Body}\", (int)response.StatusCode, json);\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n var aiText = ExtractText(json);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private string ExtractText(string json)\n {\n try\n {\n var node = JsonNode.Parse(json);\n var content = node?[\"choices\"]?[0]?[\"message\"]?[\"content\"]?.GetValue<string>();\n if (!string.IsNullOrWhiteSpace(content))\n {\n return content;\n }\n\n _logger.LogError(\"DeepSeek API returned unexpected response shape. Raw JSON: {Json}\", json);\n throw new InvalidOperationException(\"DeepSeek API returned an unexpected response shape.\");\n }\n catch (Exception ex) when (ex is not InvalidOperationException)\n {\n _logger.LogError(ex, \"Failed to parse DeepSeek API response. InnerException: {Inner}. Raw JSON: {Json}\",\n ex.InnerException?.Message, json);\n throw new InvalidOperationException(\"Failed to parse the AI service response.\", ex);\n }\n }\n}\n"}, "5a7781dd1192f39070d3dbd5e5361bef0025a175": {"t": 1785756584.5109384, "n": 3789, "h": "363bb93edbe15cb5", "c": "using System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private sealed record DeepSeekMessage(\n [property: JsonPropertyName(\"role\")] string Role,\n [property: JsonPropertyName(\"content\")] string Content\n );\n\n private sealed record DeepSeekRequest(\n [property: JsonPropertyName(\"model\")] string Model,\n [property: JsonPropertyName(\"messages\")] List<DeepSeekMessage> Messages\n );\n\n private readonly HttpClient _httpClient;\n private readonly string _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"You are 'Sahtek', a friendly Moroccan AI assistant. Always greet back warmly in Darija if the user says hello (salam, cv, etc). Never reply with robotic fallback text.\";\n\n public GeminiService(HttpClient httpClient)\n {\n _httpClient = httpClient;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\")\n ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var trimmedMessage = request.Message?.Trim();\n if (string.IsNullOrWhiteSpace(trimmedMessage))\n {\n throw new InvalidOperationException(\"User message cannot be empty.\");\n }\n\n var payload = new DeepSeekRequest(\n Model,\n [\n new DeepSeekMessage(\"system\", SystemPrompt),\n new DeepSeekMessage(\"user\", trimmedMessage)\n ]\n );\n\n using var httpRequest = new HttpRequestMessage(HttpMethod.Post, Endpoint)\n {\n Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, \"application/json\")\n };\n\n var response = await _httpClient.SendAsync(httpRequest, cancellationToken);\n var json = await response.Content.ReadAsStringAsync(cancellationToken);\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n using var jsonDoc = JsonDocument.Parse(json);\n var aiText = ExtractText(jsonDoc.RootElement);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(JsonElement root)\n {\n if (!root.TryGetProperty(\"choices\", out var choices) ||\n choices.ValueKind != JsonValueKind.Array ||\n choices.GetArrayLength() == 0)\n {\n throw new InvalidOperationException(\"DeepSeek API returned no choices.\");\n }\n\n var firstChoice = choices[0];\n if (!firstChoice.TryGetProperty(\"message\", out var messageElement) ||\n !messageElement.TryGetProperty(\"content\", out var contentElement))\n {\n throw new InvalidOperationException(\"DeepSeek API returned an invalid response shape.\");\n }\n\n var aiText = contentElement.GetString();\n if (!string.IsNullOrWhiteSpace(aiText))\n {\n return aiText;\n }\n\n throw new InvalidOperationException(\"DeepSeek API returned an empty text response.\");\n }\n\n}\n"}, "2c67d301a093e05b5451590b8ec3c19e9d737017": {"t": 1785756585.8984034, "n": 3883, "h": "37674eaac8dbf9c1", "c": "using System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private sealed record DeepSeekMessage(\n [property: JsonPropertyName(\"role\")] string Role,\n [property: JsonPropertyName(\"content\")] string Content\n );\n\n private sealed record DeepSeekRequest(\n [property: JsonPropertyName(\"model\")] string Model,\n [property: JsonPropertyName(\"messages\")] List<DeepSeekMessage> Messages\n );\n\n private readonly HttpClient _httpClient;\n private readonly string _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"You are 'Sahtek', a friendly Moroccan AI assistant. Always greet back warmly in Darija if the user says hello (salam, cv, etc). Never reply with robotic fallback text.\";\n\n public GeminiService(HttpClient httpClient)\n {\n _httpClient = httpClient;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\")\n ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var trimmedMessage = request.Message?.Trim();\n if (string.IsNullOrWhiteSpace(trimmedMessage))\n {\n throw new InvalidOperationException(\"User message cannot be empty.\");\n }\n\n var payload = new DeepSeekRequest(\n Model,\n [\n new DeepSeekMessage(\"system\", SystemPrompt),\n new DeepSeekMessage(\"user\", trimmedMessage)\n ]\n );\n\n using var httpRequest = new HttpRequestMessage(HttpMethod.Post, Endpoint)\n {\n Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, \"application/json\")\n };\n httpRequest.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n\n var response = await _httpClient.SendAsync(httpRequest, cancellationToken);\n var json = await response.Content.ReadAsStringAsync(cancellationToken);\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n using var jsonDoc = JsonDocument.Parse(json);\n var aiText = ExtractText(jsonDoc.RootElement);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(JsonElement root)\n {\n if (!root.TryGetProperty(\"choices\", out var choices) ||\n choices.ValueKind != JsonValueKind.Array ||\n choices.GetArrayLength() == 0)\n {\n throw new InvalidOperationException(\"DeepSeek API returned no choices.\");\n }\n\n var firstChoice = choices[0];\n if (!firstChoice.TryGetProperty(\"message\", out var messageElement) ||\n !messageElement.TryGetProperty(\"content\", out var contentElement))\n {\n throw new InvalidOperationException(\"DeepSeek API returned an invalid response shape.\");\n }\n\n var aiText = contentElement.GetString();\n if (!string.IsNullOrWhiteSpace(aiText))\n {\n return aiText;\n }\n\n throw new InvalidOperationException(\"DeepSeek API returned an empty text response.\");\n }\n\n}\n"}, "7bf913b8cb905fa13d15b709be6b4c0656d4a767": {"t": 1785756587.0792608, "n": 5274, "h": "1dcd7fd9698c0a5d", "c": "using System.Net.Http.Json;\nusing System.Net.Http.Headers;\nusing System.Text.Json;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private sealed record DeepSeekMessage(string role, string content);\n\n private readonly HttpClient _httpClient;\n private readonly string _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"\"\"\n نتي \"صحّتك\" (Sahtek)، مساعدة ذكاء اصطناعي ودودة وإنسانية كتدعم التوعية بصحة الثدي عند النساء.\n\n توجيهات أساسية:\n 1) إلى كان المستخدم غير سلّم عليك ولا سَوّل \"كيف دايرة؟\" أو جاب كلام اجتماعي بسيط، ردي عليه برد دافئ بالدارجة المغربية وسوّليه كيفاش تقدري تعاونيه بخصوص صحة الثدي اليوم.\n 2) ما تدخليش مباشرة فخطاب طبي طويل إلا كان غير ترحيب. خليه رد طبيعي، بشري، ومهني.\n 3) منين السؤال يكون فعلاً على صحة الثدي، جاوبي بمعلومات توعوية واضحة على الأعراض، الفحص الذاتي، الوقاية، التشخيص المبكر، والدعم النفسي.\n 4) إذا كان الطلب خارج السياق الطبي ديال صحة الثدي بشكل واضح، اعتذري بلطف ووجّهي النقاش لتوعية صحة الثدي بلا تكرار آلي.\n 5) ما تعطيش تشخيص طبي نهائي، ونبهي دائماً أن الطبيب المختص هو المرجع للحالات الشخصية.\n 6) جاوبي حصرياً بالدارجة المغربية، بأسلوب متعاطف، متوازن، وتفاعلي. تجنبي أي رد روبوتي متكرر أو قالب ثابت.\n 7) خلي الرد موجز ومفيد (تقريباً 40-160 كلمة) ويمكن استعمال نقاط عند الحاجة.\n \"\"\";\n\n public GeminiService(HttpClient httpClient)\n {\n _httpClient = httpClient;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\")\n ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var trimmedMessage = request.Message.Trim();\n var messages = new List<DeepSeekMessage>\n {\n new(\"system\", SystemPrompt)\n };\n\n var history = (request.ConversationHistory ?? [])\n .Where(msg => !string.IsNullOrWhiteSpace(msg.Content))\n .Select(msg => new DeepSeekMessage(\n NormalizeRole(msg.Role),\n msg.Content.Trim()))\n // Keep a short but slightly broader context window to preserve recent flow\n // (including greetings + follow-up question) without large token growth.\n .TakeLast(12)\n .ToList();\n\n foreach (var msg in history)\n {\n // Guard against repeated adjacent turns that can cause loop-like model replies.\n if (messages.Count > 0 &&\n messages[^1].role == msg.role &&\n messages[^1].content == msg.content)\n {\n continue;\n }\n\n messages.Add(msg);\n }\n\n var alreadyIncludedAsLastUserMessage =\n history.Count > 0 &&\n history[^1].role == \"user\" &&\n string.Equals(history[^1].content, trimmedMessage, StringComparison.Ordinal);\n\n if (!alreadyIncludedAsLastUserMessage)\n {\n messages.Add(new(\"user\", trimmedMessage));\n }\n\n var payload = new\n {\n model = Model,\n messages,\n temperature = 0.4\n };\n\n var response = await _httpClient.PostAsJsonAsync(Endpoint, payload, cancellationToken);\n var json = await response.Content.ReadAsStringAsync(cancellationToken);\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n using var jsonDoc = JsonDocument.Parse(json);\n var aiText = ExtractText(jsonDoc.RootElement);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(JsonElement root)\n {\n if (!root.TryGetProperty(\"choices\", out var choices) ||\n choices.ValueKind != JsonValueKind.Array ||\n choices.GetArrayLength() == 0)\n {\n throw new InvalidOperationException(\"DeepSeek API returned no choices.\");\n }\n\n var firstChoice = choices[0];\n if (!firstChoice.TryGetProperty(\"message\", out var messageElement) ||\n !messageElement.TryGetProperty(\"content\", out var contentElement))\n {\n throw new InvalidOperationException(\"DeepSeek API returned an invalid response shape.\");\n }\n\n var aiText = contentElement.GetString();\n if (!string.IsNullOrWhiteSpace(aiText))\n {\n return aiText;\n }\n\n throw new InvalidOperationException(\"DeepSeek API returned an empty text response.\");\n }\n\n private static string NormalizeRole(string? role)\n => string.Equals(role, \"assistant\", StringComparison.OrdinalIgnoreCase) ? \"assistant\" : \"user\";\n}\n"}, "7f8b72f580e1d9b04966a31980a626aafe303dc2": {"t": 1785756588.4533603, "n": 5100, "h": "356380483168cf38", "c": "using System.Net.Http.Json;\nusing System.Net.Http.Headers;\nusing System.Text.Json;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private sealed record DeepSeekMessage(string role, string content);\n\n private readonly HttpClient _httpClient;\n private readonly string _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"\"\"\n نتي \"صحّتك\" (Sahtek)، مساعدة ذكاء اصطناعي ودودة وإنسانية كتدعم التوعية بصحة الثدي عند النساء.\n\n توجيهات أساسية:\n 1) إلى كان المستخدم غير سلّم عليك ولا سَوّل \"كيف دايرة؟\" أو جاب كلام اجتماعي بسيط، ردي عليه برد دافئ بالدارجة المغربية وسوّليه كيفاش تقدري تعاونيه بخصوص صحة الثدي اليوم.\n 2) ما تدخليش مباشرة فخطاب طبي طويل إلا كان غير ترحيب. خليه رد طبيعي، بشري، ومهني.\n 3) منين السؤال يكون فعلاً على صحة الثدي، جاوبي بمعلومات توعوية واضحة على الأعراض، الفحص الذاتي، الوقاية، التشخيص المبكر، والدعم النفسي.\n 4) إذا كان الطلب خارج السياق الطبي ديال صحة الثدي بشكل واضح، اعتذري بلطف ووجّهي النقاش لتوعية صحة الثدي بلا تكرار آلي.\n 5) ما تعطيش تشخيص طبي نهائي، ونبهي دائماً أن الطبيب المختص هو المرجع للحالات الشخصية.\n 6) جاوبي حصرياً بالدارجة المغربية، بأسلوب متعاطف، متوازن، وتفاعلي. تجنبي أي رد روبوتي متكرر أو قالب ثابت.\n 7) خلي الرد موجز ومفيد (تقريباً 40-160 كلمة) ويمكن استعمال نقاط عند الحاجة.\n \"\"\";\n\n public GeminiService(HttpClient httpClient)\n {\n _httpClient = httpClient;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\")\n ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var trimmedMessage = request.Message.Trim();\n var messages = new List<DeepSeekMessage>\n {\n new(\"system\", SystemPrompt)\n };\n\n var history = (request.ConversationHistory ?? [])\n .Where(msg => !string.IsNullOrWhiteSpace(msg.Content))\n .Select(msg => new DeepSeekMessage(\n NormalizeRole(msg.Role),\n msg.Content.Trim()))\n .TakeLast(12)\n .ToList();\n\n foreach (var msg in history)\n {\n // Guard against repeated adjacent turns that can cause loop-like model replies.\n if (messages.Count > 0 &&\n messages[^1].role == msg.role &&\n messages[^1].content == msg.content)\n {\n continue;\n }\n\n messages.Add(msg);\n }\n\n var alreadyIncludedAsLastUserMessage =\n history.Count > 0 &&\n history[^1].role == \"user\" &&\n string.Equals(history[^1].content, trimmedMessage, StringComparison.Ordinal);\n\n if (!alreadyIncludedAsLastUserMessage)\n {\n messages.Add(new(\"user\", trimmedMessage));\n }\n\n var payload = new\n {\n model = Model,\n messages,\n temperature = 0.4\n };\n\n var response = await _httpClient.PostAsJsonAsync(Endpoint, payload, cancellationToken);\n var json = await response.Content.ReadAsStringAsync(cancellationToken);\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n using var jsonDoc = JsonDocument.Parse(json);\n var aiText = ExtractText(jsonDoc.RootElement);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(JsonElement root)\n {\n if (!root.TryGetProperty(\"choices\", out var choices) ||\n choices.ValueKind != JsonValueKind.Array ||\n choices.GetArrayLength() == 0)\n {\n throw new InvalidOperationException(\"DeepSeek API returned no choices.\");\n }\n\n var firstChoice = choices[0];\n if (!firstChoice.TryGetProperty(\"message\", out var messageElement) ||\n !messageElement.TryGetProperty(\"content\", out var contentElement))\n {\n throw new InvalidOperationException(\"DeepSeek API returned an invalid response shape.\");\n }\n\n var aiText = contentElement.GetString();\n if (!string.IsNullOrWhiteSpace(aiText))\n {\n return aiText;\n }\n\n throw new InvalidOperationException(\"DeepSeek API returned an empty text response.\");\n }\n\n private static string NormalizeRole(string? role)\n => string.Equals(role, \"assistant\", StringComparison.OrdinalIgnoreCase) ? \"assistant\" : \"user\";\n}\n"}, "5c80ea30a54d0419f027f0fc9dc7188eddd84701": {"t": 1785756589.7253313, "n": 4689, "h": "4de86906434ccf82", "c": "using System.Net.Http.Json;\nusing System.Net.Http.Headers;\nusing System.Text.Json;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private sealed record DeepSeekMessage(string role, string content);\n\n private readonly HttpClient _httpClient;\n private readonly string _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"\"\"\n نتي \"صحّتك\" (Sahtek)، مساعدة ذكاء اصطناعي للتوعية بسرطان الثدي فقط.\n\n قواعد صارمة:\n 1) جاوبي غير على مواضيع سرطان الثدي، الفحص الذاتي، الوقاية، الأعراض، التشخيص المبكر، والدعم النفسي.\n 2) إلا كان السؤال خارج هاد النطاق، اعتذري بلطف وقولي بلي اختصاصك هو التوعية بسرطان الثدي.\n 3) ما تعطيش تشخيص طبي نهائياً، ودائماً نبهي المستخدم يتواصل مع طبيب.\n 4) ردودك خاصها تكون إنسانية، مطمئنة، وبسيطة.\n 5) جاوبي حصرياً وبشكل كامل بالدارجة المغربية فقط، وما تستعمليش الفرنسية، الإنجليزية، أو العربية الفصحى.\n 6) الرد يكون واضح ومختصر (تقريباً بين 50 و 180 كلمة) ومع نقاط إلا اِحتاج الأمر.\n \"\"\";\n\n public GeminiService(HttpClient httpClient)\n {\n _httpClient = httpClient;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\")\n ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var trimmedMessage = request.Message.Trim();\n var messages = new List<DeepSeekMessage>\n {\n new(\"system\", SystemPrompt)\n };\n\n var history = (request.ConversationHistory ?? [])\n .Where(msg => !string.IsNullOrWhiteSpace(msg.Content))\n .Select(msg => new DeepSeekMessage(\n string.Equals(msg.Role, \"assistant\", StringComparison.OrdinalIgnoreCase) ? \"assistant\" : \"user\",\n msg.Content.Trim()))\n .TakeLast(10)\n .ToList();\n\n foreach (var msg in history)\n {\n // Guard against repeated adjacent turns that can cause loop-like model replies.\n if (messages.Count > 0 &&\n messages[^1].role == msg.role &&\n messages[^1].content == msg.content)\n {\n continue;\n }\n\n messages.Add(msg);\n }\n\n var alreadyIncludedAsLastUserMessage =\n history.Count > 0 &&\n history[^1].role == \"user\" &&\n string.Equals(history[^1].content, trimmedMessage, StringComparison.Ordinal);\n\n if (!alreadyIncludedAsLastUserMessage)\n {\n messages.Add(new(\"user\", trimmedMessage));\n }\n\n var payload = new\n {\n model = Model,\n messages,\n temperature = 0.4\n };\n\n var response = await _httpClient.PostAsJsonAsync(Endpoint, payload, cancellationToken);\n var json = await response.Content.ReadAsStringAsync(cancellationToken);\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n using var jsonDoc = JsonDocument.Parse(json);\n var aiText = ExtractText(jsonDoc.RootElement);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(JsonElement root)\n {\n if (!root.TryGetProperty(\"choices\", out var choices) ||\n choices.ValueKind != JsonValueKind.Array ||\n choices.GetArrayLength() == 0)\n {\n throw new InvalidOperationException(\"DeepSeek API returned no choices.\");\n }\n\n var firstChoice = choices[0];\n if (!firstChoice.TryGetProperty(\"message\", out var messageElement) ||\n !messageElement.TryGetProperty(\"content\", out var contentElement))\n {\n throw new InvalidOperationException(\"DeepSeek API returned an invalid response shape.\");\n }\n\n var aiText = contentElement.GetString();\n if (!string.IsNullOrWhiteSpace(aiText))\n {\n return aiText;\n }\n\n throw new InvalidOperationException(\"DeepSeek API returned an empty text response.\");\n }\n}\n"}, "d033f073f5637cca2a3313c1fc25abc14b7f807b": {"t": 1785756591.3378704, "n": 4595, "h": "32e75737d6956870", "c": "using System.Net.Http.Json;\nusing System.Net.Http.Headers;\nusing System.Text.Json;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private sealed record DeepSeekMessage(string role, string content);\n\n private readonly HttpClient _httpClient;\n private readonly string _apiKey;\n\n private const string Endpoint = \"https://api.deepseek.com/chat/completions\";\n private const string Model = \"deepseek-chat\";\n\n private const string SystemPrompt = \"\"\"\n نتي \"صحّتك\" (Sahtek)، مساعدة ذكاء اصطناعي للتوعية بسرطان الثدي فقط.\n\n قواعد صارمة:\n 1) جاوبي غير على مواضيع سرطان الثدي، الفحص الذاتي، الوقاية، الأعراض، التشخيص المبكر، والدعم النفسي.\n 2) إلا كان السؤال خارج هاد النطاق، اعتذري بلطف وقولي بلي اختصاصك هو التوعية بسرطان الثدي.\n 3) ما تعطيش تشخيص طبي نهائياً، ودائماً نبهي المستخدم يتواصل مع طبيب.\n 4) ردودك خاصها تكون إنسانية، مطمئنة، وبسيطة.\n 5) جاوبي حصرياً وبشكل كامل بالدارجة المغربية فقط، وما تستعمليش الفرنسية، الإنجليزية، أو العربية الفصحى.\n 6) الرد يكون واضح ومختصر (تقريباً بين 50 و 180 كلمة) ومع نقاط إلا احتاج الأمر.\n \"\"\";\n\n public GeminiService(HttpClient httpClient)\n {\n _httpClient = httpClient;\n _apiKey = Environment.GetEnvironmentVariable(\"DEEPSEEK_API_KEY\")\n ?? throw new InvalidOperationException(\"DEEPSEEK_API_KEY environment variable is not configured.\");\n _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", _apiKey);\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var trimmedMessage = request.Message.Trim();\n var messages = new List<DeepSeekMessage>\n {\n new(\"system\", SystemPrompt)\n };\n\n var history = (request.ConversationHistory ?? [])\n .Where(msg => !string.IsNullOrWhiteSpace(msg.Content))\n .Select(msg => new DeepSeekMessage(\n string.Equals(msg.Role, \"assistant\", StringComparison.OrdinalIgnoreCase) ? \"assistant\" : \"user\",\n msg.Content.Trim()))\n .TakeLast(12)\n .ToList();\n\n foreach (var msg in history)\n {\n if (messages.Count > 0 &&\n messages[^1].role == msg.role &&\n messages[^1].content == msg.content)\n {\n continue;\n }\n\n messages.Add(msg);\n }\n\n var alreadyIncludedAsLastUserMessage =\n history.Count > 0 &&\n history[^1].role == \"user\" &&\n string.Equals(history[^1].content, trimmedMessage, StringComparison.Ordinal);\n\n if (!alreadyIncludedAsLastUserMessage)\n {\n messages.Add(new(\"user\", trimmedMessage));\n }\n\n var payload = new\n {\n model = Model,\n messages,\n temperature = 0.4\n };\n\n var response = await _httpClient.PostAsJsonAsync(Endpoint, payload, cancellationToken);\n var json = await response.Content.ReadAsStringAsync(cancellationToken);\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"DeepSeek API request failed with status {(int)response.StatusCode}.\");\n }\n\n using var jsonDoc = JsonDocument.Parse(json);\n var aiText = ExtractText(jsonDoc.RootElement);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(JsonElement root)\n {\n if (!root.TryGetProperty(\"choices\", out var choices) ||\n choices.ValueKind != JsonValueKind.Array ||\n choices.GetArrayLength() == 0)\n {\n throw new InvalidOperationException(\"DeepSeek API returned no choices.\");\n }\n\n var firstChoice = choices[0];\n if (!firstChoice.TryGetProperty(\"message\", out var messageElement) ||\n !messageElement.TryGetProperty(\"content\", out var contentElement))\n {\n throw new InvalidOperationException(\"DeepSeek API returned an invalid response shape.\");\n }\n\n var aiText = contentElement.GetString();\n if (!string.IsNullOrWhiteSpace(aiText))\n {\n return aiText;\n }\n\n throw new InvalidOperationException(\"DeepSeek API returned an empty text response.\");\n }\n}\n"}, "1205b1d37b62c2693f94b1443e3f2023bfbdf3ad": {"t": 1785756592.5827005, "n": 4917, "h": "315a7666a8c800ba", "c": "using System.Net.Http.Json;\nusing System.Text.Json;\nusing SahtekApi.Models;\n\nnamespace SahtekApi.Services;\n\npublic class GeminiService : IGeminiService\n{\n private readonly HttpClient _httpClient;\n private readonly string _apiKey;\n\n private const string SystemPrompt = \"\"\"\n Tu es \"صحّتك\" (Sahtek), une assistante IA spécialisée UNIQUEMENT dans la sensibilisation au cancer du sein, conçue pour les femmes marocaines.\n\n RÈGLES ABSOLUES:\n 1. Tu réponds UNIQUEMENT aux questions liées au cancer du sein, à la santé du sein, à l'auto-examen, à la prévention, aux symptômes, au dépistage et au soutien émotionnel.\n 2. Si la question est hors sujet, dis poliment que tu es spécialisée dans le cancer du sein.\n 3. Tu n'es PAS médecin. Tu ne fais JAMAIS de diagnostic. Tu donnes des informations éducatives.\n 4. Ajoute TOUJOURS un rappel de consulter un médecin pour toute préoccupation.\n 5. Sois empathique, chaleureuse et rassurante. Beaucoup de femmes ont peur.\n 6. Utilise un langage simple et accessible.\n\n LANGUES:\n - Si language=\"ar\": Réponds en Darija marocaine. Utilise des mots comme \"خاصك\", \"كيداير\", \"بزاف\", \"ديال\". PAS en arabe classique.\n - Si language=\"fr\": Réponds en français simple.\n - Si language=\"en\": Réponds en anglais simple.\n\n FORMAT:\n - Réponses entre 50 et 200 mots maximum.\n - Utilise des émojis pertinents (💗🎀🩺💪✅).\n - Structure avec des tirets si plusieurs points.\n \"\"\";\n\n public GeminiService(HttpClient httpClient)\n {\n _httpClient = httpClient;\n _apiKey = Environment.GetEnvironmentVariable(\"GEMINI_API_KEY\")\n ?? throw new InvalidOperationException(\"GEMINI_API_KEY environment variable is not configured.\");\n }\n\n public async Task<ChatResponse> GenerateChatResponseAsync(ChatRequest request, CancellationToken cancellationToken)\n {\n var contents = new List<object>();\n\n if (request.ConversationHistory != null)\n {\n foreach (var msg in request.ConversationHistory.TakeLast(10))\n {\n if (string.IsNullOrWhiteSpace(msg.Content))\n {\n continue;\n }\n\n var role = string.Equals(msg.Role, \"assistant\", StringComparison.OrdinalIgnoreCase)\n ? \"model\"\n : \"user\";\n\n contents.Add(new\n {\n role,\n parts = new[] { new { text = msg.Content } }\n });\n }\n }\n\n contents.Add(new\n {\n role = \"user\",\n parts = new[] { new { text = $\"language={request.Language}\\n\\n{request.Message}\" } }\n });\n\n var payload = new\n {\n system_instruction = new { parts = new[] { new { text = SystemPrompt } } },\n contents = contents\n };\n\n var url = $\"https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent?key={_apiKey}\";\n\n var response = await _httpClient.PostAsJsonAsync(url, payload, cancellationToken);\n var json = await response.Content.ReadAsStringAsync(cancellationToken);\n\n if (!response.IsSuccessStatusCode)\n {\n throw new InvalidOperationException($\"Gemini API request failed with status {(int)response.StatusCode}.\");\n }\n\n using var jsonDoc = JsonDocument.Parse(json);\n var aiText = ExtractText(jsonDoc.RootElement);\n\n return new ChatResponse\n {\n Response = aiText,\n Category = \"general\",\n Confidence = 0.9,\n RelatedTopics = [\"self_check\", \"prevention\", \"when_to_see_doctor\"],\n Disclaimer = true\n };\n }\n\n private static string ExtractText(JsonElement root)\n {\n if (!root.TryGetProperty(\"candidates\", out var candidates) ||\n candidates.ValueKind != JsonValueKind.Array ||\n candidates.GetArrayLength() == 0)\n {\n throw new InvalidOperationException(\"Gemini API returned no candidates.\");\n }\n\n var candidate = candidates[0];\n if (!candidate.TryGetProperty(\"content\", out var content) ||\n !content.TryGetProperty(\"parts\", out var parts) ||\n parts.ValueKind != JsonValueKind.Array)\n {\n throw new InvalidOperationException(\"Gemini API returned an invalid response shape.\");\n }\n\n foreach (var part in parts.EnumerateArray())\n {\n if (part.TryGetProperty(\"text\", out var textElement))\n {\n var text = textElement.GetString();\n if (!string.IsNullOrWhiteSpace(text))\n {\n return text;\n }\n }\n }\n\n throw new InvalidOperationException(\"Gemini API returned an empty text response.\");\n }\n}\n"}}}}