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.81
1.11.82
11 changes: 11 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.

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 @@ -762,6 +762,17 @@
// Embeddings. Marcus's Bug-Report: Chat/Suche antworten fast nie.
var embsAll = await _db.FileEmbeddings.Where(e => fileIds.Contains(e.FileId)).ToListAsync(ct);
var embs = embsAll.Where(e => e.Model == queryModel).ToList();
// v1.11.82: 0 Treffer trotz vorhandener Embeddings = Modell-Label-
// Mismatch (Index mit anderem Modell als die aktuelle Query). War bislang
// still ("Keine passenden Dateien") — jetzt sichtbar im Log, damit ein
// gezieltes Relabel/Re-Embed entschieden werden kann statt blind (und
// riskant) zu reindexieren. Siehe Vorfall 2026-08-05.
if (embs.Count == 0 && embsAll.Count > 0)
{
var storedModels = string.Join(", ", embsAll.Select(e => e.Model).Distinct());
Console.Error.WriteLine(
$"[Retrieve] Embedding-Modell-Mismatch: query='{queryModel}', gespeichert=[{storedModels}] über {embsAll.Count} Embeddings. Relabel oder gezieltes Re-Embed nötig.");
}
var scored = new List<(Guid Id, double Score)>();
foreach (var e in embs)
{
Expand Down
6 changes: 5 additions & 1 deletion src/NimShare.Api/Services/AiPostProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ public class AiPostProcessor : IAiPostProcessor
// Jetzt: max 2 gleichzeitige AI-Runs, alle weiteren warten in Reihe.
// Semaphore statt Channel gewählt weil kein Fairness-Requirement und
// fire-and-forget Semantik erhalten bleibt.
private static readonly SemaphoreSlim _concurrency = new(2, 2);
// v1.11.82: nach dem OOM-Vorfall (2026-08-05) von 2 auf 1 gesenkt. Zusammen
// mit dem Größen-Cap in ExtractTextAsync bleibt der Speicher-Peak beim
// (Re-)Indexieren bei EINER Datei — kein paralleles Vielfaches mehr, und
// weniger SQLite-Writer-Contention (busy_timeout blockiert sonst Threads).
private static readonly SemaphoreSlim _concurrency = new(1, 1);

public AiPostProcessor(IServiceScopeFactory scopes, ILogger<AiPostProcessor> log)
{
Expand Down
16 changes: 16 additions & 0 deletions src/NimShare.Api/Services/AiProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,22 @@ public async Task<IAiProvider> CreateProviderAsync(CancellationToken ct = defaul
{
try
{
// v1.11.82: Speicher-Schutz gegen OOM beim (Re-)Indexieren. Die
// Extraktion lädt die Datei komplett in einen MemoryStream (+ToArray
// +Base64 für OCR = bis zu 3 Kopien im RAM); mehrere große Dateien
// parallel killten die Azure-Instanz (Vorfall 2026-08-05). Über der
// Grenze überspringen — die Suche fällt dann auf Dateiname/AiSummary
// zurück, statt den Prozess zu riskieren.
const long MaxExtractBytes = 15L * 1024 * 1024;
var probe = await blobs.ProbeAsync(blobPath, ct);
if (probe.Exists && probe.SizeBytes > MaxExtractBytes)
{
_log.LogInformation(
"Text extraction skipped for {Path}: {Size} bytes exceeds {Cap} byte cap.",
blobPath, probe.SizeBytes, MaxExtractBytes);
return null;
}

using var ms = new MemoryStream();
await blobs.DownloadToAsync(blobPath, ms, ct);
ms.Position = 0;
Expand Down
Loading