Skip to content

Commit 7ab11be

Browse files
committed
feat: enhance Ruixen's state management with emoji reactions and query analysis
1 parent 2e5e367 commit 7ab11be

File tree

4 files changed

+158
-28
lines changed

4 files changed

+158
-28
lines changed

.idea/agentic.iml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/workspace.xml

Lines changed: 30 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/agentic-tui/src/ui/app.rs

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,43 @@ pub enum AgentMessage {
6767
CloudSynthesisComplete(Result<AtomicNote, CloudError>),
6868
}
6969

70+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71+
pub enum RuixenState {
72+
Resting, // 😴💤🌙 - Waiting for input, peaceful state
73+
Curious, // 🤨🧠💭 - Analyzing user query, thinking
74+
Working, // 😤💦📝 - Processing complex query, working hard
75+
Searching, // 🔍☁️⚡ - Cloud processing, searching for answers
76+
Celebrating, // 💎🚀🎯 - Successful synthesis, celebration
77+
Confused, // 😅🤦‍♂️📝 - Error state, but learning from it
78+
}
79+
80+
impl RuixenState {
81+
pub fn emoji_expression(&self) -> &'static str {
82+
match self {
83+
RuixenState::Resting => "😴 💤 🌙",
84+
RuixenState::Curious => "🤨 🧠 💭",
85+
RuixenState::Working => "😤 💦 📝",
86+
RuixenState::Searching => "🔍 ☁️ ⚡",
87+
RuixenState::Celebrating => "💎 🚀 🎯",
88+
RuixenState::Confused => "😅 🤦‍♂️ 📝",
89+
}
90+
}
91+
92+
pub fn from_agent_status(status: AgentStatus) -> Self {
93+
match status {
94+
AgentStatus::Ready => RuixenState::Resting,
95+
AgentStatus::Orchestrating => RuixenState::Curious,
96+
AgentStatus::Searching => RuixenState::Searching,
97+
AgentStatus::Complete => RuixenState::Celebrating,
98+
AgentStatus::LocalEndpointError | AgentStatus::CloudEndpointError => {
99+
RuixenState::Confused
100+
}
101+
AgentStatus::ValidatingLocal | AgentStatus::ValidatingCloud => RuixenState::Working,
102+
_ => RuixenState::Resting,
103+
}
104+
}
105+
}
106+
70107
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
71108
pub enum SettingsSelection {
72109
#[default]
@@ -129,6 +166,8 @@ pub struct App {
129166
cloud_tokens_used: u32, // Token count for current cloud request
130167
show_autocomplete: bool,
131168
autocomplete_index: usize,
169+
ruixen_reaction_state: Option<RuixenState>, // Temporary reaction state
170+
reaction_timer: Option<std::time::Instant>, // When reaction started,
132171
}
133172

134173
impl App {
@@ -162,6 +201,76 @@ impl App {
162201
cloud_tokens_used: 0,
163202
show_autocomplete: false,
164203
autocomplete_index: 0,
204+
ruixen_reaction_state: None,
205+
reaction_timer: None,
206+
}
207+
}
208+
209+
fn get_current_ruixen_emoji(&self) -> &'static str {
210+
// Check if we have a temporary reaction that should expire
211+
if let (Some(reaction), Some(timer)) = (&self.ruixen_reaction_state, &self.reaction_timer) {
212+
if timer.elapsed() <= std::time::Duration::from_millis(2000) {
213+
// 2 second reactions
214+
return reaction.emoji_expression();
215+
}
216+
}
217+
218+
// Default to agent status-based emoji
219+
RuixenState::from_agent_status(self.agent_status).emoji_expression()
220+
}
221+
222+
fn set_ruixen_reaction(&mut self, reaction: RuixenState) {
223+
self.ruixen_reaction_state = Some(reaction);
224+
self.reaction_timer = Some(std::time::Instant::now());
225+
}
226+
227+
fn cleanup_expired_reactions(&mut self) {
228+
if let (Some(_), Some(timer)) = (&self.ruixen_reaction_state, &self.reaction_timer) {
229+
if timer.elapsed() > std::time::Duration::from_millis(2000) {
230+
self.ruixen_reaction_state = None;
231+
self.reaction_timer = None;
232+
}
233+
}
234+
}
235+
236+
fn analyze_query_complexity(&self, query: &str) -> RuixenState {
237+
let word_count = query.split_whitespace().count();
238+
let has_questions = query.contains('?');
239+
let has_complex_words = query.split_whitespace().any(|word| word.len() > 10);
240+
let is_philosophical = query.to_lowercase().contains("why")
241+
|| query.to_lowercase().contains("how")
242+
|| query.to_lowercase().contains("what if");
243+
244+
// Determine Ruixen's initial reaction based on query complexity
245+
if word_count < 5 && !has_questions {
246+
RuixenState::Curious // 🤨🧠💭 - Simple query, just curious
247+
} else if (word_count > 15) || has_complex_words || is_philosophical {
248+
RuixenState::Working // 😤💦📝 - Complex query, need to work hard
249+
} else {
250+
RuixenState::Curious // 🤨🧠💭 - Standard query, thinking
251+
}
252+
}
253+
254+
fn analyze_synthesis_quality(&self, response: &AtomicNote) -> RuixenState {
255+
let body_length = response.body_text.len();
256+
let has_insights = response.body_text.to_lowercase().contains("insight")
257+
|| response.body_text.to_lowercase().contains("reveals")
258+
|| response.body_text.to_lowercase().contains("understanding");
259+
let has_technical_terms = response
260+
.body_text
261+
.split_whitespace()
262+
.any(|word| word.len() > 12 || word.contains("ology") || word.contains("tion"));
263+
let tag_count = response.header_tags.len();
264+
265+
// Determine Ruixen's reaction to the synthesis quality
266+
if body_length > 800 && has_insights && tag_count > 3 {
267+
RuixenState::Celebrating // 💎🚀🎯 - Excellent synthesis, celebration!
268+
} else if body_length > 400 && (has_insights || has_technical_terms) {
269+
RuixenState::Celebrating // 💎🚀🎯 - Good synthesis, satisfied
270+
} else if body_length < 200 {
271+
RuixenState::Confused // 😅🤦‍♂️📝 - Short response, maybe didn't work well
272+
} else {
273+
RuixenState::Curious // 🤨🧠💭 - Decent response, still thinking
165274
}
166275
}
167276

@@ -173,7 +282,7 @@ impl App {
173282
};
174283

175284
let block = Block::default()
176-
.title(" Synthesize Knowledge ")
285+
.title(format!(" {} ", self.get_current_ruixen_emoji()))
177286
.borders(Borders::ALL)
178287
.style(self.theme.ratatui_style(Element::Active));
179288

@@ -299,6 +408,9 @@ impl App {
299408

300409
pub async fn run(&mut self, terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
301410
while !self.should_quit {
411+
// Clean up expired reactions
412+
self.cleanup_expired_reactions();
413+
302414
self.draw(terminal)?;
303415

304416
// Handle validation messages from background tasks
@@ -496,7 +608,7 @@ impl App {
496608
);
497609

498610
let block = Block::default()
499-
.title(" Synthesis Complete ")
611+
.title(format!(" {} ", self.get_current_ruixen_emoji()))
500612
.borders(Borders::ALL)
501613
.style(self.theme.ratatui_style(Element::Active));
502614

@@ -519,6 +631,7 @@ impl App {
519631
commands: &self.get_filtered_slash_commands(),
520632
selected_index: self.autocomplete_index,
521633
},
634+
self.get_current_ruixen_emoji(),
522635
);
523636
}
524637
})?;
@@ -638,6 +751,10 @@ impl App {
638751
self.agent_status = AgentStatus::Ready;
639752
}
640753
AgentMessage::CloudSynthesisComplete(Ok(response)) => {
754+
// Analyze the synthesis quality and show reaction
755+
let reaction = self.analyze_synthesis_quality(&response);
756+
self.set_ruixen_reaction(reaction);
757+
641758
self.cloud_response = Some(response);
642759
self.mode = AppMode::Complete;
643760
self.agent_status = AgentStatus::Complete;
@@ -1079,6 +1196,10 @@ impl App {
10791196
// Store the original user query for metadata
10801197
self.original_user_query = message.clone();
10811198

1199+
// Analyze query complexity and show brief reaction
1200+
let reaction = self.analyze_query_complexity(&message);
1201+
self.set_ruixen_reaction(reaction);
1202+
10821203
// Estimate tokens for local request (rough: chars/4 + prompt overhead)
10831204
self.local_tokens_used = (message.len() / 4) as u32 + 500; // ~500 tokens for prompt template
10841205
self.cloud_tokens_used = 0; // Reset cloud tokens for new session

crates/agentic-tui/src/ui/chat.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@ pub fn render_chat(
7171
chat_input: &str,
7272
agent_status: AgentStatus,
7373
autocomplete: AutocompleteParams,
74+
ruixen_emoji: &str,
7475
) {
7576
let chat_block = Block::new()
7677
.borders(Borders::ALL)
77-
.title(" 🤨 🔍 💡 ")
78+
.title(format!(" {} ", ruixen_emoji))
7879
.style(theme.ratatui_style(Element::Text));
7980

8081
let inner_area = chat_block.inner(area);

0 commit comments

Comments
 (0)