91 lines
2.4 KiB
Rust
91 lines
2.4 KiB
Rust
|
|
use anyhow::{Context, Result};
|
||
|
|
use grokboy_core::{ChatMessage, Config, stream_chat};
|
||
|
|
use std::io::{self, Write};
|
||
|
|
use std::process::ExitCode;
|
||
|
|
|
||
|
|
const SYSTEM: &str = "You are GrokBoy, a concise local coding assistant. Prefer clear, short answers. Traditional Chinese is welcome when the user writes in Chinese.";
|
||
|
|
|
||
|
|
#[tokio::main]
|
||
|
|
async fn main() -> ExitCode {
|
||
|
|
if let Err(err) = run().await {
|
||
|
|
eprintln!("error: {err:#}");
|
||
|
|
return ExitCode::FAILURE;
|
||
|
|
}
|
||
|
|
ExitCode::SUCCESS
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn run() -> Result<()> {
|
||
|
|
let mut args = std::env::args().skip(1);
|
||
|
|
let cmd = args.next().unwrap_or_else(|| "help".into());
|
||
|
|
match cmd.as_str() {
|
||
|
|
"chat" => cmd_chat().await,
|
||
|
|
"version" | "-V" | "--version" => {
|
||
|
|
println!("grokboy {}", env!("CARGO_PKG_VERSION"));
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
"help" | "-h" | "--help" | _ => {
|
||
|
|
print_help();
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn print_help() {
|
||
|
|
println!(
|
||
|
|
"\
|
||
|
|
GrokBoy — minimal local CLI agent (P0: chat)
|
||
|
|
|
||
|
|
USAGE:
|
||
|
|
grokboy chat Interactive streaming chat
|
||
|
|
grokboy version
|
||
|
|
grokboy help
|
||
|
|
|
||
|
|
ENV:
|
||
|
|
GROKBOY_API_KEY API key (or XAI_API_KEY / OPENAI_API_KEY)
|
||
|
|
GROKBOY_BASE_URL default https://api.x.ai/v1
|
||
|
|
GROKBOY_MODEL default grok-4
|
||
|
|
|
||
|
|
P0 has no tools yet. P1 will add shell/files + ReAct.
|
||
|
|
"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async fn cmd_chat() -> Result<()> {
|
||
|
|
let config = Config::from_env().map_err(anyhow::Error::msg)?;
|
||
|
|
println!(
|
||
|
|
"GrokBoy chat model={} base={}",
|
||
|
|
config.model, config.base_url
|
||
|
|
);
|
||
|
|
println!("Type a message. Empty line or /exit to quit.\n");
|
||
|
|
|
||
|
|
let mut history = vec![ChatMessage::system(SYSTEM)];
|
||
|
|
let stdin = io::stdin();
|
||
|
|
let mut stdout = io::stdout();
|
||
|
|
|
||
|
|
loop {
|
||
|
|
print!("you> ");
|
||
|
|
stdout.flush().ok();
|
||
|
|
let mut line = String::new();
|
||
|
|
if stdin.read_line(&mut line).context("stdin")? == 0 {
|
||
|
|
println!();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
let input = line.trim();
|
||
|
|
if input.is_empty() || input == "/exit" || input == "/quit" {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
history.push(ChatMessage::user(input));
|
||
|
|
|
||
|
|
print!("assistant> ");
|
||
|
|
stdout.flush().ok();
|
||
|
|
let reply = stream_chat(&config, &history, |delta| {
|
||
|
|
print!("{delta}");
|
||
|
|
let _ = io::stdout().flush();
|
||
|
|
})
|
||
|
|
.await?;
|
||
|
|
println!("\n");
|
||
|
|
history.push(ChatMessage::assistant(reply));
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|