1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![doc(html_favicon_url = "https://zino.cc/assets/zino-logo.png")]
4#![doc(html_logo_url = "https://zino.cc/assets/zino-logo.svg")]
5#![allow(async_fn_in_trait)]
6
7use toml::Table;
8use zino_core::{
9 LazyLock, Map, application::StaticRecord, error::Error, extension::TomlTableExt, state::State,
10};
11
12mod client;
13
14mod openai;
16
17pub use client::Chatbot;
18use openai::OpenAiChatCompletion;
19
20pub trait ChatbotService {
22 fn try_new_chatbot(config: &Table) -> Result<Chatbot, Error>;
25
26 fn model(&self) -> &str;
28
29 async fn try_send(&self, message: String, options: Option<Map>) -> Result<Vec<String>, Error>;
31}
32
33#[derive(Debug, Clone, Copy, Default)]
35pub struct GlobalChatbot;
36
37impl GlobalChatbot {
38 #[inline]
40 pub fn get(name: &str) -> Option<&'static Chatbot> {
41 SHARED_CHATBOT_SERVICES.find(name)
42 }
43}
44
45static SHARED_CHATBOT_SERVICES: LazyLock<StaticRecord<Chatbot>> = LazyLock::new(|| {
47 let mut chatbot_services = StaticRecord::new();
48 if let Some(chatbots) = State::shared().config().get_array("chatbot") {
49 for chatbot in chatbots.iter().filter_map(|v| v.as_table()) {
50 let service = chatbot.get_str("service").unwrap_or("unkown");
51 let name = chatbot.get_str("name").unwrap_or(service);
52 let chatbot_service = Chatbot::try_new(service, chatbot)
53 .unwrap_or_else(|err| panic!("fail to connect chatbot `{name}`: {err}"));
54 chatbot_services.add(name, chatbot_service);
55 }
56 }
57 chatbot_services
58});