Нормализовать переводы строк в LF
Решение по TD-STYLE-ANALYZERS: LF — инструменты проекта (Python/Node) пишут LF, CRLF-.sh не работают на Linux CI (sh scripts/ci.sh), большинство файлов уже были LF. Добавлен .gitattributes (* text=auto eol=lf, бинарные исключения), .editorconfig переведён на lf, 1029 файлов конвертированы, git add --renormalize. Из индекса убраны закравшиеся archive/**/__pycache__/*.pyc.
This commit is contained in:
@@ -1,133 +1,133 @@
|
||||
using System.Text.Json;
|
||||
using Deal.Contracts.Integrations.Abstractions;
|
||||
using Deal.Contracts.Integrations.Models;
|
||||
using Deal.Modules.Kanban.Application.Abstractions;
|
||||
using Deal.Modules.Settings.Application.Abstractions;
|
||||
using Deal.Modules.Settings.Application.Models;
|
||||
|
||||
namespace Deal.Infrastructure.Integrations.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Локальная реализация <see cref="IMlClient"/> без внешнего ML-сервиса.
|
||||
/// </summary>
|
||||
/// <param name="store">KV-хранилище настроек тенанта (таблица settings).</param>
|
||||
/// <param name="learningStore">Хранилище обучения ML: очередь MlOutbox + счётчик журнала CardMoves.</param>
|
||||
public sealed class LocalMlClient(ISettingsStore store, IMlLearningStore learningStore) : IMlClient
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<string, double> EmptyClasses = new Dictionary<string, double>();
|
||||
|
||||
private static readonly IReadOnlyDictionary<string, double> EmptyScores = new Dictionary<string, double>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MlStatusResponseDto> StatusAsync(CancellationToken ct)
|
||||
{
|
||||
var service = new MlServiceStatusDto(
|
||||
Ready: false,
|
||||
Classes: EmptyClasses,
|
||||
Learned: 0,
|
||||
Eval: new MlEvalDto(Count: 0, Correct: 0, Accuracy: 0.0));
|
||||
|
||||
bool enabled = await ReadMlEnabledAsync(ct);
|
||||
int mlDecisions = await ReadCounterAsync(SettingsKeys.MlDecisions, ct);
|
||||
int aiDecisions = await ReadCounterAsync(SettingsKeys.AiDecisions, ct);
|
||||
|
||||
int learning = await learningStore.CountLearningAsync(ct);
|
||||
int outbox = await learningStore.CountOutboxAsync(ct);
|
||||
|
||||
var stats = new MlStatsDto(
|
||||
Ml: mlDecisions,
|
||||
Ai: aiDecisions,
|
||||
Learning: learning,
|
||||
Ready: service.Ready,
|
||||
Classes: service.Classes,
|
||||
Learned: service.Learned,
|
||||
Reachable: true,
|
||||
Outbox: outbox);
|
||||
|
||||
return new MlStatusResponseDto(Enabled: enabled, Service: service, Reachable: true, Stats: stats);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<MlPredictResultDto> PredictAsync(string text, CancellationToken ct)
|
||||
{
|
||||
return Task.FromResult(new MlPredictResultDto(
|
||||
Take: false,
|
||||
Label: null,
|
||||
Scores: EmptyScores,
|
||||
Hits: 0,
|
||||
Ready: false,
|
||||
Margin: null,
|
||||
Terms: Array.Empty<string>(),
|
||||
Type: null));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MlResetResultDto> ResetAsync(CancellationToken ct)
|
||||
{
|
||||
await learningStore.ClearOutboxAsync(ct);
|
||||
return new MlResetResultDto(Ok: true, Error: null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task PushAsync(
|
||||
string text,
|
||||
string label,
|
||||
double delta,
|
||||
CancellationToken ct)
|
||||
{
|
||||
await MlOutboxQueue.PushAsync(learningStore, text, label, delta, ct);
|
||||
}
|
||||
|
||||
private async Task<bool> ReadMlEnabledAsync(CancellationToken ct)
|
||||
{
|
||||
SettingValue? row = await store.GetAsync(SettingsKeys.MlEnabled, ct);
|
||||
if (row is null)
|
||||
{
|
||||
return SettingsDefaults.MlEnabled;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using JsonDocument document = JsonDocument.Parse(row.ValueJson);
|
||||
if (document.RootElement.ValueKind is JsonValueKind.True or JsonValueKind.False)
|
||||
{
|
||||
return document.RootElement.GetBoolean();
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Повреждённая строка — дефолт (мягкая семантика, как в SettingsService).
|
||||
}
|
||||
|
||||
return SettingsDefaults.MlEnabled;
|
||||
}
|
||||
|
||||
// Читает целочисленный счётчик (mlDecisions/aiDecisions); отсутствие/повреждение → 0.
|
||||
// key: Внутренний KV-ключ счётчика.
|
||||
// ct: Токен отмены.
|
||||
// Возвращает: Значение счётчика из хранилища или 0.
|
||||
private async Task<int> ReadCounterAsync(string key, CancellationToken ct)
|
||||
{
|
||||
SettingValue? row = await store.GetAsync(key, ct);
|
||||
if (row is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using JsonDocument document = JsonDocument.Parse(row.ValueJson);
|
||||
if (document.RootElement.ValueKind == JsonValueKind.Number
|
||||
&& document.RootElement.TryGetInt64(out long wide))
|
||||
{
|
||||
return (int)Math.Clamp(wide, int.MinValue, int.MaxValue);
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Повреждённая строка — 0 (мягкая семантика, как в SettingsService).
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
using System.Text.Json;
|
||||
using Deal.Contracts.Integrations.Abstractions;
|
||||
using Deal.Contracts.Integrations.Models;
|
||||
using Deal.Modules.Kanban.Application.Abstractions;
|
||||
using Deal.Modules.Settings.Application.Abstractions;
|
||||
using Deal.Modules.Settings.Application.Models;
|
||||
|
||||
namespace Deal.Infrastructure.Integrations.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Локальная реализация <see cref="IMlClient"/> без внешнего ML-сервиса.
|
||||
/// </summary>
|
||||
/// <param name="store">KV-хранилище настроек тенанта (таблица settings).</param>
|
||||
/// <param name="learningStore">Хранилище обучения ML: очередь MlOutbox + счётчик журнала CardMoves.</param>
|
||||
public sealed class LocalMlClient(ISettingsStore store, IMlLearningStore learningStore) : IMlClient
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<string, double> EmptyClasses = new Dictionary<string, double>();
|
||||
|
||||
private static readonly IReadOnlyDictionary<string, double> EmptyScores = new Dictionary<string, double>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MlStatusResponseDto> StatusAsync(CancellationToken ct)
|
||||
{
|
||||
var service = new MlServiceStatusDto(
|
||||
Ready: false,
|
||||
Classes: EmptyClasses,
|
||||
Learned: 0,
|
||||
Eval: new MlEvalDto(Count: 0, Correct: 0, Accuracy: 0.0));
|
||||
|
||||
bool enabled = await ReadMlEnabledAsync(ct);
|
||||
int mlDecisions = await ReadCounterAsync(SettingsKeys.MlDecisions, ct);
|
||||
int aiDecisions = await ReadCounterAsync(SettingsKeys.AiDecisions, ct);
|
||||
|
||||
int learning = await learningStore.CountLearningAsync(ct);
|
||||
int outbox = await learningStore.CountOutboxAsync(ct);
|
||||
|
||||
var stats = new MlStatsDto(
|
||||
Ml: mlDecisions,
|
||||
Ai: aiDecisions,
|
||||
Learning: learning,
|
||||
Ready: service.Ready,
|
||||
Classes: service.Classes,
|
||||
Learned: service.Learned,
|
||||
Reachable: true,
|
||||
Outbox: outbox);
|
||||
|
||||
return new MlStatusResponseDto(Enabled: enabled, Service: service, Reachable: true, Stats: stats);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<MlPredictResultDto> PredictAsync(string text, CancellationToken ct)
|
||||
{
|
||||
return Task.FromResult(new MlPredictResultDto(
|
||||
Take: false,
|
||||
Label: null,
|
||||
Scores: EmptyScores,
|
||||
Hits: 0,
|
||||
Ready: false,
|
||||
Margin: null,
|
||||
Terms: Array.Empty<string>(),
|
||||
Type: null));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MlResetResultDto> ResetAsync(CancellationToken ct)
|
||||
{
|
||||
await learningStore.ClearOutboxAsync(ct);
|
||||
return new MlResetResultDto(Ok: true, Error: null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task PushAsync(
|
||||
string text,
|
||||
string label,
|
||||
double delta,
|
||||
CancellationToken ct)
|
||||
{
|
||||
await MlOutboxQueue.PushAsync(learningStore, text, label, delta, ct);
|
||||
}
|
||||
|
||||
private async Task<bool> ReadMlEnabledAsync(CancellationToken ct)
|
||||
{
|
||||
SettingValue? row = await store.GetAsync(SettingsKeys.MlEnabled, ct);
|
||||
if (row is null)
|
||||
{
|
||||
return SettingsDefaults.MlEnabled;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using JsonDocument document = JsonDocument.Parse(row.ValueJson);
|
||||
if (document.RootElement.ValueKind is JsonValueKind.True or JsonValueKind.False)
|
||||
{
|
||||
return document.RootElement.GetBoolean();
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Повреждённая строка — дефолт (мягкая семантика, как в SettingsService).
|
||||
}
|
||||
|
||||
return SettingsDefaults.MlEnabled;
|
||||
}
|
||||
|
||||
// Читает целочисленный счётчик (mlDecisions/aiDecisions); отсутствие/повреждение → 0.
|
||||
// key: Внутренний KV-ключ счётчика.
|
||||
// ct: Токен отмены.
|
||||
// Возвращает: Значение счётчика из хранилища или 0.
|
||||
private async Task<int> ReadCounterAsync(string key, CancellationToken ct)
|
||||
{
|
||||
SettingValue? row = await store.GetAsync(key, ct);
|
||||
if (row is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using JsonDocument document = JsonDocument.Parse(row.ValueJson);
|
||||
if (document.RootElement.ValueKind == JsonValueKind.Number
|
||||
&& document.RootElement.TryGetInt64(out long wide))
|
||||
{
|
||||
return (int)Math.Clamp(wide, int.MinValue, int.MaxValue);
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Повреждённая строка — 0 (мягкая семантика, как в SettingsService).
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user