Files
FictionArchive/FictionArchive.Service.TranslationService/Program.cs
gamer147 d5a9529d6f
Some checks failed
CI / build-backend (pull_request) Failing after 1m48s
CI / build-frontend (pull_request) Successful in 1m27s
[FA-misc] Translation engine work
2026-08-25 10:38:27 -04:00

129 lines
4.4 KiB
C#

using DeepL;
using FictionArchive.Common.Extensions;
using FictionArchive.Service.Shared;
using FictionArchive.Service.Shared.Extensions;
using FictionArchive.Service.Shared.Services.GraphQL;
using FictionArchive.Service.TranslationService.Consumers;
using FictionArchive.Service.TranslationService.GraphQL;
using FictionArchive.Service.TranslationService.Services;
using FictionArchive.Service.TranslationService.Services.Database;
using FictionArchive.Service.TranslationService.Services.TranslationEngines;
using FictionArchive.Service.TranslationService.Services.TranslationEngines.DeepLTranslate;
using System.Net.Http.Headers;
using FictionArchive.Service.TranslationService.Services.Resilience;
using FictionArchive.Service.TranslationService.Services.TranslationEngines.NanoGpt;
using Polly;
namespace FictionArchive.Service.TranslationService;
public class Program
{
public static void Main(string[] args)
{
var isSchemaExport = SchemaExportDetector.IsSchemaExportMode(args);
var builder = WebApplication.CreateBuilder(args);
builder.AddLocalAppsettings();
builder.Services.AddHealthChecks();
#region MassTransit
if (!isSchemaExport)
{
builder.Services.AddFictionArchiveMassTransit(
builder.Configuration,
x =>
{
x.AddConsumer<TranslationRequestCreatedConsumer>();
});
}
#endregion
#region Database
builder.Services.RegisterDbContext<TranslationServiceDbContext>(
builder.Configuration.GetConnectionString("DefaultConnection"),
skipInfrastructure: isSchemaExport);
#endregion
#region GraphQL
builder.Services.AddDefaultGraphQl<Query, Mutation>()
.AddAuthorization();
#endregion
#region Translation Adapter
// Shared resilience pipeline used by both DeepL and NanoGPT engines.
builder.Services.AddSingleton(TranslationResiliencePipeline.Build());
// DeepL
builder.Services.AddTransient<DeepLClient>(_ => new DeepLClient(builder.Configuration["DeepL:ApiKey"]));
builder.Services.AddTransient<ITranslationEngine, DeepLTranslationEngine>();
// NanoGPT — typed HttpClient (bearer auth + base address + 120s timeout).
builder.Services.AddHttpClient<NanoGptApiClient>(client =>
{
var baseAddress = builder.Configuration["NanoGpt:BaseAddress"];
if (!string.IsNullOrWhiteSpace(baseAddress))
{
client.BaseAddress = new Uri(baseAddress);
}
var apiKey = builder.Configuration["NanoGpt:ApiKey"];
if (!string.IsNullOrWhiteSpace(apiKey) && apiKey != "REPLACE_ME")
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
}
client.Timeout = TimeSpan.FromSeconds(120);
});
// Register one ITranslationEngine per configured NanoGPT model.
var nanoGptModels = builder.Configuration
.GetSection("NanoGpt:Models")
.Get<NanoGptModelOptions[]>() ?? [];
foreach (var modelOptions in nanoGptModels)
{
builder.Services.AddTransient<ITranslationEngine>(sp => new NanoGptTranslationEngine(
sp.GetRequiredService<NanoGptApiClient>(),
modelOptions,
sp.GetRequiredService<ResiliencePipeline>(),
sp.GetRequiredService<ILogger<NanoGptTranslationEngine>>()));
}
builder.Services.AddTransient<TranslationEngineService>();
#endregion
// Authentication & Authorization
builder.Services.AddOidcAuthentication(builder.Configuration);
builder.Services.AddFictionArchiveAuthorization();
var app = builder.Build();
// Update database (skip in schema export mode)
if (!isSchemaExport)
{
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<TranslationServiceDbContext>();
dbContext.UpdateDatabase();
}
app.UseHttpsRedirection();
app.MapHealthChecks("/healthz");
app.UseAuthentication();
app.UseAuthorization();
app.MapGraphQL();
app.RunWithGraphQLCommands(args);
}
}