use crate::error::RuChatError;
use crate::io::Io;
use crate::ollama::model::get_name;
use clap::Parser;
use ollama_rs::models::ModelOptions;
use ollama_rs::{
Ollama,
coordinator::Coordinator,
generation::{
chat::ChatMessage,
parameters::{FormatType, JsonSchema, JsonStructure},
},
};
use serde::Deserialize;
use std::path::PathBuf;
#[derive(Parser, Debug, Clone, PartialEq)]
pub struct FuncStructArgs {
#[clap(short, long, default_value = "qwen2.5-coder:14b")]
pub(crate) model: String,
}
#[ollama_rs::function]
async fn get_weather(city: String) -> Result<String, Box<dyn std::error::Error + Sync + Send>> {
println!("Get weather function called for {city}");
Ok(
reqwest::get(format!("https://wttr.in/{city}?format=%C+%t+%w+%P"))
.await?
.text()
.await?,
)
}
#[ollama_rs::function]
async fn get_available_space(
path: PathBuf,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
Ok(fs2::available_space(path).map_or_else(
|err| format!("failed to get available space: {err}"),
|space| space.to_string(),
))
}
pub(crate) async fn func_struct(ollama: Ollama, args: &FuncStructArgs) -> Result<(), RuChatError> {
let history = vec![];
let model_name = get_name(&ollama, &args.model).await?;
let format = FormatType::StructuredJson(JsonStructure::new::<Weather>());
let mut coordinator = Coordinator::new(ollama, model_name.to_string(), history)
.add_tool(get_weather)
.add_tool(get_available_space)
.format(format)
.options(ModelOptions::default().temperature(0.0));
let mut cio = Io::new();
cio.write_line("Ask about the weather somewhere or 'q' to quit:")
.await?;
loop {
let input = cio.read_line(true).await?;
if input.eq_ignore_ascii_case("q") {
break;
}
let response = coordinator
.chat(vec![ChatMessage::user(input)])
.await
.unwrap();
cio.write_line(&response.message.content).await?;
}
Ok(())
}
#[allow(dead_code)]
#[derive(JsonSchema, Deserialize, Debug)]
struct Weather {
city: String,
temperature_units: String,
temperature: f32,
wind_units: String,
wind: f32,
pressure_units: String,
pressure: f32,
}