From a5a5304f3bed275bc2581e16f32a9a83ec1a2c6a Mon Sep 17 00:00:00 2001 From: Malte Herrmann Date: Wed, 7 Jan 2026 00:00:34 +0100 Subject: [PATCH 1/3] expose constants and re-export relevant model trait and get_available_models function --- src/lib.rs | 6 ++++++ src/llm/anthropic.rs | 4 ++-- src/llm/claude.rs | 4 ++-- src/llm/constants.rs | 17 +++++++++++++++++ src/llm/cursor.rs | 4 ++-- src/llm/mod.rs | 4 ++++ src/llm/openai.rs | 4 ++-- 7 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 src/llm/constants.rs diff --git a/src/lib.rs b/src/lib.rs index 85e16d1..0572caa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,8 @@ mod error; pub mod llm; + +// Re-export constants for convenient access +pub use llm::constants; + +// Re-export Model trait and get_available_models function for convenient access +pub use llm::{get_available_models, Model}; diff --git a/src/llm/anthropic.rs b/src/llm/anthropic.rs index a0618c3..a5a6f34 100644 --- a/src/llm/anthropic.rs +++ b/src/llm/anthropic.rs @@ -1,6 +1,6 @@ use crate::error::LLMError; -use super::{Model, ModelFactory}; +use super::{constants::names, Model, ModelFactory}; use std::env::var; @@ -18,7 +18,7 @@ impl ModelFactory for Anthropic { impl Model for Anthropic { fn get_name(&self) -> String { - "Anthropic API".into() + names::ANTHROPIC.into() } fn prompt(&self, _: &str) -> Result { diff --git a/src/llm/claude.rs b/src/llm/claude.rs index 5432fc8..0d3768f 100644 --- a/src/llm/claude.rs +++ b/src/llm/claude.rs @@ -2,7 +2,7 @@ use std::process::Command; use crate::{ error::LLMError, - llm::{Model, ModelFactory}, + llm::{constants::names, Model, ModelFactory}, }; const CLAUDE_CLI_NAME: &str = "claude"; @@ -22,7 +22,7 @@ impl ModelFactory for Claude { impl Model for Claude { fn get_name(&self) -> String { - "Claude CLI".into() + names::CLAUDE.into() } fn prompt(&self, input: &str) -> Result { diff --git a/src/llm/constants.rs b/src/llm/constants.rs new file mode 100644 index 0000000..2850960 --- /dev/null +++ b/src/llm/constants.rs @@ -0,0 +1,17 @@ +/// Model name constants that can be used to check against +/// model names in external projects. +/// +/// These constants match the values returned by `Model::get_name()`. +pub mod names { + /// The display name for the Cursor model: "Cursor CLI" + pub const CURSOR: &str = "Cursor CLI"; + + /// The display name for the Claude model: "Claude CLI" + pub const CLAUDE: &str = "Claude CLI"; + + /// The display name for the Anthropic model: "Anthropic API" + pub const ANTHROPIC: &str = "Anthropic API"; + + /// The display name for the OpenAI model: "OpenAI API" + pub const OPENAI: &str = "OpenAI API"; +} diff --git a/src/llm/cursor.rs b/src/llm/cursor.rs index 2a9c7fb..8dec72f 100644 --- a/src/llm/cursor.rs +++ b/src/llm/cursor.rs @@ -1,6 +1,6 @@ use crate::{ error::LLMError, - llm::{Model, ModelFactory}, + llm::{constants::names, Model, ModelFactory}, }; use std::process::Command; @@ -22,7 +22,7 @@ impl ModelFactory for CursorCLI { impl Model for CursorCLI { fn get_name(&self) -> String { - "Cursor CLI".into() + names::CURSOR.into() } fn prompt(&self, input: &str) -> Result { diff --git a/src/llm/mod.rs b/src/llm/mod.rs index 09aea66..1862ea2 100644 --- a/src/llm/mod.rs +++ b/src/llm/mod.rs @@ -6,6 +6,10 @@ mod claude; mod cursor; mod openai; +/// Model name constants that can be used to check against +/// model names in external projects. +pub mod constants; + use crate::{ error::LLMError, llm::{anthropic::Anthropic, claude::Claude, cursor::CursorCLI, openai::OpenAI}, diff --git a/src/llm/openai.rs b/src/llm/openai.rs index 383a0b3..3953972 100644 --- a/src/llm/openai.rs +++ b/src/llm/openai.rs @@ -2,7 +2,7 @@ use std::env::var; use crate::{ error::LLMError, - llm::{Model, ModelFactory}, + llm::{constants::names, Model, ModelFactory}, }; pub struct OpenAI { @@ -18,7 +18,7 @@ impl ModelFactory for OpenAI { impl Model for OpenAI { fn get_name(&self) -> String { - "OpenAI API".into() + names::OPENAI.into() } fn prompt(&self, _: &str) -> Result { From 4d742dfda2e691680da06654f1852f1782bf4d1e Mon Sep 17 00:00:00 2001 From: Malte Herrmann Date: Wed, 7 Jan 2026 00:19:20 +0100 Subject: [PATCH 2/3] add and export public constants for names --- src/error/mod.rs | 2 ++ src/llm/anthropic.rs | 9 +++++++-- src/llm/mod.rs | 3 +++ src/llm/openai.rs | 10 ++++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/error/mod.rs b/src/error/mod.rs index f6b1a55..c95ce5b 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -8,6 +8,8 @@ pub enum LLMError { BytesConversion(#[from] FromUtf8Error), #[error("cli not found: {0}")] CLINotFound(String), + #[error("empty credential: {0}")] + EmptyCredential(String), #[error("missing env variable: {0}")] Env(#[from] VarError), #[error("failed to prompt: {0}")] diff --git a/src/llm/anthropic.rs b/src/llm/anthropic.rs index a5a6f34..1572e40 100644 --- a/src/llm/anthropic.rs +++ b/src/llm/anthropic.rs @@ -1,16 +1,21 @@ use crate::error::LLMError; -use super::{constants::names, Model, ModelFactory}; +use super::{Model, ModelFactory, constants::names}; use std::env::var; +const ANTHROPIC_API_KEY: &'static str = "ANTHROPIC_API_KEY"; + pub struct Anthropic { api_key: String, } impl ModelFactory for Anthropic { fn init() -> Result { - let api_key = var("ANTHROPIC_API_KEY")?; + let api_key = var(ANTHROPIC_API_KEY)?.trim().to_string(); + if api_key.is_empty() { + return Err(LLMError::EmptyCredential(ANTHROPIC_API_KEY.into())); + } Ok(Self { api_key }) } diff --git a/src/llm/mod.rs b/src/llm/mod.rs index 1862ea2..bb855d7 100644 --- a/src/llm/mod.rs +++ b/src/llm/mod.rs @@ -39,6 +39,9 @@ pub trait Model: Send + Sync { /// Returns the available models in the current /// system context. +/// +/// TODO: ideally this should also include the lifeness check for the found models (e.g. sending a +/// simple prompt). pub fn get_available_models() -> Result>, LLMError> { let mut models: Vec> = vec![]; diff --git a/src/llm/openai.rs b/src/llm/openai.rs index 3953972..82787c5 100644 --- a/src/llm/openai.rs +++ b/src/llm/openai.rs @@ -2,16 +2,22 @@ use std::env::var; use crate::{ error::LLMError, - llm::{constants::names, Model, ModelFactory}, + llm::{Model, ModelFactory, constants::names}, }; +const OPENAI_API_KEY: &'static str = "OPENAI_API_KEY"; + pub struct OpenAI { api_key: String, } impl ModelFactory for OpenAI { fn init() -> Result { - let api_key = var("OPENAI_API_KEY")?; + let api_key = var(OPENAI_API_KEY)?.trim().to_string(); + if api_key.is_empty() { + return Err(LLMError::EmptyCredential(OPENAI_API_KEY.into())); + } + Ok(Self { api_key }) } } From cb1641612b0b3fb08b4f88b0bee8e1e8b14700f1 Mon Sep 17 00:00:00 2001 From: Malte Herrmann Date: Wed, 7 Jan 2026 00:22:03 +0100 Subject: [PATCH 3/3] align lifetimes across repo --- src/llm/claude.rs | 2 +- src/llm/constants.rs | 8 ++++---- src/llm/cursor.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/llm/claude.rs b/src/llm/claude.rs index 0d3768f..b003651 100644 --- a/src/llm/claude.rs +++ b/src/llm/claude.rs @@ -5,7 +5,7 @@ use crate::{ llm::{constants::names, Model, ModelFactory}, }; -const CLAUDE_CLI_NAME: &str = "claude"; +const CLAUDE_CLI_NAME: &'static str = "claude"; pub struct Claude {} diff --git a/src/llm/constants.rs b/src/llm/constants.rs index 2850960..b52cc8b 100644 --- a/src/llm/constants.rs +++ b/src/llm/constants.rs @@ -4,14 +4,14 @@ /// These constants match the values returned by `Model::get_name()`. pub mod names { /// The display name for the Cursor model: "Cursor CLI" - pub const CURSOR: &str = "Cursor CLI"; + pub const CURSOR: &'static str = "Cursor CLI"; /// The display name for the Claude model: "Claude CLI" - pub const CLAUDE: &str = "Claude CLI"; + pub const CLAUDE: &'static str = "Claude CLI"; /// The display name for the Anthropic model: "Anthropic API" - pub const ANTHROPIC: &str = "Anthropic API"; + pub const ANTHROPIC: &'static str = "Anthropic API"; /// The display name for the OpenAI model: "OpenAI API" - pub const OPENAI: &str = "OpenAI API"; + pub const OPENAI: &'static str = "OpenAI API"; } diff --git a/src/llm/cursor.rs b/src/llm/cursor.rs index 8dec72f..326dbd2 100644 --- a/src/llm/cursor.rs +++ b/src/llm/cursor.rs @@ -5,7 +5,7 @@ use crate::{ use std::process::Command; -const CURSOR_CLI_NAME: &str = "cursor-agent"; +const CURSOR_CLI_NAME: &'static str = "cursor-agent"; pub struct CursorCLI {}