Skip to main content

recursive/
config.rs

1//! Runtime configuration.
2//!
3//! All of these can be overridden via env vars or CLI flags. Sensible
4//! defaults make the binary runnable with just `RECURSIVE_API_KEY` and
5//! `RECURSIVE_MODEL` set.
6
7use std::path::PathBuf;
8
9use crate::error::{Error, Result};
10
11#[derive(Debug, Clone)]
12pub struct Config {
13    pub workspace: PathBuf,
14    pub api_base: String,
15    pub api_key: Option<String>,
16    pub model: String,
17    pub max_steps: usize,
18    pub temperature: f64,
19    pub system_prompt: String,
20}
21
22impl Config {
23    /// Load from environment. The API key is optional here so commands that
24    /// don't need the LLM (e.g. `tools`, future offline ones) still run.
25    pub fn from_env() -> Result<Self> {
26        let workspace = std::env::var("RECURSIVE_WORKSPACE")
27            .map(PathBuf::from)
28            .unwrap_or_else(|_| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));
29
30        let api_base = std::env::var("RECURSIVE_API_BASE")
31            .or_else(|_| std::env::var("OPENAI_API_BASE"))
32            .unwrap_or_else(|_| "https://api.openai.com/v1".into());
33
34        let api_key = std::env::var("RECURSIVE_API_KEY")
35            .or_else(|_| std::env::var("OPENAI_API_KEY"))
36            .ok();
37
38        let model = std::env::var("RECURSIVE_MODEL")
39            .or_else(|_| std::env::var("OPENAI_MODEL"))
40            .unwrap_or_else(|_| "gpt-4o-mini".into());
41
42        let max_steps = std::env::var("RECURSIVE_MAX_STEPS")
43            .ok()
44            .and_then(|s| s.parse().ok())
45            .unwrap_or(32);
46
47        let temperature = std::env::var("RECURSIVE_TEMPERATURE")
48            .ok()
49            .and_then(|s| s.parse().ok())
50            .unwrap_or(0.2);
51
52        let system_prompt = match std::env::var("RECURSIVE_SYSTEM_PROMPT_FILE") {
53            Ok(path) => std::fs::read_to_string(&path)
54                .map_err(|e| Error::Config(format!("read system prompt {path}: {e}")))?,
55            Err(_) => default_system_prompt(),
56        };
57
58        Ok(Self {
59            workspace,
60            api_base,
61            api_key,
62            model,
63            max_steps,
64            temperature,
65            system_prompt,
66        })
67    }
68
69    /// Return the API key or a descriptive error if none was configured.
70    pub fn require_api_key(&self) -> Result<&str> {
71        self.api_key
72            .as_deref()
73            .ok_or_else(|| Error::Config("set RECURSIVE_API_KEY (or OPENAI_API_KEY)".into()))
74    }
75}
76
77pub fn default_system_prompt() -> String {
78    "You are Recursive, a minimal but capable coding agent. \
79    You have these tools: read_file, write_file, list_dir, run_shell. \
80    Work inside the workspace; never reach outside. \
81    Plan briefly, then act with tool calls. \
82    Before writing code, read the relevant existing files. \
83    After changes, run the project's tests via run_shell to verify. \
84    Stop calling tools and write a final summary once the task is done."
85        .to_string()
86}