use crate::storage::DeveloperProfile;
use crate::semantic::CodeEmbedding;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AIContext {
pub profile: Option<DeveloperProfile>,
pub relevant_code: Vec<CodeSnippet>,
pub conversation_history: Vec<Message>,
pub metadata: ContextMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeSnippet {
pub content: String,
pub language: String,
pub source_file: String,
pub relevance_score: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: MessageRole,
pub content: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageRole {
User,
Assistant,
System,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ContextMetadata {
pub query_type: Option<String>,
pub technologies: Vec<String>,
pub repositories: Vec<String>,
}
impl AIContext {
pub fn new() -> Self {
Self {
profile: None,
relevant_code: Vec::new(),
conversation_history: Vec::new(),
metadata: ContextMetadata::default(),
}
}
pub fn with_profile(mut self, profile: DeveloperProfile) -> Self {
self.profile = Some(profile);
self
}
pub fn with_code(mut self, embeddings: &[CodeEmbedding], top_k: usize) -> Self {
self.relevant_code = embeddings.iter()
.take(top_k)
.map(|e| CodeSnippet {
content: e.content.clone(),
language: e.metadata.language.clone(),
source_file: e.metadata.source_file.clone(),
relevance_score: 1.0, })
.collect();
self
}
pub fn add_message(&mut self, role: MessageRole, content: String) {
self.conversation_history.push(Message {
role,
content,
timestamp: chrono::Utc::now(),
});
}
pub fn format_context(&self) -> String {
let mut context = String::new();
if let Some(profile) = &self.profile {
context.push_str("## Developer Context\n");
context.push_str(&format!("Primary Languages: {}\n",
profile.github.primary_languages.iter()
.take(3)
.map(|(l, _)| l.as_str())
.collect::<Vec<_>>()
.join(", ")
));
if !profile.coding_patterns.preferred_languages.is_empty() {
context.push_str(&format!("Preferred Languages: {}\n",
profile.coding_patterns.preferred_languages.join(", ")
));
}
context.push('\n');
}
if !self.relevant_code.is_empty() {
context.push_str("## Relevant Code Examples\n\n");
for (i, snippet) in self.relevant_code.iter().enumerate() {
context.push_str(&format!("### Example {} ({} - {})\n",
i + 1,
snippet.language,
snippet.source_file
));
context.push_str(&format!("```{}\\n{}\\n```\\n\\n",
snippet.language.to_lowercase(),
snippet.content
));
}
}
context
}
pub fn format_history(&self, max_messages: usize) -> String {
let start = self.conversation_history.len().saturating_sub(max_messages);
self.conversation_history[start..]
.iter()
.map(|m| format!("{}: {}", format_role(&m.role), m.content))
.collect::<Vec<_>>()
.join("\n")
}
}
fn format_role(role: &MessageRole) -> &'static str {
match role {
MessageRole::User => "User",
MessageRole::Assistant => "Assistant",
MessageRole::System => "System",
}
}