use anyhow::{bail, Context, Result};
use super::labeling::SimpleLlm;
pub struct CliLlm {
model: String,
}
impl CliLlm {
pub fn new(model: impl Into<String>) -> Self {
Self {
model: model.into(),
}
}
pub fn model(&self) -> &str {
&self.model
}
}
#[async_trait::async_trait]
impl SimpleLlm for CliLlm {
async fn complete(&self, prompt: &str) -> Result<String> {
tracing::debug!(
model = %self.model,
prompt_len = prompt.len(),
"CliLlm: calling claude CLI"
);
let output = tokio::process::Command::new("claude")
.arg("-p")
.arg(prompt)
.arg("--model")
.arg(&self.model)
.output()
.await
.context(
"Failed to run `claude` CLI. Is it installed? \
Run: npm install -g @anthropic-ai/claude-code",
)?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("claude CLI failed (exit {}): {}", output.status, stderr);
}
let response = String::from_utf8_lossy(&output.stdout).to_string();
tracing::debug!(response_len = response.len(), "CliLlm: got response");
Ok(response)
}
}