All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Add Domain interfaces: IMistralService, IVoskService, ITtsService - Add Configuration classes: VoskConfig, CoquiConfig - Add Application service: MistralService (text generation with Mistral) - Add Infrastructure services: VoskService (speech recognition), TtsService (TTS) - Add Presentation controllers: MistralController, SpeechController, TtsController - Fix MistralService to use correct IMistralConnector methods (CompleteAsync, ChatAsync) - Fix TtsController to use record constructor syntax - Fix TtsService Task.FromResult type specification - Fix Program.cs service registration and remove merge conflict markers - Update docs/features/ai-services.md with progress (Phases 0-4 complete) - Update docs/ROADMAP.md with AI Services status and test metrics (296 tests) Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
216 lines
6.3 KiB
C#
216 lines
6.3 KiB
C#
using GermanApp.Domain.Interfaces;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace GermanApp.Presentation.Controllers;
|
|
|
|
/// <summary>
|
|
/// API controller for Mistral AI text generation.
|
|
/// This is part of the Presentation layer.
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
[Authorize]
|
|
public class MistralController : ControllerBase
|
|
{
|
|
private readonly IMistralService _mistralService;
|
|
|
|
public MistralController(IMistralService mistralService)
|
|
{
|
|
_mistralService = mistralService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates text from a prompt.
|
|
/// </summary>
|
|
/// <param name="request">Text generation request</param>
|
|
/// <param name="cancellationToken">Cancellation token</param>
|
|
/// <returns>Generated text</returns>
|
|
[HttpPost("generate")]
|
|
public async Task<IActionResult> GenerateText(
|
|
[FromBody] TextGenerationRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(request.Prompt))
|
|
return BadRequest("Prompt is required");
|
|
|
|
try
|
|
{
|
|
var result = await _mistralService.GenerateTextAsync(
|
|
request.Prompt,
|
|
request.Model,
|
|
request.Temperature,
|
|
request.MaxTokens,
|
|
cancellationToken);
|
|
|
|
return Ok(new TextGenerationResponse(result));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return StatusCode(500, new { Error = ex.Message });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates text from chat messages.
|
|
/// </summary>
|
|
/// <param name="request">Chat generation request</param>
|
|
/// <param name="cancellationToken">Cancellation token</param>
|
|
/// <returns>Generated text</returns>
|
|
[HttpPost("chat")]
|
|
public async Task<IActionResult> GenerateChat(
|
|
[FromBody] ChatGenerationRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (request.Messages == null || request.Messages.Count == 0)
|
|
return BadRequest("Messages are required");
|
|
|
|
try
|
|
{
|
|
var result = await _mistralService.GenerateChatAsync(
|
|
request.Messages,
|
|
request.Model,
|
|
request.Temperature,
|
|
request.MaxTokens,
|
|
cancellationToken);
|
|
|
|
return Ok(new TextGenerationResponse(result));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return StatusCode(500, new { Error = ex.Message });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a story based on lesson context.
|
|
/// </summary>
|
|
/// <param name="request">Story generation request</param>
|
|
/// <param name="cancellationToken">Cancellation token</param>
|
|
/// <returns>Generated story</returns>
|
|
[HttpPost("story")]
|
|
public async Task<IActionResult> GenerateStory(
|
|
[FromBody] StoryGenerationRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(request.Level))
|
|
return BadRequest("Level is required");
|
|
|
|
try
|
|
{
|
|
var result = await _mistralService.GenerateStoryAsync(
|
|
request.Level,
|
|
request.Topic ?? string.Empty,
|
|
request.VocabularyWords ?? new List<string>(),
|
|
request.Length,
|
|
cancellationToken);
|
|
|
|
return Ok(new StoryGenerationResponse(result));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return StatusCode(500, new { Error = ex.Message });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides feedback on user writing.
|
|
/// </summary>
|
|
/// <param name="request">Writing feedback request</param>
|
|
/// <param name="cancellationToken">Cancellation token</param>
|
|
/// <returns>Generated feedback</returns>
|
|
[HttpPost("writing-feedback")]
|
|
public async Task<IActionResult> GenerateWritingFeedback(
|
|
[FromBody] WritingFeedbackRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(request.UserText))
|
|
return BadRequest("User text is required");
|
|
if (string.IsNullOrWhiteSpace(request.Level))
|
|
return BadRequest("Level is required");
|
|
|
|
try
|
|
{
|
|
var result = await _mistralService.GenerateWritingFeedbackAsync(
|
|
request.UserText,
|
|
request.Level,
|
|
request.Prompt,
|
|
cancellationToken);
|
|
|
|
return Ok(new WritingFeedbackResponse(result));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return StatusCode(500, new { Error = ex.Message });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests the Mistral API connection.
|
|
/// </summary>
|
|
/// <returns>Health check result</returns>
|
|
[HttpGet("health")]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> HealthCheck(CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var isHealthy = await _mistralService.TestConnectionAsync(cancellationToken);
|
|
return Ok(new { Healthy = isHealthy });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return StatusCode(500, new { Error = ex.Message });
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Request DTO for text generation.
|
|
/// </summary>
|
|
public record TextGenerationRequest(
|
|
string Prompt,
|
|
string? Model = null,
|
|
float Temperature = 0.7f,
|
|
int? MaxTokens = null);
|
|
|
|
/// <summary>
|
|
/// Request DTO for chat generation.
|
|
/// </summary>
|
|
public record ChatGenerationRequest(
|
|
IReadOnlyList<(string Role, string Content)> Messages,
|
|
string? Model = null,
|
|
float Temperature = 0.7f,
|
|
int? MaxTokens = null);
|
|
|
|
/// <summary>
|
|
/// Request DTO for story generation.
|
|
/// </summary>
|
|
public record StoryGenerationRequest(
|
|
string Level,
|
|
string? Topic = null,
|
|
IReadOnlyList<string>? VocabularyWords = null,
|
|
int Length = 200);
|
|
|
|
/// <summary>
|
|
/// Request DTO for writing feedback.
|
|
/// </summary>
|
|
public record WritingFeedbackRequest(
|
|
string UserText,
|
|
string Level,
|
|
string? Prompt = null);
|
|
|
|
/// <summary>
|
|
/// Response DTO for text generation.
|
|
/// </summary>
|
|
public record TextGenerationResponse(string Text);
|
|
|
|
/// <summary>
|
|
/// Response DTO for story generation.
|
|
/// </summary>
|
|
public record StoryGenerationResponse(string Story);
|
|
|
|
/// <summary>
|
|
/// Response DTO for writing feedback.
|
|
/// </summary>
|
|
public record WritingFeedbackResponse(string Feedback);
|