Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.11.82
1.11.83
65 changes: 65 additions & 0 deletions src/NimShare.Api/Controllers/AiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@

// Only serve summaries for files behind a currently-valid share link.
var link = await _db.ShareLinks
.Include(l => l.File).ThenInclude(f => f.Owner)

Check warning on line 348 in src/NimShare.Api/Controllers/AiController.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
.SingleOrDefaultAsync(l => l.Slug == req.Slug && l.FileId != null, ct);
if (link is null || link.File is null) return NotFound();
// v1.10.148: dieselben Access-Gates wie im Download-Pfad
Expand Down Expand Up @@ -452,7 +452,7 @@
}
else
{
var text = await _ai.ExtractTextAsync(file.BlobPath, file.ContentType, _blobs, ct);

Check warning on line 455 in src/NimShare.Api/Controllers/AiController.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'contentType' in 'Task<string?> IAiGatewayService.ExtractTextAsync(string blobPath, string contentType, IBlobStorageService blobs, CancellationToken ct = default(CancellationToken))'.
if (string.IsNullOrWhiteSpace(text))
return Problem(statusCode: 415, title: "Cannot summarise this file type",
detail: "Text extraction is currently available for text and PDF files.");
Expand Down Expand Up @@ -1011,6 +1011,71 @@
return Ok(new { queued = ids.Count });
}

/// <summary>v1.11.83: Admin-Diagnose für den KI-Chat/Suche-"0 Treffer"-Fall.
/// Zeigt, mit welchem Embedding-Modell-Label die vorhandenen Vektoren
/// gespeichert sind vs. welches Label die aktuelle Query erzeugt — ein
/// Mismatch ist die Ursache für "Keine passenden Dateien" trotz Index
/// (siehe RetrieveHitsAsync). Rein lesend; als Admin im Browser aufrufbar:
/// GET /api/v1/ai/embedding-stats</summary>
[Authorize(Policy = "ApiUser")]
[HttpGet("embedding-stats")]
public async Task<IActionResult> EmbeddingStats(
[FromServices] ICurrentUserService users, CancellationToken ct)
{
var me = await users.GetOrProvisionAsync(User, ct);
if (me.Role != UserRole.Admin)
return Problem(statusCode: 403, title: "Nur für Admins.");

// Nur die Modell-Labels laden (nicht die Vektor-Bytes) und im Speicher
// gruppieren — leicht und ohne EF-GroupBy-Übersetzungsrisiko auf SQLite.
var labels = await _db.FileEmbeddings.Select(e => e.Model).ToListAsync(ct);
var storedModels = labels
.GroupBy(m => m)
.Select(g => new { model = g.Key, count = g.Count() })
.OrderByDescending(x => x.count)
.ToList();

var totalEmbeddings = labels.Count;
var readyFiles = await _db.Files.CountAsync(
f => f.Status == StorageFileStatus.Ready, ct);
var filesWithoutEmbedding = await _db.Files.CountAsync(
f => f.Status == StorageFileStatus.Ready
&& !_db.FileEmbeddings.Any(e => e.FileId == f.Id), ct);

// Aktuelles Query-Modell live ermitteln (ein billiger Ping-Embed).
string? queryModel = null;
string? providerError = null;
try
{
var provider = await _ai.CreateProviderAsync(ct);
var res = await provider.EmbedAsync("ping", ct);
if (res is { } r) queryModel = r.Model;
else providerError = (provider as OpenAiProvider)?.LastError
?? (provider as GeminiProvider)?.LastError
?? "Provider lieferte kein Embedding.";
}
catch (Exception ex) { providerError = ex.Message; }

var mismatch = queryModel != null && storedModels.Count > 0
&& storedModels.All(s => s.model != queryModel);

return Ok(new
{
queryModel,
providerError,
storedModels,
totalEmbeddings,
readyFiles,
filesWithoutEmbedding,
mismatch,
hint = storedModels.Count == 0
? "Keine Embeddings vorhanden — Index ist leer."
: mismatch
? "MISMATCH: gespeicherte Vektoren tragen ein anderes Modell-Label als die aktuelle Query → Relabel (wenn real dasselbe Modell) oder gezieltes Re-Embed nötig, KEIN Reindex."
: "Kein Label-Mismatch: Query- und Index-Label passen zusammen."
});
}

// ── #4 Guided upload-request cover email ───────────────────────────────

public record GuidedUrReq(Guid LinkId, string RecipientEmail, string? Context);
Expand Down
Loading