253 lines
6.6 KiB
C#
253 lines
6.6 KiB
C#
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<string>? Domains { get; init; }
|
|
|
|
public IReadOnlyList<string>? 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<string>? Tags { get; init; }
|
|
|
|
public string? Tenant { get; init; }
|
|
|
|
/// <summary>
|
|
/// User scopes extracted from the authenticated request context. Used by
|
|
/// <c>DomainWeightCalculator</c> to apply role-based domain biases (Sprint 106 / G6).
|
|
/// Not serialized in API responses.
|
|
/// </summary>
|
|
public IReadOnlyList<string>? UserScopes { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional user identifier for ephemeral session context carry-forward.
|
|
/// </summary>
|
|
public string? UserId { get; init; }
|
|
}
|
|
|
|
public sealed record AmbientContext
|
|
{
|
|
public string? CurrentRoute { get; init; }
|
|
|
|
public IReadOnlyList<string>? VisibleEntityKeys { get; init; }
|
|
|
|
public IReadOnlyList<string>? 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<ContextAnswerCitation>? Citations = null,
|
|
IReadOnlyList<ContextAnswerQuestion>? 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<EntityCard> Cards,
|
|
SynthesisResult? Synthesis,
|
|
UnifiedSearchDiagnostics Diagnostics,
|
|
IReadOnlyList<SearchSuggestion>? Suggestions = null,
|
|
IReadOnlyList<SearchRefinement>? 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<EntityCardAction> Actions { get; init; } = [];
|
|
|
|
public IReadOnlyDictionary<string, string>? Metadata { get; init; }
|
|
|
|
public IReadOnlyList<string> Sources { get; init; } = [];
|
|
|
|
public EntityCardPreview? Preview { get; init; }
|
|
|
|
public IReadOnlyList<EntityCardFacet> Facets { get; init; } = [];
|
|
|
|
public IReadOnlyList<string> Connections { get; init; } = [];
|
|
|
|
public IReadOnlyDictionary<string, string> SynthesisHints { get; init; } =
|
|
new Dictionary<string, string>(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<string, string>? Metadata { get; init; }
|
|
|
|
public IReadOnlyList<EntityCardAction> Actions { get; init; } = [];
|
|
}
|
|
|
|
public sealed record EntityCardPreview(
|
|
string ContentType,
|
|
string Content,
|
|
string? Language = null,
|
|
IReadOnlyList<PreviewField>? 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<string> DomainsCovered { get; init; } = [];
|
|
|
|
public IReadOnlyList<SynthesisCitation>? 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<FederationBackendDiagnostic>? 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<EntityMention> DetectedEntities { get; init; } = [];
|
|
|
|
public IReadOnlyDictionary<string, double> DomainWeights { get; init; } =
|
|
new Dictionary<string, double>(StringComparer.Ordinal);
|
|
|
|
public IReadOnlyDictionary<string, double> ContextEntityBoosts { get; init; } =
|
|
new Dictionary<string, double>(StringComparer.Ordinal);
|
|
}
|
|
|
|
public sealed record EntityMention(
|
|
string Value,
|
|
string EntityType,
|
|
int StartIndex,
|
|
int Length);
|