use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use crate::error::CoreResult;
#[cfg(feature = "chat-backend")]
use crate::messages::Message;
pub type TokenCallback = Box<dyn FnMut(&str, u32, f64) + Send>;
#[derive(Debug, Clone)]
pub struct InferenceParams {
pub max_tokens: u32,
pub temperature: f32,
pub context_size: u32,
pub n_threads: u32,
}
impl Default for InferenceParams {
fn default() -> Self {
let default_threads = std::thread::available_parallelism()
.map(|n| (n.get() as u32).saturating_sub(2).max(1))
.unwrap_or(4);
Self {
max_tokens: 2048,
temperature: 0.7,
context_size: 4096,
n_threads: default_threads,
}
}
}
#[derive(Debug, Clone)]
pub struct GenerationResult {
pub text: String,
pub tokens_generated: u32,
pub prompt_tokens: u32,
pub tokens_per_sec: f64,
pub time_to_first_token_ms: f64,
pub generation_time_ms: f64,
}
pub trait LlmBackend: Send + Sync {
fn generate(
&self,
prompt: &str,
params: &InferenceParams,
abort: Arc<AtomicBool>,
on_token: TokenCallback,
) -> CoreResult<GenerationResult>;
fn tokenize_count(&self, text: &str) -> CoreResult<u32> {
Ok(estimate_tokens(text))
}
fn is_ready(&self) -> bool {
true
}
}
pub fn estimate_tokens(text: &str) -> u32 {
(text.chars().count() as u32 / 4).max(1)
}
#[cfg(feature = "chat-backend")]
#[async_trait::async_trait]
pub trait ChatBackend: Send + Sync {
async fn chat(
&self,
system: &str,
messages: &[Message],
params: &InferenceParams,
abort: Arc<AtomicBool>,
on_token: TokenCallback,
) -> CoreResult<GenerationResult>;
fn tokenize_count(&self, text: &str) -> u32 {
estimate_tokens(text)
}
fn is_ready(&self) -> bool {
true
}
}
#[derive(Clone)]
pub enum Backend {
Prompt(Arc<dyn LlmBackend>),
#[cfg(feature = "chat-backend")]
Chat(Arc<dyn ChatBackend>),
}
impl Backend {
pub fn is_ready(&self) -> bool {
match self {
Self::Prompt(backend) => backend.is_ready(),
#[cfg(feature = "chat-backend")]
Self::Chat(backend) => backend.is_ready(),
}
}
pub fn tokenize_count(&self, text: &str) -> u32 {
match self {
Self::Prompt(backend) => backend.tokenize_count(text).unwrap_or(0),
#[cfg(feature = "chat-backend")]
Self::Chat(backend) => backend.tokenize_count(text),
}
}
}
impl From<Arc<dyn LlmBackend>> for Backend {
fn from(backend: Arc<dyn LlmBackend>) -> Self {
Self::Prompt(backend)
}
}
#[cfg(feature = "chat-backend")]
impl From<Arc<dyn ChatBackend>> for Backend {
fn from(backend: Arc<dyn ChatBackend>) -> Self {
Self::Chat(backend)
}
}