feat(cv-import): ground reviewed facts in revisioned career profiles

This commit is contained in:
cesnimda
2026-10-26 14:30:14 +01:00
parent 3ea8ff9d62
commit e8f76a9dea
24 changed files with 1197 additions and 13 deletions
@@ -184,11 +184,13 @@ public sealed partial class ProfileCvController : ControllerBase
var storedFileName = $"{DateTimeOffset.UtcNow:yyyyMMddHHmmss}-{Guid.NewGuid():N}{extension}";
var storagePath = Path.Combine(userRoot, storedFileName);
await using (var target = System.IO.File.Create(storagePath))
await using (var source = file.OpenReadStream())
try
{
await using var target = System.IO.File.Create(storagePath);
await using var source = file.OpenReadStream();
await source.CopyToAsync(target, cancellationToken);
}
catch { TryDeleteCvArtifactFile(storagePath); throw; }
await using var hashStream = System.IO.File.OpenRead(storagePath);
var shaBytes = await SHA256.HashDataAsync(hashStream, cancellationToken);
@@ -402,7 +404,7 @@ public sealed partial class ProfileCvController : ControllerBase
// transient service). NonAction keeps it off the HTTP surface: without it the
// controller-level [Route] exposes it as an any-verb endpoint.
[NonAction]
public async Task<CvProcessingOutcome?> ProcessQueuedRunAsync(int runId, CancellationToken cancellationToken)
public async Task<CvProcessingOutcome?> ProcessQueuedRunAsync(int runId, CancellationToken cancellationToken, AiOperationExecutionContext? operationContext = null)
{
var ownerUserId = _db.CurrentUserId;
var run = ownerUserId is null
@@ -439,6 +441,11 @@ public sealed partial class ProfileCvController : ControllerBase
AiGenerationResult? generation = null;
switch (run.Trigger)
{
case "document_import":
{
await ProcessDocumentImportAsync(run, operationContext, cancellationToken);
break;
}
case "rebuild":
{
if (string.IsNullOrWhiteSpace(user.ProfileCvText)) throw new InvalidOperationException("Add or import CV text before rebuilding it.");
@@ -556,7 +563,7 @@ public sealed partial class ProfileCvController : ControllerBase
private async Task PruneExtractionRunsAsync(string ownerUserId, CancellationToken cancellationToken)
{
var completedRuns = _db.CvExtractionRuns.IgnoreQueryFilters()
.Where(x => x.OwnerUserId == ownerUserId && x.Status != "queued" && x.Status != "running");
.Where(x => x.OwnerUserId == ownerUserId && x.Status != "queued" && x.Status != "running" && x.Status != "pending_review");
var expired = _db.Database.IsSqlite()
? (await completedRuns.ToListAsync(cancellationToken)).OrderByDescending(x => x.StartedAtUtc).Skip(ExtractionRunRetentionCount).ToList()
: await completedRuns.OrderByDescending(x => x.StartedAtUtc).Skip(ExtractionRunRetentionCount).ToListAsync(cancellationToken);