using GermanApp.Application.Models; using GermanApp.Domain.Interfaces; using GermanApp.Infrastructure.Configuration; using Microsoft.Extensions.Options; namespace GermanApp.Application.Services; /// /// Application service for Mistral AI text generation. /// This is part of the Application layer. /// public class MistralService : IMistralService { private readonly IMistralConnector _connector; private readonly MistralConfig _config; public MistralService( IMistralConnector connector, IOptions config) { _connector = connector; _config = config.Value ?? new MistralConfig(); } /// /// Generates text from a prompt using Mistral API. /// public virtual async Task GenerateTextAsync( string prompt, string? model = null, float temperature = 0.7f, int? maxTokens = null, CancellationToken cancellationToken = default) { model ??= _config.DefaultModel; maxTokens ??= 500; var request = new MistralRequest { Model = model, Prompt = prompt, Temperature = temperature, MaxTokens = maxTokens.Value }; var response = await _connector.CompleteAsync(request, cancellationToken); if (response.Choices == null || response.Choices.Count == 0) throw new InvalidOperationException("No choices returned from Mistral API"); return response.Choices[0].Text ?? string.Empty; } /// /// Generates text from chat messages using Mistral API. /// public virtual async Task GenerateChatAsync( IReadOnlyList<(string role, string content)> messages, string? model = null, float temperature = 0.7f, int? maxTokens = null, CancellationToken cancellationToken = default) { model ??= _config.DefaultModel; maxTokens ??= 500; var chatMessages = messages .Select(m => new MistralMessage { Role = m.role, Content = m.content }) .ToList(); var request = new MistralChatRequest { Model = model, Messages = chatMessages, Temperature = temperature, MaxTokens = maxTokens.Value }; var response = await _connector.ChatAsync(request, cancellationToken); if (response.Choices == null || response.Choices.Count == 0) throw new InvalidOperationException("No choices returned from Mistral API"); return response.Choices[0].Message?.Content ?? string.Empty; } /// /// Generates a story based on lesson context and vocabulary. /// public virtual async Task GenerateStoryAsync( string level, string topic, IReadOnlyList vocabularyWords, int length = 200, CancellationToken cancellationToken = default) { var prompt = BuildStoryPrompt(level, topic, vocabularyWords, length); return await GenerateTextAsync( prompt, model: _config.DefaultModel, temperature: 0.8f, maxTokens: 1000, cancellationToken); } /// /// Provides feedback on user writing. /// public virtual async Task GenerateWritingFeedbackAsync( string userText, string level, string? prompt = null, CancellationToken cancellationToken = default) { var messages = new List<(string role, string content)> { ("system", BuildFeedbackSystemPrompt(level)), ("user", prompt ?? "Please provide feedback on the following German text:"), ("user", userText) }; return await GenerateChatAsync( messages, model: _config.DefaultModel, temperature: 0.3f, // Lower temperature for more deterministic feedback maxTokens: 800, cancellationToken); } /// /// Tests the Mistral API connection. /// public virtual async Task TestConnectionAsync(CancellationToken cancellationToken = default) { try { // Send a simple test prompt var testPrompt = "Say 'test successful'"; var result = await GenerateTextAsync( testPrompt, maxTokens: 10, cancellationToken: cancellationToken); return result.Contains("test successful", System.StringComparison.OrdinalIgnoreCase); } catch { return false; } } /// /// Builds a prompt for story generation. /// private string BuildStoryPrompt(string level, string topic, IReadOnlyList vocabularyWords, int length) { var vocabularyList = string.Join(", ", vocabularyWords); return $"You are a helpful German language teacher. Create an engaging story for a {level} level learner.\n\nRequirements:\n- Topic: {topic}\n- Length: approximately {length} words\n- Use these German vocabulary words: {vocabularyList}\n- Write in German language\n- Appropriate for A1-A2 learners (simple sentences, common vocabulary)\n- Include dialogue\n- End with a question for the reader\n\nWrite only the story text, no additional explanation or formatting." .Replace("\n\n", "\n"); } /// /// Builds a system prompt for writing feedback. /// private string BuildFeedbackSystemPrompt(string level) { return $"You are a helpful German language tutor. Provide constructive feedback on the user's German writing.\n\nGuidelines:\n- Respond in English\n- First, identify and correct any grammar mistakes\n- Then, provide suggestions for improvement\n- Finally, give encouragement\n- Be specific and helpful\n- Keep feedback concise (3-5 sentences)\n- Level: {level}" .Replace("\n\n", "\n"); } }