use async_openai::error::OpenAIError;
use async_openai::types::{
ChatCompletionTool, ChatCompletionToolArgs, ChatCompletionToolType, FunctionObject,
FunctionObjectArgs,
};
use async_trait::async_trait;
pub use openai_func_embeddings::*;
pub use openai_func_enums_macros::*;
use serde_json::Value;
use std::error::Error;
use std::fmt::{self, Debug};
use std::sync::Arc;
use tokio::sync::mpsc;
pub trait EnumDescriptor {
fn name_with_token_count() -> &'static (&'static str, usize);
fn arg_description_with_token_count() -> &'static (&'static str, usize);
}
pub trait ToolSet {}
pub trait VariantDescriptors {
fn variant_names_with_token_counts(
) -> &'static (&'static [&'static str], &'static [usize], usize, usize);
fn variant_name_with_token_count(&self) -> (&'static str, usize);
}
#[derive(Clone, Debug)]
pub enum ToolCallExecutionStrategy {
Parallel,
Async,
Synchronous,
}
#[derive(Debug)]
pub struct CommandError {
details: String,
}
impl CommandError {
pub fn new(msg: &str) -> CommandError {
CommandError {
details: msg.to_string(),
}
}
}
impl fmt::Display for CommandError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl Error for CommandError {}
impl From<OpenAIError> for CommandError {
fn from(error: OpenAIError) -> Self {
CommandError::new(&format!("OpenAI Error: {}", error))
}
}
pub struct Logger {
pub sender: mpsc::Sender<String>,
}
impl Logger {
pub async fn log(&self, message: String) {
let _ = self.sender.send(message).await;
}
}
pub async fn logger_task(mut receiver: mpsc::Receiver<String>) {
while let Some(message) = receiver.recv().await {
println!("{}", message);
}
}
#[async_trait]
pub trait RunCommand: Sync + Send {
async fn run(
&self,
execution_strategy: ToolCallExecutionStrategy,
arguments: Option<Vec<String>>,
logger: Arc<Logger>,
system_message: Option<(String, usize)>,
) -> Result<
(Option<String>, Option<Vec<String>>),
Box<dyn std::error::Error + Send + Sync + 'static>,
>;
}
#[macro_export]
macro_rules! parse_function_call {
($func_call:expr, $type:ty) => {
match serde_json::from_str::<$type>($func_call.arguments.as_str()) {
Ok(response) => Some(response),
Err(e) => {
println!("Failed to parse function call: {}", e);
None
}
}
};
}
pub fn get_function_chat_completion_args(
func: impl Fn() -> (Value, usize),
) -> Result<(Vec<FunctionObject>, usize), OpenAIError> {
let (func_json, total_tokens) = func();
let mut chat_completion_functions_vec = Vec::new();
let values = match func_json {
Value::Object(_) => vec![func_json],
Value::Array(arr) => arr,
_ => {
return Err(OpenAIError::InvalidArgument(String::from(
"Something went wrong parsing the json",
)))
}
};
for value in values {
let parameters = value.get("parameters").cloned();
let description = value
.get("description")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let name = value.get("name").unwrap().as_str().unwrap().to_string();
let chat_completion_args = match description {
Some(desc) => FunctionObjectArgs::default()
.name(name)
.description(desc)
.parameters(parameters)
.build()?,
None => FunctionObjectArgs::default()
.name(name)
.parameters(parameters)
.build()?,
};
chat_completion_functions_vec.push(chat_completion_args);
}
Ok((chat_completion_functions_vec, total_tokens))
}
pub fn get_tool_chat_completion_args(
tool_func: impl Fn() -> (Value, usize),
) -> Result<(Vec<ChatCompletionTool>, usize), OpenAIError> {
let (tool_json, total_tokens) = tool_func();
let mut chat_completion_tool_vec = Vec::new();
let values = match tool_json {
Value::Object(_) => vec![tool_json],
Value::Array(arr) => arr,
_ => {
return Err(OpenAIError::InvalidArgument(String::from(
"Something went wrong parsing the json",
)))
}
};
for value in values {
let parameters = value.get("parameters").cloned();
let description = value
.get("description")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let name = value.get("name").unwrap().as_str().unwrap().to_string();
if name != "GPT" {
let chat_completion_functions_args = match description {
Some(desc) => FunctionObjectArgs::default()
.name(name)
.description(desc)
.parameters(parameters)
.build()?,
None => FunctionObjectArgs::default()
.name(name)
.parameters(parameters)
.build()?,
};
let chat_completion_tool = ChatCompletionToolArgs::default()
.r#type(ChatCompletionToolType::Function)
.function(chat_completion_functions_args)
.build()?;
chat_completion_tool_vec.push(chat_completion_tool);
}
}
Ok((chat_completion_tool_vec, total_tokens))
}
pub fn get_tools_limited(
tool_func: impl Fn(Vec<String>, Option<Vec<String>>) -> (Value, usize),
allowed_func_names: Vec<String>,
required_func_names: Option<Vec<String>>,
) -> Result<(Vec<ChatCompletionTool>, usize), OpenAIError> {
let (tool_json, total_tokens) = tool_func(allowed_func_names, required_func_names);
let mut chat_completion_tool_vec = Vec::new();
let values = match tool_json {
Value::Object(_) => vec![tool_json],
Value::Array(arr) => arr,
_ => {
return Err(OpenAIError::InvalidArgument(String::from(
"Something went wrong parsing the json",
)))
}
};
for value in values {
let parameters = value.get("parameters").cloned();
let description = value
.get("description")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
let name = value.get("name").unwrap().as_str().unwrap().to_string();
if name != "GPT" {
let chat_completion_functions_args = match description {
Some(desc) => FunctionObjectArgs::default()
.name(name)
.description(desc)
.parameters(parameters)
.build()?,
None => FunctionObjectArgs::default()
.name(name)
.parameters(parameters)
.build()?,
};
let chat_completion_tool = ChatCompletionToolArgs::default()
.r#type(ChatCompletionToolType::Function)
.function(chat_completion_functions_args)
.build()?;
chat_completion_tool_vec.push(chat_completion_tool);
}
}
Ok((chat_completion_tool_vec, total_tokens))
}