using DramaLing.Api.Data; using DramaLing.Api.Models.DTOs; using DramaLing.Api.Services.Storage; using DramaLing.Api.Services; using Microsoft.EntityFrameworkCore; using System.Diagnostics; using System.Text.Json; namespace DramaLing.Api.Services.AI.Generation; public class GenerationPipelineService : IGenerationPipelineService { private readonly IServiceProvider _serviceProvider; private readonly IGenerationStateManager _stateManager; private readonly IImageSaveManager _imageSaveManager; private readonly ILogger _logger; public GenerationPipelineService( IServiceProvider serviceProvider, IGenerationStateManager stateManager, IImageSaveManager imageSaveManager, ILogger logger) { _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); _stateManager = stateManager ?? throw new ArgumentNullException(nameof(stateManager)); _imageSaveManager = imageSaveManager ?? throw new ArgumentNullException(nameof(imageSaveManager)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task ExecuteGenerationPipelineAsync(Guid requestId) { var totalStopwatch = Stopwatch.StartNew(); using var scope = _serviceProvider.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); var geminiService = scope.ServiceProvider.GetRequiredService(); var replicateService = scope.ServiceProvider.GetRequiredService(); var storageService = scope.ServiceProvider.GetRequiredService(); var imageProcessingService = scope.ServiceProvider.GetRequiredService(); try { _logger.LogInformation("Starting generation pipeline for request {RequestId}", requestId); var request = await dbContext.ImageGenerationRequests .Include(r => r.Flashcard) .FirstOrDefaultAsync(r => r.Id == requestId); if (request == null) { _logger.LogError("Generation request {RequestId} not found in pipeline", requestId); return; } var options = JsonSerializer.Deserialize(request.OriginalRequest); // 第一階段:Gemini 描述生成 _logger.LogInformation("Starting Gemini description generation for request {RequestId}", requestId); await _stateManager.UpdateRequestStatusAsync(requestId, "description_generating", "processing", "pending"); var optimizedPrompt = await geminiService.GenerateImageDescriptionAsync( request.Flashcard, options?.Options ?? new GenerationOptionsDto()); if (string.IsNullOrWhiteSpace(optimizedPrompt)) { await _stateManager.MarkRequestAsFailedAsync(requestId, "gemini", "Generated prompt is empty"); return; } await _stateManager.UpdateGeminiResultAsync(requestId, optimizedPrompt); // 第二階段:Replicate 圖片生成 _logger.LogInformation("Starting Replicate image generation for request {RequestId}", requestId); await _stateManager.UpdateRequestStatusAsync(requestId, "image_generating", "completed", "processing"); var modelName = "ideogram-v2a-turbo"; _logger.LogInformation("Using Replicate model: {ModelName}", modelName); var imageResult = await replicateService.GenerateImageAsync( optimizedPrompt, modelName, new ReplicateGenerationOptions { Width = options?.Width ?? 512, Height = options?.Height ?? 512, TimeoutMinutes = 5 }); if (!imageResult.Success) { await _stateManager.MarkRequestAsFailedAsync(requestId, "replicate", imageResult.Error); return; } // 下載並儲存圖片 var savedImage = await _imageSaveManager.SaveGeneratedImageAsync( dbContext, storageService, imageProcessingService, request, optimizedPrompt, imageResult); // 完成請求 await _stateManager.CompleteRequestAsync(requestId, savedImage.Id, totalStopwatch.ElapsedMilliseconds); _logger.LogInformation("Generation pipeline completed successfully for request {RequestId} in {ElapsedMs}ms", requestId, totalStopwatch.ElapsedMilliseconds); } catch (Exception ex) { totalStopwatch.Stop(); _logger.LogError(ex, "Generation pipeline failed for request {RequestId}", requestId); await _stateManager.MarkRequestAsFailedAsync(requestId, "system", ex.Message); } } }