refactor, security updates, cv extraction upgrades

This commit is contained in:
2026-04-11 01:34:32 +02:00
parent 806b200ac5
commit 27fd70a2d7
59 changed files with 6817 additions and 1561 deletions
+46 -4
View File
@@ -111,6 +111,35 @@ namespace JobTrackerApi.Services
return $"summ:{hash}";
}
private static async Task<string> ReadErrorBodyAsync(HttpResponseMessage response, CancellationToken cancellationToken = default)
{
var body = await response.Content.ReadAsStringAsync(cancellationToken);
if (string.IsNullOrWhiteSpace(body))
{
return $"HTTP {(int)response.StatusCode}";
}
try
{
using var doc = JsonDocument.Parse(body);
if (doc.RootElement.TryGetProperty("detail", out var detailEl) && detailEl.ValueKind == JsonValueKind.String)
{
return $"HTTP {(int)response.StatusCode}: {detailEl.GetString()}";
}
if (doc.RootElement.TryGetProperty("message", out var messageEl) && messageEl.ValueKind == JsonValueKind.String)
{
return $"HTTP {(int)response.StatusCode}: {messageEl.GetString()}";
}
}
catch (JsonException)
{
}
body = body.Length <= 400 ? body : body[..400];
return $"HTTP {(int)response.StatusCode}: {body}";
}
public async Task<string?> SummarizeAsync(string text, int maxLength = 150, int minLength = 30)
{
if (string.IsNullOrWhiteSpace(text)) return null;
@@ -171,12 +200,12 @@ namespace JobTrackerApi.Services
Interlocked.Add(ref _totalLatencyTicks, sw.ElapsedTicks);
if (!res.IsSuccessStatusCode)
{
var errorBody = await res.Content.ReadAsStringAsync();
var errorBody = await ReadErrorBodyAsync(res);
Interlocked.Increment(ref _failures);
lock (_metricsLock)
{
_lastFailureAt = DateTimeOffset.UtcNow;
_lastError = $"AI summarize returned {(int)res.StatusCode}: {errorBody}";
_lastError = $"AI summarize failed: {errorBody}";
}
return null;
}
@@ -235,11 +264,12 @@ namespace JobTrackerApi.Services
Interlocked.Add(ref _totalOcrLatencyTicks, sw.ElapsedTicks);
if (!response.IsSuccessStatusCode)
{
var errorBody = await ReadErrorBodyAsync(response, cancellationToken);
Interlocked.Increment(ref _ocrFailures);
lock (_metricsLock)
{
_lastOcrFailureAt = DateTimeOffset.UtcNow;
_lastError = $"AI extraction returned {(int)response.StatusCode}.";
_lastError = $"AI extraction failed: {errorBody}";
}
return null;
}
@@ -296,11 +326,12 @@ namespace JobTrackerApi.Services
if (!res.IsSuccessStatusCode)
{
var errorBody = await ReadErrorBodyAsync(res, cancellationToken);
Interlocked.Increment(ref _probeFailures);
lock (_metricsLock)
{
_lastProbeFailureAt = DateTimeOffset.UtcNow;
_lastError = $"Probe returned {(int)res.StatusCode}.";
_lastError = $"AI probe failed: {errorBody}";
}
return;
}
@@ -358,6 +389,8 @@ namespace JobTrackerApi.Services
double? healthLatencyMs = null;
var healthy = false;
string? healthError = null;
bool? summarizeAvailable = null;
string? modelLoadError = null;
try
{
@@ -377,6 +410,8 @@ namespace JobTrackerApi.Services
if (doc.RootElement.TryGetProperty("gpu_name", out var gpuNameEl)) gpuName = gpuNameEl.GetString();
if (doc.RootElement.TryGetProperty("ocr_available", out var ocrAvailableEl) && ocrAvailableEl.ValueKind is JsonValueKind.True or JsonValueKind.False) ocrAvailable = ocrAvailableEl.GetBoolean();
if (doc.RootElement.TryGetProperty("ocr_languages", out var ocrLanguagesEl)) ocrLanguages = ocrLanguagesEl.GetString();
if (doc.RootElement.TryGetProperty("summarize_available", out var summarizeAvailableEl) && summarizeAvailableEl.ValueKind is JsonValueKind.True or JsonValueKind.False) summarizeAvailable = summarizeAvailableEl.GetBoolean();
if (doc.RootElement.TryGetProperty("model_load_error", out var modelLoadErrorEl) && modelLoadErrorEl.ValueKind == JsonValueKind.String) modelLoadError = modelLoadErrorEl.GetString();
if (doc.RootElement.TryGetProperty("ollama_configured", out var ollamaConfiguredEl) && ollamaConfiguredEl.ValueKind is JsonValueKind.True or JsonValueKind.False) ollamaConfigured = ollamaConfiguredEl.GetBoolean();
if (doc.RootElement.TryGetProperty("ollama_reachable", out var ollamaReachableEl) && ollamaReachableEl.ValueKind is JsonValueKind.True or JsonValueKind.False) ollamaReachable = ollamaReachableEl.GetBoolean();
if (doc.RootElement.TryGetProperty("ollama_model", out var ollamaModelEl)) ollamaModel = ollamaModelEl.GetString();
@@ -391,6 +426,13 @@ namespace JobTrackerApi.Services
ollamaLoadedModels = ollamaLoadedModelsEl.EnumerateArray().Where(x => x.ValueKind == JsonValueKind.String).Select(x => x.GetString()).Where(x => !string.IsNullOrWhiteSpace(x)).Cast<string>().ToList();
}
if (doc.RootElement.TryGetProperty("ollama_loaded_count", out var ollamaLoadedCountEl) && ollamaLoadedCountEl.ValueKind == JsonValueKind.Number) ollamaLoadedCount = ollamaLoadedCountEl.GetInt32();
if (summarizeAvailable == false)
{
healthy = false;
healthError = string.IsNullOrWhiteSpace(modelLoadError)
? "AI summarize capability is unavailable."
: modelLoadError;
}
}
else
{