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>
109 lines
3.2 KiB
C#
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);
|