using System.Text.Json; namespace StellaOps.AdvisoryAI.UnifiedSearch; public sealed record UnifiedChunk( string ChunkId, string DocId, string Kind, string Domain, string Title, string Body, float[]? Embedding, string? EntityKey, string? EntityType, string? Anchor, string? SectionPath, int SpanStart, int SpanEnd, DateTimeOffset? Freshness, JsonDocument Metadata); public sealed record UnifiedSearchRequest( string Q, int? K = null, UnifiedSearchFilter? Filters = null, bool IncludeSynthesis = true, bool IncludeDebug = false, AmbientContext? Ambient = null); public sealed record UnifiedSearchFilter { public IReadOnlyList? Domains { get; init; } public IReadOnlyList? EntityTypes { get; init; } public string? EntityKey { get; init; } public string? Product { get; init; } public string? Version { get; init; } public string? Service { get; init; } public IReadOnlyList? Tags { get; init; } public string? Tenant { get; init; } /// /// User scopes extracted from the authenticated request context. Used by /// DomainWeightCalculator to apply role-based domain biases (Sprint 106 / G6). /// Not serialized in API responses. /// public IReadOnlyList? UserScopes { get; init; } /// /// Optional user identifier for ephemeral session context carry-forward. /// public string? UserId { get; init; } } public sealed record AmbientContext { public string? CurrentRoute { get; init; } public IReadOnlyList? VisibleEntityKeys { get; init; } public IReadOnlyList? RecentSearches { get; init; } public string? SessionId { get; init; } public bool ResetSession { get; init; } public AmbientAction? LastAction { get; init; } } public sealed record AmbientAction { public string Action { get; init; } = string.Empty; public string? Source { get; init; } public string? QueryHint { get; init; } public string? Domain { get; init; } public string? EntityKey { get; init; } public string? Route { get; init; } public DateTimeOffset? OccurredAt { get; init; } } public sealed record SearchSuggestion(string Text, string Reason); public sealed record SearchRefinement(string Text, string Source); public sealed record ContextAnswer( string Status, string Code, string Summary, string Reason, string Evidence, IReadOnlyList? Citations = null, IReadOnlyList? Questions = null); public sealed record ContextAnswerCitation( string EntityKey, string Title, string Domain, string? Route = null); public sealed record ContextAnswerQuestion( string Query, string Kind = "follow_up"); public sealed record UnifiedSearchResponse( string Query, int TopK, IReadOnlyList Cards, SynthesisResult? Synthesis, UnifiedSearchDiagnostics Diagnostics, IReadOnlyList? Suggestions = null, IReadOnlyList? Refinements = null, ContextAnswer? ContextAnswer = null); public sealed record EntityCard { public string EntityKey { get; init; } = string.Empty; public string EntityType { get; init; } = string.Empty; public string Domain { get; init; } = "knowledge"; public string Title { get; init; } = string.Empty; public string Snippet { get; init; } = string.Empty; public double Score { get; init; } public string? Severity { get; init; } public IReadOnlyList Actions { get; init; } = []; public IReadOnlyDictionary? Metadata { get; init; } public IReadOnlyList Sources { get; init; } = []; public EntityCardPreview? Preview { get; init; } public IReadOnlyList Facets { get; init; } = []; public IReadOnlyList Connections { get; init; } = []; public IReadOnlyDictionary SynthesisHints { get; init; } = new Dictionary(StringComparer.Ordinal); } public sealed record EntityCardFacet { public string Domain { get; init; } = "knowledge"; public string Title { get; init; } = string.Empty; public string Snippet { get; init; } = string.Empty; public double Score { get; init; } public IReadOnlyDictionary? Metadata { get; init; } public IReadOnlyList Actions { get; init; } = []; } public sealed record EntityCardPreview( string ContentType, string Content, string? Language = null, IReadOnlyList? StructuredFields = null); public sealed record PreviewField(string Label, string Value, string? Severity = null); public sealed record EntityCardAction( string Label, string ActionType, string? Route = null, string? Command = null, bool IsPrimary = false); public sealed record SynthesisResult { public string Summary { get; init; } = string.Empty; public string Template { get; init; } = string.Empty; public string Confidence { get; init; } = "low"; public int SourceCount { get; init; } public IReadOnlyList DomainsCovered { get; init; } = []; public IReadOnlyList? Citations { get; init; } public double? GroundingScore { get; init; } } public sealed record SynthesisCitation { public int Index { get; init; } public string EntityKey { get; init; } = string.Empty; public string Title { get; init; } = string.Empty; } public sealed record UnifiedSearchDiagnostics( int FtsMatches, int VectorMatches, int EntityCardCount, long DurationMs, bool UsedVector, string Mode, QueryPlan? Plan = null, IReadOnlyList? Federation = null); public sealed record FederationBackendDiagnostic( string Backend, int ResultCount, long DurationMs, bool TimedOut, string Status); public sealed record QueryPlan { public string OriginalQuery { get; init; } = string.Empty; public string NormalizedQuery { get; init; } = string.Empty; public string Intent { get; init; } = "explore"; public IReadOnlyList DetectedEntities { get; init; } = []; public IReadOnlyDictionary DomainWeights { get; init; } = new Dictionary(StringComparer.Ordinal); public IReadOnlyDictionary ContextEntityBoosts { get; init; } = new Dictionary(StringComparer.Ordinal); } public sealed record EntityMention( string Value, string EntityType, int StartIndex, int Length);