save progress

This commit is contained in:
StellaOps Bot
2026-01-04 14:54:52 +02:00
parent c49b03a254
commit 3098e84de4
132 changed files with 19783 additions and 31 deletions

View File

@@ -1,5 +1,8 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.VexLens.Api;
using StellaOps.VexLens.Delta;
using StellaOps.VexLens.NoiseGate;
using StellaOps.VexLens.Storage;
namespace StellaOps.VexLens.WebService.Extensions;
@@ -73,6 +76,32 @@ public static class VexLensEndpointExtensions
.WithDescription("Get projections with conflicts")
.Produces<QueryProjectionsResponse>(StatusCodes.Status200OK);
// Delta/Noise-Gating endpoints
var deltaGroup = app.MapGroup("/api/v1/vexlens/deltas")
.WithTags("VexLens Delta")
.WithOpenApi();
deltaGroup.MapPost("/compute", ComputeDeltaAsync)
.WithName("ComputeDelta")
.WithDescription("Compute delta report between two snapshots")
.Produces<DeltaReportResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest);
var gatingGroup = app.MapGroup("/api/v1/vexlens/gating")
.WithTags("VexLens Gating")
.WithOpenApi();
gatingGroup.MapGet("/statistics", GetGatingStatisticsAsync)
.WithName("GetGatingStatistics")
.WithDescription("Get aggregated noise-gating statistics")
.Produces<AggregatedGatingStatisticsResponse>(StatusCodes.Status200OK);
gatingGroup.MapPost("/snapshots/{snapshotId}/gate", GateSnapshotAsync)
.WithName("GateSnapshot")
.WithDescription("Apply noise-gating to a snapshot")
.Produces<GatedSnapshotResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
// Issuer endpoints
var issuerGroup = app.MapGroup("/api/v1/vexlens/issuers")
.WithTags("VexLens Issuers")
@@ -265,6 +294,91 @@ public static class VexLensEndpointExtensions
return Results.Ok(conflictsOnly);
}
// Delta/Noise-Gating handlers
private static async Task<IResult> ComputeDeltaAsync(
[FromBody] ComputeDeltaRequest request,
[FromServices] INoiseGate noiseGate,
[FromServices] ISnapshotStore snapshotStore,
HttpContext context,
CancellationToken cancellationToken)
{
var tenantId = GetTenantId(context) ?? request.TenantId;
// Get snapshots
var fromSnapshot = await snapshotStore.GetAsync(request.FromSnapshotId, tenantId, cancellationToken);
var toSnapshot = await snapshotStore.GetAsync(request.ToSnapshotId, tenantId, cancellationToken);
if (fromSnapshot is null || toSnapshot is null)
{
return Results.BadRequest("One or both snapshot IDs not found");
}
// Compute delta
var options = NoiseGatingApiMapper.MapOptions(request.Options);
var delta = await noiseGate.DiffAsync(fromSnapshot, toSnapshot, options, cancellationToken);
return Results.Ok(NoiseGatingApiMapper.MapToResponse(delta));
}
private static async Task<IResult> GetGatingStatisticsAsync(
[FromQuery] string? tenantId,
[FromQuery] DateTimeOffset? fromDate,
[FromQuery] DateTimeOffset? toDate,
[FromServices] IGatingStatisticsStore statsStore,
HttpContext context,
CancellationToken cancellationToken)
{
var tenant = GetTenantId(context) ?? tenantId;
var stats = await statsStore.GetAggregatedAsync(tenant, fromDate, toDate, cancellationToken);
return Results.Ok(new AggregatedGatingStatisticsResponse(
TotalSnapshots: stats.TotalSnapshots,
TotalEdgesProcessed: stats.TotalEdgesProcessed,
TotalEdgesAfterDedup: stats.TotalEdgesAfterDedup,
AverageEdgeReductionPercent: stats.AverageEdgeReductionPercent,
TotalVerdicts: stats.TotalVerdicts,
TotalSurfaced: stats.TotalSurfaced,
TotalDamped: stats.TotalDamped,
AverageDampingPercent: stats.AverageDampingPercent,
ComputedAt: DateTimeOffset.UtcNow));
}
private static async Task<IResult> GateSnapshotAsync(
string snapshotId,
[FromBody] GateSnapshotRequest request,
[FromServices] INoiseGate noiseGate,
[FromServices] ISnapshotStore snapshotStore,
HttpContext context,
CancellationToken cancellationToken)
{
var tenantId = GetTenantId(context) ?? request.TenantId;
// Get the raw snapshot
var snapshot = await snapshotStore.GetRawAsync(snapshotId, tenantId, cancellationToken);
if (snapshot is null)
{
return Results.NotFound();
}
// Apply noise-gating
var gateRequest = new NoiseGateRequest
{
Graph = snapshot.Graph,
SnapshotId = snapshotId,
Verdicts = snapshot.Verdicts
};
var gatedSnapshot = await noiseGate.GateAsync(gateRequest, cancellationToken);
return Results.Ok(new GatedSnapshotResponse(
SnapshotId: gatedSnapshot.SnapshotId,
Digest: gatedSnapshot.Digest,
CreatedAt: gatedSnapshot.CreatedAt,
EdgeCount: gatedSnapshot.Edges.Count,
VerdictCount: gatedSnapshot.Verdicts.Count,
Statistics: NoiseGatingApiMapper.MapStatistics(gatedSnapshot.Statistics)));
}
// Issuer handlers
private static async Task<IResult> ListIssuersAsync(
[FromQuery] string? category,