Implement MongoDB-based storage for Pack Run approval, artifact, log, and state management
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Added MongoPackRunApprovalStore for managing approval states with MongoDB.
- Introduced MongoPackRunArtifactUploader for uploading and storing artifacts.
- Created MongoPackRunLogStore to handle logging of pack run events.
- Developed MongoPackRunStateStore for persisting and retrieving pack run states.
- Implemented unit tests for MongoDB stores to ensure correct functionality.
- Added MongoTaskRunnerTestContext for setting up MongoDB test environment.
- Enhanced PackRunStateFactory to correctly initialize state with gate reasons.
This commit is contained in:
master
2025-11-07 10:01:35 +02:00
parent e5ffcd6535
commit a1ce3f74fa
122 changed files with 8730 additions and 914 deletions

View File

@@ -0,0 +1,8 @@
using System.Diagnostics;
namespace StellaOps.AdvisoryAI.Diagnostics;
internal static class AdvisoryAiActivitySource
{
public static readonly ActivitySource Instance = new("StellaOps.AdvisoryAI");
}

View File

@@ -1,4 +1,7 @@
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Linq;
using StellaOps.AdvisoryAI.Guardrails;
using StellaOps.AdvisoryAI.Outputs;
using StellaOps.AdvisoryAI.Orchestration;
@@ -53,27 +56,72 @@ internal sealed class AdvisoryPipelineExecutor : IAdvisoryPipelineExecutor
var prompt = await _promptAssembler.AssembleAsync(plan, cancellationToken).ConfigureAwait(false);
var guardrailResult = await _guardrailPipeline.EvaluateAsync(prompt, cancellationToken).ConfigureAwait(false);
var violationCount = guardrailResult.Violations.Length;
if (guardrailResult.Blocked)
{
_logger?.LogWarning(
"Guardrail blocked advisory pipeline output for {TaskType} on advisory {AdvisoryKey}",
"Guardrail blocked advisory pipeline output for {TaskType} on advisory {AdvisoryKey} with {ViolationCount} violations",
plan.Request.TaskType,
plan.Request.AdvisoryKey,
violationCount);
}
else if (violationCount > 0)
{
_logger?.LogInformation(
"Guardrail recorded {ViolationCount} advisory validation violations for {TaskType} on advisory {AdvisoryKey}",
violationCount,
plan.Request.TaskType,
plan.Request.AdvisoryKey);
}
var citationCoverage = CalculateCitationCoverage(plan, prompt);
var activity = Activity.Current;
activity?.SetTag("advisory.guardrail_blocked", guardrailResult.Blocked);
activity?.SetTag("advisory.validation_failures", violationCount);
activity?.SetTag("advisory.citation_coverage", citationCoverage);
_metrics.RecordGuardrailOutcome(plan.Request.TaskType, guardrailResult.Blocked, violationCount);
_metrics.RecordCitationCoverage(
plan.Request.TaskType,
citationCoverage,
prompt.Citations.Length,
plan.StructuredChunks.Length);
var generatedAt = _timeProvider.GetUtcNow();
var output = AdvisoryPipelineOutput.Create(plan, prompt, guardrailResult, generatedAt, planFromCache);
await _outputStore.SaveAsync(output, cancellationToken).ConfigureAwait(false);
_metrics.RecordGuardrailResult(plan.Request.TaskType, guardrailResult.Blocked);
_metrics.RecordOutputStored(plan.Request.TaskType, planFromCache, guardrailResult.Blocked);
_logger?.LogInformation(
"Stored advisory pipeline output {CacheKey} (task {TaskType}, cache:{CacheHit}, guardrail_blocked:{Blocked})",
"Stored advisory pipeline output {CacheKey} (task {TaskType}, cache:{CacheHit}, guardrail_blocked:{Blocked}, validation_failures:{ValidationFailures}, citation_coverage:{CitationCoverage:0.00})",
output.CacheKey,
plan.Request.TaskType,
planFromCache,
guardrailResult.Blocked);
guardrailResult.Blocked,
violationCount,
citationCoverage);
}
private static double CalculateCitationCoverage(AdvisoryTaskPlan plan, AdvisoryPrompt prompt)
{
var structuredCount = plan.StructuredChunks.Length;
if (structuredCount <= 0)
{
return 0d;
}
if (prompt.Citations.IsDefaultOrEmpty)
{
return 0d;
}
var uniqueCitations = prompt.Citations
.Select(citation => (citation.DocumentId, citation.ChunkId))
.Distinct()
.Count();
var coverage = (double)uniqueCitations / structuredCount;
return Math.Clamp(coverage, 0d, 1d);
}
}

View File

@@ -13,7 +13,10 @@ public sealed class AdvisoryPipelineMetrics : IDisposable
private readonly Counter<long> _plansProcessed;
private readonly Counter<long> _outputsStored;
private readonly Counter<long> _guardrailBlocks;
private readonly Counter<long> _validationFailures;
private readonly Histogram<double> _planBuildDuration;
private readonly Histogram<double> _pipelineLatencySeconds;
private readonly Histogram<double> _citationCoverageRatio;
private bool _disposed;
public AdvisoryPipelineMetrics(IMeterFactory meterFactory)
@@ -25,8 +28,11 @@ public sealed class AdvisoryPipelineMetrics : IDisposable
_plansQueued = _meter.CreateCounter<long>("advisory_plans_queued");
_plansProcessed = _meter.CreateCounter<long>("advisory_plans_processed");
_outputsStored = _meter.CreateCounter<long>("advisory_outputs_stored");
_guardrailBlocks = _meter.CreateCounter<long>("advisory_guardrail_blocks");
_guardrailBlocks = _meter.CreateCounter<long>("advisory_ai_guardrail_blocks_total");
_validationFailures = _meter.CreateCounter<long>("advisory_ai_validation_failures_total");
_planBuildDuration = _meter.CreateHistogram<double>("advisory_plan_build_duration_seconds");
_pipelineLatencySeconds = _meter.CreateHistogram<double>("advisory_ai_latency_seconds");
_citationCoverageRatio = _meter.CreateHistogram<double>("advisory_ai_citation_coverage_ratio");
}
public void RecordPlanCreated(double buildSeconds, AdvisoryTaskType taskType)
@@ -55,12 +61,40 @@ public sealed class AdvisoryPipelineMetrics : IDisposable
KeyValuePair.Create<string, object?>("guardrail_blocked", guardrailBlocked));
}
public void RecordGuardrailResult(AdvisoryTaskType taskType, bool blocked)
public void RecordGuardrailOutcome(AdvisoryTaskType taskType, bool blocked, int validationFailures)
{
if (blocked)
{
_guardrailBlocks.Add(1, KeyValuePair.Create<string, object?>("task_type", taskType.ToString()));
}
if (validationFailures > 0)
{
_validationFailures.Add(
validationFailures,
KeyValuePair.Create<string, object?>("task_type", taskType.ToString()));
}
}
public void RecordPipelineLatency(AdvisoryTaskType taskType, double seconds, bool planFromCache)
{
_pipelineLatencySeconds.Record(
seconds,
KeyValuePair.Create<string, object?>("task_type", taskType.ToString()),
KeyValuePair.Create<string, object?>("plan_cache_hit", planFromCache));
}
public void RecordCitationCoverage(
AdvisoryTaskType taskType,
double coverageRatio,
int citationCount,
int structuredChunkCount)
{
_citationCoverageRatio.Record(
coverageRatio,
KeyValuePair.Create<string, object?>("task_type", taskType.ToString()),
KeyValuePair.Create<string, object?>("citations", citationCount),
KeyValuePair.Create<string, object?>("structured_chunks", structuredChunkCount));
}
public void Dispose()

View File

@@ -6,11 +6,11 @@
| AIAI-31-003 | DONE (2025-11-04) | Advisory AI Guild | AIAI-31-001..002 | Implement deterministic toolset (version comparators, range checks, dependency analysis, policy lookup) exposed via orchestrator. | Tools validated with property tests; outputs cached; docs updated. |
| AIAI-31-004 | DONE (2025-11-04) | Advisory AI Guild | AIAI-31-001..003, AUTH-VULN-29-001 | Build orchestration pipeline for Summary/Conflict/Remediation tasks (prompt templates, tool calls, token budgets, caching). | Pipeline executes tasks deterministically; caches keyed by tuple+policy; integration tests cover tasks. |
| AIAI-31-004A | DONE (2025-11-04) | Advisory AI Guild, Platform Guild | AIAI-31-004, AIAI-31-002 | Wire `AdvisoryPipelineOrchestrator` into WebService/Worker, expose API/queue contracts, emit metrics, and stand up cache stub. | API returns plan metadata; worker executes queue message; metrics recorded; doc updated. |
| AIAI-31-004B | TODO | Advisory AI Guild, Security Guild | AIAI-31-004A, DOCS-AIAI-31-003, AUTH-AIAI-31-004 | Implement prompt assembler, guardrail plumbing, cache persistence, DSSE provenance; add golden outputs. | Deterministic outputs cached; guardrails enforced; tests cover prompt assembly + caching. |
| AIAI-31-004C | TODO | Advisory AI Guild, CLI Guild, Docs Guild | AIAI-31-004B, CLI-AIAI-31-003 | Deliver CLI `stella advise run <task>` command, renderers, documentation updates, and CLI golden tests. | CLI command produces deterministic output; docs published; smoke run recorded. |
| AIAI-31-004B | DONE (2025-11-06) | Advisory AI Guild, Security Guild | AIAI-31-004A, DOCS-AIAI-31-003, AUTH-AIAI-31-004 | Implement prompt assembler, guardrail plumbing, cache persistence, DSSE provenance; add golden outputs. | Deterministic outputs cached; guardrails enforced; tests cover prompt assembly + caching. |
| AIAI-31-004C | DONE (2025-11-06) | Advisory AI Guild, CLI Guild, Docs Guild | AIAI-31-004B, CLI-AIAI-31-003 | Deliver CLI `stella advise run <task>` command, renderers, documentation updates, and CLI golden tests. | CLI command produces deterministic output; docs published; smoke run recorded. |
| AIAI-31-005 | DONE (2025-11-04) | Advisory AI Guild, Security Guild | AIAI-31-004 | Implement guardrails (redaction, injection defense, output validation, citation enforcement) and fail-safe handling. | Guardrails block adversarial inputs; output validator enforces schemas; security tests pass. |
| AIAI-31-006 | DONE (2025-11-04) | Advisory AI Guild | AIAI-31-004..005 | Expose REST API endpoints (`/advisory/ai/*`) with RBAC, rate limits, OpenAPI schemas, and batching support. | Endpoints deployed with schema validation; rate limits enforced; integration tests cover error codes. |
| AIAI-31-007 | TODO | Advisory AI Guild, Observability Guild | AIAI-31-004..006 | Instrument metrics (`advisory_ai_latency`, `guardrail_blocks`, `validation_failures`, `citation_coverage`), logs, and traces; publish dashboards/alerts. | Telemetry live; dashboards approved; alerts configured. |
| AIAI-31-007 | DONE (2025-11-06) | Advisory AI Guild, Observability Guild | AIAI-31-004..006 | Instrument metrics (`advisory_ai_latency`, `guardrail_blocks`, `validation_failures`, `citation_coverage`), logs, and traces; publish dashboards/alerts. | Telemetry live; dashboards approved; alerts configured. |
| AIAI-31-008 | TODO | Advisory AI Guild, DevOps Guild | AIAI-31-006..007 | Package inference on-prem container, remote inference toggle, Helm/Compose manifests, scaling guidance, offline kit instructions. | Deployment docs merged; smoke deploy executed; offline kit updated; feature flags documented. |
| AIAI-31-010 | DONE (2025-11-02) | Advisory AI Guild | CONCELIER-VULN-29-001, EXCITITOR-VULN-29-001 | Implement Concelier advisory raw document provider mapping CSAF/OSV payloads into structured chunks for retrieval. | Provider resolves content format, preserves metadata, and passes unit tests covering CSAF/OSV cases. |
| AIAI-31-011 | DONE (2025-11-02) | Advisory AI Guild | EXCITITOR-LNM-21-201, EXCITITOR-CORE-AOC-19-002 | Implement Excititor VEX document provider to surface structured VEX statements for vector retrieval. | Provider returns conflict-aware VEX chunks with deterministic metadata and tests for representative statements. |
@@ -31,3 +31,5 @@
> 2025-11-04: AIAI-31-005 DONE guardrail pipeline redacts secrets, enforces citation/injection policies, emits block counters, and tests (`AdvisoryGuardrailPipelineTests`) cover redaction + citation validation.
> 2025-11-04: AIAI-31-006 DONE REST endpoints enforce header scopes, apply token bucket rate limiting, sanitize prompts via guardrails, and queue execution with cached metadata. Tests executed via `dotnet test src/AdvisoryAI/__Tests/StellaOps.AdvisoryAI.Tests/StellaOps.AdvisoryAI.Tests.csproj --no-restore`.
> 2025-11-06: AIAI-31-004B/C Resuming prompt/cache hardening and CLI integration; first focus on backend client wiring and deterministic CLI outputs before full suite.
> 2025-11-06: AIAI-31-004B/C DONE Advisory AI Mongo integration validated, backend client + CLI `advise run` wired, deterministic console renderer with provenance/guardrail display added, docs refreshed, and targeted CLI tests executed.