Почистить комментарии от упоминаний процесса
Удалены <remarks>, <summary> сжаты до короткой фразы, вырезаны ссылки на Task/Ruling/этап/python/прототип; //-комментарии со ссылками на процесс удалены; то же в .proto. Правила обновлены в docs/spec/Код-стайл-Дейл.md. Строк комментариев 27210 -> ~19100.
This commit is contained in:
@@ -17,43 +17,22 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Deal.Infrastructure.Integrations.Services;
|
||||
|
||||
/// <summary>
|
||||
/// gRPC-адаптер порта IMlClient к автономному ml-service (Ruling 4/6, план Task 16 L438–445).
|
||||
/// gRPC-адаптер порта IMlClient к автономному ml-service.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Регистрируется вместо Local-заглушки при <c>Services:Ml:UseLocal=false</c> (выбор на старте, Ruling 6).
|
||||
/// Поведение 1:1 с <c>backend/app/services/ml_client.py</c> и ml.proto:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="StatusAsync"/> — статус модели из ml-service (RPC Status, deadline 10 с) с кэшем 15 с
|
||||
/// (<see cref="MlStatusCache"/>, python L30–31/127–135) + локальная статистика тенанта из KV/таблиц
|
||||
/// (счётчики ml/ai, learning = count(CardMoves), outbox = count(MlOutbox)); сервис недоступен — старые
|
||||
/// данные кэша (или «не готова») и <c>reachable=false</c>;</item>
|
||||
/// <item><see cref="PredictAsync"/> — RPC Predict (deadline 5 с); сбой/недоступность → фиксированный «не
|
||||
/// уверен» (python L101–107: решит ИИ/локальный путь воркера);</item>
|
||||
/// <item><see cref="ResetAsync"/> — RPC Reset (deadline 10 с); при успехе — очистка своей очереди
|
||||
/// MlOutbox (reset_model L110–124) и инвалидация кэша статуса; сбой — мягкий <c>{ok:false,error}</c>,
|
||||
/// очередь не трогается;</item>
|
||||
/// <item><see cref="PushAsync"/> — ВСЕГДА запись в MlOutbox через <see cref="IMlLearningStore"/> (Ruling 6:
|
||||
/// обучение гарантированно и локально; отправку батчами делает <c>MlOutboxFlushScheduler</c>);</item>
|
||||
/// <item><see cref="TrainBatchAsync"/> (IMlTrainClient) — RPC TrainBatch (deadline 30 с) для фонового флашера.</item>
|
||||
/// </list>
|
||||
/// Каждый вызов несёт metadata tenant-id + service-token (<see cref="MlGrpcConnection"/>, Ruling 1). Scoped:
|
||||
/// локальная статистика читает KV-настройки и таблицы тенанта (ISettingsStore/IMlLearningStore → scoped
|
||||
/// TenantDbContext), как LocalMlClient.
|
||||
/// </remarks>
|
||||
public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Deadline Predict — 5 с (README контрактов: локальная модель).
|
||||
/// Deadline Predict — 5 с
|
||||
/// </summary>
|
||||
public const int PredictDeadlineSeconds = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Deadline Status/Reset — 10 с (README контрактов).
|
||||
/// Deadline Status/Reset — 10 с
|
||||
/// </summary>
|
||||
public const int StatusDeadlineSeconds = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Deadline TrainBatch — 30 с (README контрактов: батч ≤100, 1 транзакция).
|
||||
/// Deadline TrainBatch — 30 с
|
||||
/// </summary>
|
||||
public const int TrainBatchDeadlineSeconds = 30;
|
||||
|
||||
@@ -78,7 +57,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
// Кэш статуса сервиса на тенанта (15 с).
|
||||
private readonly MlStatusCache _statusCache;
|
||||
|
||||
// Recorder истории расхода (ML-событие расхода, этап 10, T2): оценка токенов входного текста.
|
||||
private readonly TokenUsageRecorder _usageRecorder;
|
||||
|
||||
// Логгер сбоев вызовов ml-service.
|
||||
@@ -92,7 +70,7 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
/// <param name="learningStore">Хранилище обучения ML (очередь MlOutbox + журнал).</param>
|
||||
/// <param name="connection">Транспорт ml-service (singleton-канал + service-token).</param>
|
||||
/// <param name="statusCache">Кэш статуса сервиса на тенанта (singleton).</param>
|
||||
/// <param name="usageRecorder">Recorder истории расхода (ML-событие predict, этап 10, T2).</param>
|
||||
/// <param name="usageRecorder">Recorder истории расхода.</param>
|
||||
/// <param name="logger">Логгер сбоев.</param>
|
||||
public GrpcMlClient(
|
||||
ITenantContext tenantContext,
|
||||
@@ -155,14 +133,12 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
new PredictRequest { Text = text ?? string.Empty },
|
||||
CallOptions(tenantId.Value, TimeSpan.FromSeconds(PredictDeadlineSeconds), ct));
|
||||
|
||||
// История расхода (этап 10, T2): ML-ответ токенов не несёт — оценка входного текста (≈chars/4),
|
||||
// бюджет/lifetime AI-счётчик не затрагиваются (локальная модель бесплатна).
|
||||
await _usageRecorder.AddEstimatedAsync(text, TokenUsageSources.Local, TokenUsageSources.Ml, ct);
|
||||
return MapPredict(reply);
|
||||
}
|
||||
catch (Exception exception) when (exception is RpcException or OperationCanceledException or HttpRequestException)
|
||||
{
|
||||
// Сервис недоступен/таймаут/отмена — «не уверен» (python predict L101–107): решит ИИ/локальный путь.
|
||||
_logger.LogDebug(exception, "ML predict недоступен (тенант {TenantId})", tenantId.Value);
|
||||
return NotReadyPrediction;
|
||||
}
|
||||
@@ -182,7 +158,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
}
|
||||
catch (Exception exception) when (exception is RpcException or OperationCanceledException or HttpRequestException)
|
||||
{
|
||||
// Мягкая ошибка реального сервиса (python reset_model L117–121): ok:false + текст; outbox не трогаем.
|
||||
_logger.LogWarning(exception, "ML reset не удался (тенант {TenantId})", tenantId.Value);
|
||||
return new MlResetResultDto(Ok: false, Error: ErrorText(exception));
|
||||
}
|
||||
@@ -192,7 +167,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
return new MlResetResultDto(Ok: false, Error: reply.HasError ? reply.Error : DefaultResetError);
|
||||
}
|
||||
|
||||
// 1:1 reset_model L122–123: после успешного сброса сервиса — очистка своей очереди + свежий статус.
|
||||
await _learningStore.ClearOutboxAsync(ct);
|
||||
_statusCache.Invalidate(tenantId.Value);
|
||||
return new MlResetResultDto(Ok: true, Error: null);
|
||||
@@ -205,8 +179,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
double delta,
|
||||
CancellationToken ct)
|
||||
{
|
||||
// Обучение гарантированно и локально (Ruling 6): сигнал всегда пишется в MlOutbox, отправку батчами
|
||||
// делает MlOutboxFlushScheduler — и в Local-, и в gRPC-режиме (ml_client.py L6–7).
|
||||
await MlOutboxQueue.PushAsync(_learningStore, text, label, delta, ct);
|
||||
}
|
||||
|
||||
@@ -251,7 +223,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
return fresh;
|
||||
}
|
||||
|
||||
// Последние известные данные (при сбое refresh останутся они — python refresh_status L132–135).
|
||||
_statusCache.TryGet(tenantId.Value, out MlStatusCache.Snapshot stale);
|
||||
MlServiceStatusDto previous = stale?.Service ?? NotReadyServiceStatus;
|
||||
|
||||
@@ -275,7 +246,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
}
|
||||
}
|
||||
|
||||
// Маппит ответ Status в контрактный статус модели (поля 1:1 с MlServiceStatusDto).
|
||||
// reply: Ответ ml-service.
|
||||
// Возвращает: DTO статуса модели.
|
||||
private static MlServiceStatusDto MapStatus(StatusReply reply)
|
||||
@@ -290,7 +260,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
Accuracy: reply.Eval?.Accuracy ?? 0.0));
|
||||
}
|
||||
|
||||
// Маппит ответ Predict в контрактный результат (поля 1:1 с MlPredictResultDto).
|
||||
// reply: Ответ ml-service.
|
||||
// Возвращает: DTO предсказания.
|
||||
private static MlPredictResultDto MapPredict(PredictReply reply)
|
||||
@@ -320,7 +289,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
Margin: decision.Margin);
|
||||
}
|
||||
|
||||
// CallOptions вызова: metadata tenant-id/service-token + deadline + токен отмены (Ruling 1).
|
||||
// tenantId: Id тенанта (формат N).
|
||||
// deadline: Лимит времени вызова.
|
||||
// ct: Токен отмены вызова.
|
||||
@@ -342,7 +310,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
? "ML-сервис недоступен"
|
||||
: "ML-сервис не ответил — повторите попытку через несколько секунд";
|
||||
|
||||
// Фиксированный ответ неготовой/недоступной модели: «не уверен» (Ruling 5, ml.proto L21–23).
|
||||
private static MlPredictResultDto NotReadyPrediction => new(
|
||||
Take: false,
|
||||
Label: null,
|
||||
@@ -360,7 +327,6 @@ public sealed class GrpcMlClient : IMlClient, IMlTrainClient
|
||||
Learned: 0,
|
||||
Eval: new MlEvalDto(Count: 0, Correct: 0, Accuracy: 0.0));
|
||||
|
||||
// Читает выключатель mlEnabled: «не false» (ml_routes.py L71) — false только при сохранённом JSON-false.
|
||||
// ct: Токен отмены.
|
||||
// Возвращает: True, если ключ отсутствует, повреждён или хранит JSON-true.
|
||||
private async Task<bool> ReadMlEnabledAsync(CancellationToken ct)
|
||||
|
||||
Reference in New Issue
Block a user