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;
}
///
/// 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");
}
}