1use crate::code::CodeIndex;
4use crate::code::symbols::CodeChunk;
5use crate::error::Result;
6
7#[derive(Debug, Clone)]
9pub struct CodeContext {
10 pub text: String,
12 pub chunks: Vec<CodeChunk>,
14 pub tokens: usize,
16}
17
18impl CodeContext {
19 pub fn as_system_prompt(&self) -> String {
21 format!(
22 "You have access to the following code context:\n\n{}",
23 self.text
24 )
25 }
26
27 pub fn as_user_context(&self) -> String {
29 format!("Relevant code:\n\n{}\n\nQuestion: ", self.text)
30 }
31}
32
33pub struct ContextBuilder<'a> {
35 code_index: &'a CodeIndex,
36 question: String,
37 max_tokens: usize,
38 max_chunks: usize,
39}
40
41impl<'a> ContextBuilder<'a> {
42 pub(crate) fn new(code_index: &'a CodeIndex, question: String) -> Self {
43 Self {
44 code_index,
45 question,
46 max_tokens: 4000,
47 max_chunks: 10,
48 }
49 }
50
51 pub fn max_tokens(mut self, n: usize) -> Self {
53 self.max_tokens = n;
54 self
55 }
56
57 pub fn max_chunks(mut self, n: usize) -> Self {
59 self.max_chunks = n;
60 self
61 }
62
63 pub async fn build(self) -> Result<CodeContext> {
65 let hits = self
66 .code_index
67 .collection
68 .search(&self.question)
69 .limit(self.max_chunks)
70 .run()
71 .await?;
72
73 let mut chunks = Vec::new();
74 let mut text = String::new();
75 let mut approx_tokens = 0;
76
77 for hit in hits {
78 let chunk = hit.doc;
79 let chunk_tokens = chunk.code.len() / 4;
81
82 if approx_tokens + chunk_tokens > self.max_tokens {
83 break;
84 }
85
86 text.push_str(&format!(
88 "// File: {} (lines {}-{})\n",
89 chunk.file, chunk.start_line, chunk.end_line
90 ));
91 if let Some(ref name) = chunk.name {
92 text.push_str(&format!("// Symbol: {} ({:?})\n", name, chunk.kind));
93 }
94 text.push_str(&chunk.code);
95 text.push_str("\n\n");
96
97 approx_tokens += chunk_tokens;
98 chunks.push(chunk);
99 }
100
101 Ok(CodeContext {
102 text,
103 chunks,
104 tokens: approx_tokens,
105 })
106 }
107}