relay_knowledge/domain/code/context/
mod.rs1use serde::{Deserialize, Serialize};
4
5use super::{
6 CodeQueryKind, CodeRepositorySelector, CodeRetrievalHit, CodeRetrievalLayer, DomainError,
7 FreshnessPolicy, RepositoryCodeRange, error::required_text,
8};
9
10pub const CODEGRAPH_CONTEXT_DEFAULT_LIMIT: usize = 8;
11pub const CODEGRAPH_CONTEXT_MAX_LIMIT: usize = 20;
12pub const CODEGRAPH_CONTEXT_MIN_BYTES: usize = 1024;
13pub const CODEGRAPH_CONTEXT_DEFAULT_MAX_BYTES: usize = 65_536;
14pub const CODEGRAPH_CONTEXT_MAX_BYTES: usize = 262_144;
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct CodeGraphContextRequest {
19 pub repository: CodeRepositorySelector,
20 pub query: String,
21 pub limit: usize,
22 pub freshness_policy: FreshnessPolicy,
23 pub max_context_bytes: usize,
24 #[serde(default = "default_include_code")]
25 pub include_code: bool,
26 #[serde(default)]
27 pub exclude_generated: bool,
28}
29
30impl CodeGraphContextRequest {
31 pub fn new(
33 repository: CodeRepositorySelector,
34 query: impl Into<String>,
35 limit: usize,
36 freshness_policy: FreshnessPolicy,
37 max_context_bytes: usize,
38 include_code: bool,
39 exclude_generated: bool,
40 ) -> Result<Self, DomainError> {
41 let limit = match limit {
42 1..=CODEGRAPH_CONTEXT_MAX_LIMIT => limit,
43 0 => return Err(DomainError::invalid("limit", "must be greater than zero")),
44 _ => {
45 return Err(DomainError::invalid(
46 "limit",
47 "must be 20 or less for codegraph context",
48 ));
49 }
50 };
51 let max_context_bytes = match max_context_bytes {
52 0..CODEGRAPH_CONTEXT_MIN_BYTES => {
53 return Err(DomainError::invalid(
54 "max_context_bytes",
55 "must be at least 1024 for codegraph context",
56 ));
57 }
58 CODEGRAPH_CONTEXT_MIN_BYTES..=CODEGRAPH_CONTEXT_MAX_BYTES => max_context_bytes,
59 _ => {
60 return Err(DomainError::invalid(
61 "max_context_bytes",
62 "must be 262144 or less",
63 ));
64 }
65 };
66
67 Ok(Self {
68 repository,
69 query: required_text("query", query)?,
70 limit,
71 freshness_policy,
72 max_context_bytes,
73 include_code,
74 exclude_generated,
75 })
76 }
77}
78
79fn default_include_code() -> bool {
80 true
81}
82
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub struct CodeGraphContextProvenance {
86 pub query_kind: CodeQueryKind,
87 pub retrieval_layers: Vec<CodeRetrievalLayer>,
88 pub score: f64,
89}
90
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct CodeGraphCodeExcerpt {
94 pub path: String,
95 pub language_id: String,
96 pub line_range: RepositoryCodeRange,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub symbol_snapshot_id: Option<String>,
99 pub provenance: CodeGraphContextProvenance,
100 pub excerpt: String,
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct CodeGraphImpactHint {
106 pub path: String,
107 pub line_range: RepositoryCodeRange,
108 pub relationship: String,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub symbol_snapshot_id: Option<String>,
111 pub retrieval_layers: Vec<CodeRetrievalLayer>,
112 pub score: f64,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct CodeGraphContextBudget {
118 pub limit: usize,
119 pub max_context_bytes: usize,
120 pub candidate_count: usize,
121 pub returned_count: usize,
122 pub context_bytes: usize,
123 pub elapsed_ms: u64,
124}
125
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
128pub struct CodeGraphContextPack {
129 #[serde(default, skip_serializing_if = "Vec::is_empty")]
130 pub business_context: Vec<crate::domain::BusinessTerm>,
131 pub entry_points: Vec<CodeRetrievalHit>,
132 pub related_symbols: Vec<CodeRetrievalHit>,
133 pub graph_paths: Vec<CodeRetrievalHit>,
134 pub impact_hints: Vec<CodeGraphImpactHint>,
135 pub code_excerpts: Vec<CodeGraphCodeExcerpt>,
136}
137
138#[cfg(test)]
139mod mod_tests;