DeutschLernen/GermanApp/Infrastructure/Services/MistralRateLimiter.cs
Lasse Rune Hansen 2b11367a97
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat(backend/infrastructure): implement Mistral API Connector (Phase 0 of AI Services)
- Add Domain/Interfaces/IMistralConnector.cs with AiServiceException and AiErrorCode enum
- Add Infrastructure/Configuration/MistralConfig.cs with validation
- Add Infrastructure/Services/MistralConnector.cs with HTTP client implementation
- Add Infrastructure/Services/MistralRateLimiter.cs for rate limiting
- Add Infrastructure/Services/MistralCircuitBreaker.cs for circuit breaker pattern
- Add Application/Models/MistralRequest.cs with request models
- Add Application/Models/MistralResponse.cs with response models
- Update Program.cs to register IMistralConnector with MemoryCache, HttpClient, and config
- Update GermanApp.csproj with existing dependencies
- Update docs/features/ai-services.md with Phase 0 completion

Phase 0 of AI Services feature is complete. Mistral API Connector is ready for use by MistralService.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-09 18:56:37 +02:00

97 lines
2.7 KiB
C#

using System.Collections.Concurrent;
namespace GermanApp.Infrastructure.Services;
/// <summary>
/// Simple in-memory rate limiter for Mistral API requests.
/// This is part of the Infrastructure layer.
/// </summary>
public class MistralRateLimiter
{
private readonly int _maxRequests;
private readonly TimeSpan _window;
private readonly ConcurrentDictionary<string, List<DateTime>> _requests = new();
/// <summary>
/// Creates a new rate limiter.
/// </summary>
/// <param name="maxRequestsPerMinute">Maximum requests allowed per minute</param>
public MistralRateLimiter(int maxRequestsPerMinute)
{
_maxRequests = maxRequestsPerMinute;
_window = TimeSpan.FromMinutes(1);
}
/// <summary>
/// Attempts to acquire a rate limit token for the specified endpoint.
/// </summary>
/// <param name="endpoint">The API endpoint being called</param>
/// <returns>True if request is allowed, false if rate limit exceeded</returns>
public bool TryAcquire(string endpoint)
{
var now = DateTime.UtcNow;
var requests = _requests.GetOrAdd(endpoint, _ => new List<DateTime>());
lock (requests)
{
// Remove old requests outside the window
requests.RemoveAll(r => now - r > _window);
// Check if limit exceeded
if (requests.Count >= _maxRequests)
return false;
// Add new request
requests.Add(now);
return true;
}
}
/// <summary>
/// Gets the current request count for an endpoint.
/// </summary>
/// <param name="endpoint">The API endpoint</param>
/// <returns>Number of requests in the current window</returns>
public int GetCurrentCount(string endpoint)
{
if (_requests.TryGetValue(endpoint, out var requests))
{
lock (requests)
{
var now = DateTime.UtcNow;
requests.RemoveAll(r => now - r > _window);
return requests.Count;
}
}
return 0;
}
/// <summary>
/// Resets the rate limiter for a specific endpoint.
/// </summary>
/// <param name="endpoint">The API endpoint</param>
public void Reset(string endpoint)
{
if (_requests.TryGetValue(endpoint, out var requests))
{
lock (requests)
{
requests.Clear();
}
}
}
/// <summary>
/// Resets all rate limiters.
/// </summary>
public void ResetAll()
{
foreach (var kvp in _requests)
{
lock (kvp.Value)
{
kvp.Value.Clear();
}
}
}
}