Удалены <remarks>, <summary> сжаты до короткой фразы, вырезаны ссылки на Task/Ruling/этап/python/прототип; //-комментарии со ссылками на процесс удалены; то же в .proto. Правила обновлены в docs/spec/Код-стайл-Дейл.md. Строк комментариев 27210 -> ~19100.
62 lines
3.6 KiB
C#
62 lines
3.6 KiB
C#
using Deal.Grpc.Hosting.Interceptors;
|
||
using Deal.Grpc.Hosting.Models;
|
||
using Deal.Grpc.Hosting.Options;
|
||
using Deal.Grpc.Hosting.Services;
|
||
|
||
namespace Deal.Ai;
|
||
|
||
/// <summary>
|
||
/// Собирает WebApplication gRPC-хоста ai-service.
|
||
/// </summary>
|
||
public static class AiServiceHost
|
||
{
|
||
/// <summary>
|
||
/// Создаёт (не запускает) хост
|
||
/// </summary>
|
||
/// <param name="grpcPort">TCP-порт Kestrel.</param>
|
||
/// <param name="args">Аргументы командной строки (Program.cs); в тестах не нужны.</param>
|
||
/// <param name="configureServices">Опциональный хук DI для тестов (подмена LLM-фасада фейками: регистрация, добавленная здесь, побеждает — DI резолвит последнюю; см. AiServiceHostTests/AiRpcTests).</param>
|
||
/// <param name="configureBuilder">Опциональный хук конфигурации билдера для production-точки входа (Program.cs): Serilog. Тесты хост поднимают БЕЗ этого хука — логирование файлов/консоли тестам не нужно.</param>
|
||
/// <returns>Собранный хост; запуск — StartAsync/RunAsync у вызывающего.</returns>
|
||
public static WebApplication Create(
|
||
int grpcPort,
|
||
string[]? args = null,
|
||
Action<IServiceCollection>? configureServices = null,
|
||
Action<WebApplicationBuilder>? configureBuilder = null)
|
||
{
|
||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args ?? []);
|
||
|
||
// Общая серверная обвязка (Deal.Grpc.Hosting, C31): mTLS env DEAL_MTLS_* — загрузка
|
||
// сертификатов сразу с fail-fast (compose-prod монтирует deploy/certs, scripts/mtls-certs.sh);
|
||
MtlsCertificates? mtlsCertificates = GrpcServer.LoadMtlsCertificates(builder);
|
||
GrpcServer.ConfigureKestrelHttp2Endpoint(builder, grpcPort, mtlsCertificates);
|
||
builder.Services.AddDealGrpcServer();
|
||
builder.Services.AddReadyHealthCheck("хост ai-service готов");
|
||
|
||
// попытки управляется внутри — 90 с OpenAI / 60 с Anthropic; клиент без общего таймаута) и
|
||
// оркестратор ретраев/JSON/usage поверх него. Ключи API — в конфиге запроса, не в DI/логах.
|
||
builder.Services.AddHttpClient<Llm.LlmHttpClient>(static httpClient =>
|
||
httpClient.Timeout = Timeout.InfiniteTimeSpan);
|
||
builder.Services.AddSingleton<Llm.IProviderClient>(
|
||
static sp => sp.GetRequiredService<Llm.LlmHttpClient>());
|
||
builder.Services.AddSingleton<Func<TimeSpan, CancellationToken, Task>>(DefaultRetryDelayAsync);
|
||
builder.Services.AddSingleton<Llm.ProviderCaller>();
|
||
|
||
configureServices?.Invoke(builder.Services);
|
||
configureBuilder?.Invoke(builder);
|
||
|
||
WebApplication app = builder.Build();
|
||
|
||
app.MapGrpcService<AiServiceImpl>();
|
||
app.MapGrpcHealthChecksService();
|
||
|
||
return app;
|
||
}
|
||
|
||
// Пауза между ретраями по умолчанию (Task.Delay; тесты подменяют через DI).
|
||
// delay: Длительность паузы.
|
||
// cancellationToken: Токен отмены.
|
||
private static Task DefaultRetryDelayAsync(TimeSpan delay, CancellationToken cancellationToken)
|
||
=> Task.Delay(delay, cancellationToken);
|
||
}
|