#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![doc(html_favicon_url = "https://zino.cc/assets/zino-logo.png")]
#![doc(html_logo_url = "https://zino.cc/assets/zino-logo.svg")]
#![allow(async_fn_in_trait)]
#![forbid(unsafe_code)]
use toml::Table;
use zino_core::{
application::StaticRecord, error::Error, extension::TomlTableExt, state::State, LazyLock, Map,
};
mod client;
mod openai;
pub use client::Chatbot;
use openai::OpenAiChatCompletion;
pub trait ChatbotService {
fn try_new_chatbot(config: &Table) -> Result<Chatbot, Error>;
fn model(&self) -> &str;
async fn try_send(&self, message: String, options: Option<Map>) -> Result<Vec<String>, Error>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct GlobalChatbot;
impl GlobalChatbot {
#[inline]
pub fn get(name: &str) -> Option<&'static Chatbot> {
SHARED_CHATBOT_SERVICES.find(name)
}
}
static SHARED_CHATBOT_SERVICES: LazyLock<StaticRecord<Chatbot>> = LazyLock::new(|| {
let mut chatbot_services = StaticRecord::new();
if let Some(chatbots) = State::shared().config().get_array("chatbot") {
for chatbot in chatbots.iter().filter_map(|v| v.as_table()) {
let service = chatbot.get_str("service").unwrap_or("unkown");
let name = chatbot.get_str("name").unwrap_or(service);
let chatbot_service = Chatbot::try_new(service, chatbot)
.unwrap_or_else(|err| panic!("fail to connect chatbot `{name}`: {err}"));
chatbot_services.add(name, chatbot_service);
}
}
chatbot_services
});