Skip to main content

opi_coding_agent/
prompt.rs

1//! System prompt construction (S8.4).
2//!
3//! Assembles the layered system prompt sent to the provider:
4//! 1. Base coding-agent instructions
5//! 2. Tool descriptions from ToolDef
6//! 3. User system prompt file
7
8use opi_ai::message::ToolDef;
9
10const BASE_INSTRUCTIONS: &str = "\
11You are opi, an expert coding agent. You help users with software engineering \
12tasks including reading, writing, and editing code, running commands, and \
13searching codebases. Be concise and precise. Explain your reasoning when \
14making changes.";
15
16/// Builder for assembling the system prompt from layered components.
17pub struct SystemPromptBuilder {
18    tools: Vec<ToolDef>,
19    user_system: Option<String>,
20}
21
22impl SystemPromptBuilder {
23    pub fn new() -> Self {
24        Self {
25            tools: Vec::new(),
26            user_system: None,
27        }
28    }
29
30    /// Add tool definitions. Their names and descriptions are included in the prompt.
31    pub fn tools(mut self, tools: Vec<ToolDef>) -> Self {
32        self.tools = tools;
33        self
34    }
35
36    /// Add user-provided system prompt content (from --system flag or config).
37    pub fn user_system(mut self, content: impl Into<String>) -> Self {
38        let s = content.into();
39        self.user_system = if s.is_empty() { None } else { Some(s) };
40        self
41    }
42
43    /// Return the collected tool definitions for `Request.tools`.
44    pub fn tool_definitions(&self) -> &[ToolDef] {
45        &self.tools
46    }
47
48    /// Assemble and return the full system prompt string.
49    pub fn build(self) -> String {
50        let mut parts = Vec::new();
51
52        // Layer 1: base instructions
53        parts.push(BASE_INSTRUCTIONS.to_owned());
54
55        // Layer 2: tool descriptions
56        if !self.tools.is_empty() {
57            let mut tool_section = String::from("Available tools:\n");
58            for tool in &self.tools {
59                tool_section.push_str(&format!("- {}: {}\n", tool.name, tool.description));
60            }
61            parts.push(tool_section);
62        }
63
64        // Layer 3: user system prompt
65        if let Some(user) = self.user_system {
66            parts.push(format!("User instructions:\n{}", user));
67        }
68
69        parts.join("\n\n")
70    }
71}
72
73impl Default for SystemPromptBuilder {
74    fn default() -> Self {
75        Self::new()
76    }
77}