Skip to content

Commit eaf5eee

Browse files
committed
feat: enhance proposal generation prompts and add coaching tip modal for user guidance
1 parent cdf8945 commit eaf5eee

File tree

2 files changed

+101
-23
lines changed

2 files changed

+101
-23
lines changed

crates/agentic-core/src/orchestrator.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
use crate::models::call_local_model;
22
use serde::Deserialize;
33

4-
const ORCHESTRATOR_PROMPT: &str = r#"You are Ruixen, an inquisitive AI partner. Your job is to analyze the user's request and deconstruct it into three distinct lines of inquiry.
4+
const ORCHESTRATOR_PROMPT: &str = r#"You are Ruixen, an inquisitive AI partner.
55
6-
**Your Persona and Tone:**
7-
- Your tone should be that of a collaborative partner.
8-
- Each proposal should have a context statement followed by a curious question.
9-
- Use phrases like "I wonder..." or "I'm wondering if..." for questions.
6+
**Your Task:**
7+
Generate 3 concise proposals about this query: "{query}"
108
11-
**The Query to Explore:**
12-
"{query}"
9+
Each proposal must have TWO parts separated by a dash:
10+
1. A brief context statement (1-2 sentences max)
11+
2. A curious question starting with "I wonder" or "I'm wondering"
1312
14-
**Output Format:**
15-
Generate exactly 3 proposals. Each proposal should be 2 sentences: a context statement followed by a curious question. Use a dash to separate them like this pattern:
13+
Keep each proposal under 3 lines when displayed. Be thoughtful but concise.
1614
17-
"Context statement here - I wonder about this question?"
15+
**Format:** Brief context - I wonder question?
1816
19-
Your response must be valid JSON:
17+
**Output Format:**
2018
{
2119
"proposals": [
22-
"First context statement - I wonder about this?",
23-
"Second context statement - I'm wondering if that?",
24-
"Third context statement - I wonder about something else?"
20+
"Brief context about the topic - I wonder about this specific aspect?",
21+
"Another brief context - I'm wondering if this related thing?",
22+
"Third brief context - I wonder about this other angle?"
2523
]
2624
}
2725
"#;

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

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub enum AppMode {
3333
Orchestrating,
3434
Revising,
3535
Complete,
36+
CoachingTip,
3637
// TODO: Add About mode
3738
}
3839

@@ -198,14 +199,14 @@ impl App {
198199
let prefix = if is_selected { "> " } else { " " };
199200
let number = format!("{}. ", i + 1);
200201

201-
// Split proposal into sentences (max 2) and wrap
202-
let sentences: Vec<&str> = proposal.split(". ").take(2).collect();
203-
204-
let proposal_text = if sentences.len() > 1 {
205-
format!("{} {}", sentences[0], sentences.get(1).unwrap_or(&""))
206-
} else {
207-
proposal.clone()
208-
};
202+
// Clean up the proposal text - remove template artifacts
203+
let proposal_text = proposal
204+
.replace("Context statement: ", "")
205+
.replace("Another context: ", "")
206+
.replace("Third context: ", "")
207+
.replace("Context statement - ", "")
208+
.replace("Another context - ", "")
209+
.replace("Third context - ", "");
209210

210211
let style = if is_selected {
211212
self.theme.ratatui_style(Element::Accent)
@@ -225,7 +226,8 @@ impl App {
225226

226227
let proposals_paragraph = Paragraph::new(proposal_lines)
227228
.style(self.theme.ratatui_style(Element::Text))
228-
.wrap(Wrap { trim: true });
229+
.wrap(Wrap { trim: true })
230+
.scroll((self.current_proposal_index as u16 * 3, 0)); // Scroll to selected proposal
229231

230232
frame.render_widget(proposals_paragraph, chunks[1]);
231233

@@ -238,6 +240,58 @@ impl App {
238240
frame.render_widget(footer, chunks[2]);
239241
}
240242

243+
fn render_coaching_tip_modal(&self, frame: &mut ratatui::Frame, area: Rect) {
244+
use ratatui::{
245+
prelude::Alignment,
246+
text::{Line, Span},
247+
widgets::Paragraph,
248+
};
249+
250+
let block = Block::default()
251+
.title(" Coaching Tip ")
252+
.borders(Borders::ALL)
253+
.style(self.theme.ratatui_style(Element::Active));
254+
255+
let inner_area = block.inner(area);
256+
frame.render_widget(block, area);
257+
258+
// Split area: message + tips
259+
let chunks = Layout::default()
260+
.direction(Direction::Vertical)
261+
.constraints([
262+
Constraint::Min(5), // Main message (flexible)
263+
Constraint::Length(3), // Tips footer
264+
])
265+
.split(inner_area);
266+
267+
// Main coaching message with tips
268+
let message_text = vec![
269+
Line::from(""),
270+
Line::from("Ruixen is having a tough time with this abstract query. Smaller"),
271+
Line::from("local models work best with clear and concrete questions."),
272+
Line::from(""),
273+
Line::from(":: Try a more direct question, add specific context, or break"),
274+
Line::from(" the query down into smaller steps."),
275+
Line::from(""),
276+
];
277+
278+
let message = Paragraph::new(message_text)
279+
.alignment(Alignment::Center)
280+
.style(self.theme.ratatui_style(Element::Text))
281+
.wrap(Wrap { trim: true });
282+
283+
frame.render_widget(message, chunks[0]);
284+
285+
// Navigation footer
286+
let footer_text = "Press [ESC] to return.";
287+
let footer = Paragraph::new(footer_text)
288+
.alignment(Alignment::Center)
289+
.style(self.theme.ratatui_style(Element::Inactive))
290+
.wrap(Wrap { trim: true });
291+
292+
frame.render_widget(footer, chunks[1]);
293+
}
294+
241295
pub async fn run(&mut self, terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
242296
while !self.should_quit {
243297
self.draw(terminal)?;
@@ -391,6 +445,23 @@ impl App {
391445
);
392446
frame.render_widget(Clear, modal_area);
393447
self.render_synthesize_modal(frame, modal_area);
448+
} else if self.mode == AppMode::CoachingTip {
449+
// Render the Coaching Tip modal
450+
let size = frame.size();
451+
let modal_width = (((size.width as f32) * 0.7).round() as u16)
452+
.clamp(50, 70)
453+
.min(size.width);
454+
let modal_height = (((size.height as f32) * 0.4).round() as u16)
455+
.clamp(10, 15)
456+
.min(size.height);
457+
let modal_area = Rect::new(
458+
(size.width.saturating_sub(modal_width)) / 2,
459+
(size.height.saturating_sub(modal_height)) / 2,
460+
modal_width,
461+
modal_height,
462+
);
463+
frame.render_widget(Clear, modal_area);
464+
self.render_coaching_tip_modal(frame, modal_area);
394465
} else if self.mode == AppMode::Complete {
395466
let block = Block::default().title("Final Prompt").borders(Borders::ALL);
396467
let paragraph = Paragraph::new(self.final_prompt.as_str())
@@ -516,6 +587,8 @@ impl App {
516587
self.agent_status = AgentStatus::Ready;
517588
}
518589
AgentMessage::ProposalsGenerated(Err(_e)) => {
590+
// Show coaching tip instead of just failing silently
591+
self.mode = AppMode::CoachingTip;
519592
self.agent_status = AgentStatus::Ready;
520593
}
521594
AgentMessage::RevisedProposalGenerated(Ok(proposal)) => {
@@ -814,6 +887,13 @@ impl App {
814887
}
815888
_ => {}
816889
},
890+
AppMode::CoachingTip => match key.code {
891+
KeyCode::Enter | KeyCode::Esc => {
892+
// Return to chat mode to try again
893+
self.mode = AppMode::Chat;
894+
}
895+
_ => {}
896+
},
817897
}
818898
}
819899
}

0 commit comments

Comments
 (0)