DeutschLernen/GermanApp/Presentation/Controllers/SpeechController.cs
Lasse Rune Hansen 769c63b005
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat(backend/ai-services): Complete AI Services Phase 0-4 implementation
- 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>
2026-06-13 10:09:19 +02:00

109 lines
3.2 KiB
C#

using GermanApp.Domain.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace GermanApp.Presentation.Controllers;
/// <summary>
/// API controller for speech recognition using Vosk.
/// This is part of the Presentation layer.
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class SpeechController : ControllerBase
{
private readonly IVoskService _voskService;
public SpeechController(IVoskService voskService)
{
_voskService = voskService;
}
/// <summary>
/// Recognizes speech from audio bytes.
/// </summary>
/// <param name="request">Audio recognition request</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Recognized text</returns>
[HttpPost("recognize")]
[RequestSizeLimit(10_000_000)] // 10MB limit
[RequestFormLimits(MultipartBodyLengthLimit = 10_000_000)]
public async Task<IActionResult> RecognizeSpeech(
[FromForm] SpeechRecognitionRequest request,
CancellationToken cancellationToken = default)
{
if (request?.Audio == null || request.Audio.Length == 0)
return BadRequest("Audio data is required");
try
{
var result = await _voskService.RecognizeSpeechAsync(
request.Audio,
request.SampleRate,
request.Hint,
cancellationToken);
return Ok(new SpeechRecognitionResponse(result));
}
catch (TimeoutException ex)
{
return StatusCode(504, new { Error = ex.Message });
}
catch (Exception ex)
{
return StatusCode(500, new { Error = ex.Message });
}
}
/// <summary>
/// Tests the speech recognition service.
/// </summary>
/// <returns>Health check result</returns>
[HttpGet("health")]
[AllowAnonymous]
public async Task<IActionResult> HealthCheck(CancellationToken cancellationToken = default)
{
try
{
var isHealthy = await _voskService.TestModelAsync(cancellationToken);
return Ok(new { Healthy = isHealthy });
}
catch (Exception ex)
{
return StatusCode(500, new { Error = ex.Message });
}
}
/// <summary>
/// Gets information about the current Vosk model.
/// </summary>
/// <returns>Model information</returns>
[HttpGet("model-info")]
[AllowAnonymous]
public async Task<IActionResult> GetModelInfo(CancellationToken cancellationToken = default)
{
try
{
var (modelName, modelPath) = await _voskService.GetModelInfoAsync();
return Ok(new { ModelName = modelName, ModelPath = modelPath });
}
catch (Exception ex)
{
return StatusCode(500, new { Error = ex.Message });
}
}
}
/// <summary>
/// Request DTO for speech recognition.
/// </summary>
public record SpeechRecognitionRequest(
byte[] Audio,
int SampleRate = 16000,
string? Hint = null);
/// <summary>
/// Response DTO for speech recognition.
/// </summary>
public record SpeechRecognitionResponse(string Text);