use crate::error::RuChatError;
use crate::io::Io;
use crate::ollama::model::get_name;
use crate::options::get_options;
use clap::Parser;
use ollama_rs::{Ollama, generation::completion::request::GenerationRequest};
use std::iter::Iterator;
use std::{fs, io::Read};
use tokio_stream::StreamExt;
#[derive(Parser, Debug, Clone, Default, PartialEq)]
pub struct AskArgs {
#[clap(short, long, default_value = "qwen2.5-coder:14b")]
pub(crate) model: String,
#[clap(short, long)]
pub(crate) prompt: Option<String>,
#[clap(short, long, default_value_t = String::from("text"))]
pub(crate) output_format: String,
#[clap(short = 'i', long)]
pub(crate) text_files: Option<String>,
#[clap(short, long)]
pub(crate) options: Option<String>,
pub(crate) positional_prompt: Option<String>,
}
fn generate_prompt(args: &AskArgs) -> Result<String, RuChatError> {
let mut prompt = String::new();
if let Some(text_files) = &args.text_files {
text_files.split(',').try_for_each(|file| {
if prompt.is_empty() {
prompt.push_str("Concerning:\n");
}
let content = if file == "-" {
prompt.push_str("stdin:\n");
let stdin = std::io::stdin();
let mut handle = stdin.lock();
let mut content = String::new();
handle.read_to_string(&mut content)?;
content
} else {
prompt.push_str("file: ");
prompt.push_str(file);
prompt.push('\n');
fs::read_to_string(file)?
};
if content.starts_with("```") {
prompt.push_str(&content);
} else {
prompt.push_str("```\n");
prompt.push_str(&content);
prompt.push_str("\n```");
}
Ok::<(), RuChatError>(())
})?;
}
let question = args
.prompt
.as_deref()
.or(args.positional_prompt.as_deref())
.unwrap_or("What do you make of this?");
prompt.push_str(question);
Ok(prompt)
}
pub(crate) async fn ask(ollama: Ollama, args: &AskArgs) -> Result<(), RuChatError> {
let mut cio = Io::new();
let mut prompt =
if args.prompt.is_some() || args.positional_prompt.is_some() || args.text_files.is_some() {
generate_prompt(args)?
} else {
let mut input = String::new();
while let Ok(line) = cio.read_line(false).await {
if line.is_empty() {
break;
}
input += line.as_str();
}
input
};
if args.output_format != "text" {
prompt.push_str("\nPlease generate your response in valid ");
prompt.push_str(&args.output_format);
prompt.push_str(" output format.\n");
}
let model_name = get_name(&ollama, &args.model).await?;
let request =
GenerationRequest::new(model_name, prompt).options(get_options(&args.options).await?);
let mut stream = ollama.generate_stream(request).await?;
while let Some(res) = stream.next().await {
let responses = res?;
for resp in responses {
cio.write_line(&resp.response).await?;
}
}
Ok(())
}