use std::{
collections::{BTreeMap, HashMap},
fmt::Debug,
};
type Map = BTreeMap<String, Box<dyn ParamFull>>;
#[derive(Default, Debug)]
pub struct Parameters {
map: Map,
}
impl Clone for Parameters {
fn clone(&self) -> Self {
let mut map = Map::new();
for (key, value) in self.map.iter() {
map.insert(key.clone(), value.boxed_clone());
}
Self { map }
}
}
impl PartialEq for Parameters {
fn eq(&self, other: &Self) -> bool {
self.map.keys().len() == other.map.keys().len()
&& self.map.iter().all(|(k, v)| {
if let Some(other_v) = other.map.get(k) {
v.get() == other_v.get()
} else {
false
}
})
}
}
pub trait Param: Send + Sync {
fn get(&self) -> String;
}
#[doc(hidden)]
pub trait ParamFull: Param + Debug + Send + Sync {
#[doc(hidden)]
fn boxed_clone(&self) -> Box<dyn ParamFull + Send>;
}
impl<T: Param + Debug + Clone + 'static> ParamFull for T {
#[doc(hidden)]
fn boxed_clone(&self) -> Box<dyn ParamFull + Send> {
Box::new(self.clone())
}
}
#[derive(Debug, Clone)]
struct StringParam {
value: String,
}
impl StringParam {
fn new(value: String) -> Self {
Self { value }
}
}
impl Param for StringParam {
fn get(&self) -> String {
self.value.clone()
}
}
const TEXT_KEY: &str = "text";
impl Parameters {
pub fn new() -> Parameters {
Default::default()
}
pub fn new_with_text<T: Into<String>>(text: T) -> Parameters {
let mut map = Map::new();
map.insert(
TEXT_KEY.to_string(),
Box::new(StringParam::new(text.into())),
);
Parameters { map }
}
pub fn with<K: Into<String>, V: Into<String>>(&self, key: K, value: V) -> Parameters {
let mut copy = self.clone();
copy.map
.insert(key.into(), Box::new(StringParam::new(value.into())));
copy
}
pub fn with_dynamic<K: Into<String>, V: ParamFull>(&self, key: K, value: V) -> Parameters {
let mut copy = self.clone();
copy.map.insert(key.into(), value.boxed_clone());
copy
}
pub fn with_text<K: Into<String>>(&self, text: K) -> Parameters {
self.with(TEXT_KEY, text)
}
pub fn combine(&self, other: &Parameters) -> Parameters {
let mut copy = self.clone();
for (key, value) in other.map.iter() {
copy.map.insert(key.clone(), value.boxed_clone());
}
copy
}
pub fn get(&self, key: &str) -> Option<String> {
self.map.get(key).map(|param| param.get())
}
pub fn get_text(&self) -> Option<String> {
self.get(TEXT_KEY)
}
pub(crate) fn to_tera(&self) -> tera::Context {
let mut context = tera::Context::new();
for (key, value) in self.map.iter() {
context.insert(key, &value.get());
}
context
}
fn from_seq<K, V, M>(m: M) -> Self
where
K: Into<String>,
V: Into<String>,
M: IntoIterator<Item = (K, V)>,
{
let mut map = Map::new();
for (k, v) in m.into_iter() {
map.insert(k.into(), Box::new(StringParam::new(v.into())));
}
Parameters { map }
}
}
impl From<String> for Parameters {
fn from(text: String) -> Self {
Parameters::new_with_text(text)
}
}
impl From<&str> for Parameters {
fn from(text: &str) -> Self {
Parameters::new_with_text(text)
}
}
impl From<HashMap<String, String>> for Parameters {
fn from(map: HashMap<String, String>) -> Self {
Parameters::from_seq(map.into_iter())
}
}
impl From<BTreeMap<String, String>> for Parameters {
fn from(map: BTreeMap<String, String>) -> Self {
Parameters::from_seq(map.into_iter())
}
}
impl From<Vec<(String, String)>> for Parameters {
fn from(data: Vec<(String, String)>) -> Self {
Parameters::from_seq(data.into_iter())
}
}
impl From<Vec<(&str, &str)>> for Parameters {
fn from(data: Vec<(&str, &str)>) -> Self {
Parameters::from_seq(data)
}
}
#[macro_export]
macro_rules! parameters {
() => {
$crate::Parameters::new()
};
($text:expr) => {
llm_chain::Parameters::new_with_text($text)
};
($($key:expr => $value:expr),+$(,)?) => {{
let mut params = $crate::Parameters::new();
$(
params = params.with($key, $value);
)+
params
}};
}