pub mod chat_completion;
pub mod completion;
pub mod embedding;
pub mod hf_tokenizer;
pub mod infill;
pub mod openai_compat;
pub mod rerank;
pub mod tokenizer;
#[cfg(feature = "hf-tokenizer")]
#[cfg_attr(docsrs, doc(cfg(feature = "hf-tokenizer")))]
pub use self::hf_tokenizer::HfTokenizer;
pub use self::tokenizer::{FimTokens, LlamaTokenizer, Tokenizer};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use crate::backend::LlamaBackend;
use crate::context::{LlamaContext, LlamaContextParams};
use crate::error::Result;
use crate::model::params::LlamaModelParams;
use crate::model::LlamaModel;
pub use self::chat_completion::{
create_chat_completion, create_chat_completion_stream, create_chat_completion_stream_with,
ChatMessage,
};
pub use self::completion::{
create_completion, create_completion_stream, create_completion_stream_with_sampler,
create_completion_with_options, create_completion_with_sampler, Completion, CompletionChunk,
CompletionLogprobs, CompletionOptions, SamplingOptions, StopReason, StreamControl,
TokenLogprob,
};
#[derive(Debug)]
pub struct Llama {
_backend: LlamaBackend,
model: LlamaModel,
context: LlamaContext<'static>,
_not_send_sync: std::marker::PhantomData<*mut ()>,
}
impl Llama {
pub fn load(params: LlamaParams) -> Result<Self> {
let backend = LlamaBackend::init()?;
let model = LlamaModel::load_from_file(&backend, ¶ms.model_path, ¶ms.model)?;
let ctx = model.new_context(&backend, params.context.clone())?;
let ctx: LlamaContext<'static> =
unsafe { std::mem::transmute::<LlamaContext<'_>, LlamaContext<'static>>(ctx) };
Ok(Self {
_backend: backend,
model,
context: ctx,
_not_send_sync: std::marker::PhantomData,
})
}
#[must_use]
pub const fn model(&self) -> &LlamaModel {
&self.model
}
#[must_use]
pub const fn context(&mut self) -> &mut LlamaContext<'static> {
&mut self.context
}
pub fn create_completion(&mut self, prompt: &str, max_tokens: usize) -> Result<Completion> {
create_completion(self, prompt, max_tokens)
}
pub fn create_completion_with_options(
&mut self,
prompt: &str,
options: CompletionOptions,
) -> Result<Completion> {
create_completion_with_options(self, prompt, options)
}
pub fn create_completion_with_sampler(
&mut self,
prompt: &str,
options: CompletionOptions,
sampler: &mut crate::sampling::LlamaSampler,
) -> Result<Completion> {
create_completion_with_sampler(self, prompt, options, sampler)
}
pub fn create_completion_stream<F>(
&mut self,
prompt: &str,
options: CompletionOptions,
on_chunk: F,
) -> Result<Completion>
where
F: FnMut(CompletionChunk) -> StreamControl,
{
create_completion_stream(self, prompt, options, on_chunk)
}
pub fn create_completion_stream_with_sampler<F>(
&mut self,
prompt: &str,
options: CompletionOptions,
sampler: &mut crate::sampling::LlamaSampler,
on_chunk: F,
) -> Result<Completion>
where
F: FnMut(CompletionChunk) -> StreamControl,
{
create_completion_stream_with_sampler(self, prompt, options, sampler, on_chunk)
}
pub fn create_chat_completion(
&mut self,
messages: &[ChatMessage],
max_tokens: usize,
) -> Result<ChatMessage> {
create_chat_completion(self, messages, max_tokens)
}
pub fn create_chat_completion_stream<F>(
&mut self,
messages: &[ChatMessage],
max_tokens: usize,
on_chunk: F,
) -> Result<ChatMessage>
where
F: FnMut(CompletionChunk) -> StreamControl,
{
create_chat_completion_stream(self, messages, max_tokens, on_chunk)
}
pub fn create_chat_completion_stream_with<F>(
&mut self,
messages: &[ChatMessage],
template: crate::chat::BuiltinTemplate,
tools: &[crate::chat::ToolDefinition],
options: CompletionOptions,
on_chunk: F,
) -> Result<ChatMessage>
where
F: FnMut(CompletionChunk) -> StreamControl,
{
create_chat_completion_stream_with(self, messages, template, tools, options, on_chunk)
}
}
#[derive(Debug, Clone)]
pub struct LlamaParams {
pub model_path: PathBuf,
pub model: LlamaModelParams,
pub context: LlamaContextParams,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MobilePreset {
LowRam,
Balanced,
GpuMax,
}
impl FromStr for MobilePreset {
type Err = String;
fn from_str(value: &str) -> std::result::Result<Self, Self::Err> {
match value {
"low-ram" | "low_ram" | "lowram" => Ok(Self::LowRam),
"balanced" => Ok(Self::Balanced),
"gpu-max" | "gpu_max" | "gpumax" => Ok(Self::GpuMax),
other => Err(format!(
"unknown mobile preset: {other} (expected low-ram, balanced, or gpu-max)"
)),
}
}
}
impl LlamaParams {
#[must_use]
pub fn new(model_path: impl AsRef<Path>) -> Self {
Self {
model_path: model_path.as_ref().to_path_buf(),
model: LlamaModelParams::default(),
context: LlamaContextParams::default(),
}
}
#[must_use]
pub fn with_model_path(mut self, p: impl AsRef<Path>) -> Self {
self.model_path = p.as_ref().to_path_buf();
self
}
#[must_use]
pub fn with_n_gpu_layers(mut self, n: i32) -> Self {
self.model = self.model.with_n_gpu_layers(n);
self
}
#[must_use]
pub fn with_use_mmap(mut self, yes: bool) -> Self {
self.model = self.model.with_use_mmap(yes);
self
}
#[must_use]
pub fn with_n_ctx(mut self, n: u32) -> Self {
self.context = self.context.with_n_ctx(n);
self
}
#[must_use]
pub fn with_n_batch(mut self, n: u32) -> Self {
self.context = self.context.with_n_batch(n);
self
}
#[must_use]
pub fn with_n_ubatch(mut self, n: u32) -> Self {
self.context = self.context.with_n_ubatch(n);
self
}
#[must_use]
pub fn with_embeddings(mut self, yes: bool) -> Self {
self.context = self.context.with_embeddings(yes);
self
}
#[must_use]
pub fn with_n_threads(mut self, n: i32) -> Self {
self.context = self.context.with_n_threads(n);
self
}
#[must_use]
pub fn with_n_threads_batch(mut self, n: i32) -> Self {
self.context = self.context.with_n_threads_batch(n);
self
}
#[must_use]
pub fn with_offload_kqv(mut self, yes: bool) -> Self {
self.context = self.context.with_offload_kqv(yes);
self
}
#[must_use]
pub fn with_flash_attn(mut self, yes: bool) -> Self {
self.context = self.context.with_flash_attn(yes);
self
}
#[must_use]
pub fn with_pooling_type(mut self, p: crate::context::params::PoolingType) -> Self {
self.context = self.context.with_pooling_type(p);
self
}
#[must_use]
pub fn with_mobile_preset(self, preset: MobilePreset) -> Self {
match preset {
MobilePreset::LowRam => self
.with_n_ctx(2048)
.with_n_batch(128)
.with_n_ubatch(128)
.with_n_threads(4)
.with_n_threads_batch(4)
.with_n_gpu_layers(0)
.with_flash_attn(false)
.with_use_mmap(true),
MobilePreset::Balanced => self
.with_n_ctx(4096)
.with_n_batch(512)
.with_n_ubatch(256)
.with_n_threads(4)
.with_n_threads_batch(4)
.with_n_gpu_layers(32)
.with_flash_attn(true)
.with_use_mmap(true),
MobilePreset::GpuMax => self
.with_n_ctx(4096)
.with_n_batch(1024)
.with_n_ubatch(512)
.with_n_gpu_layers(99)
.with_flash_attn(true)
.with_offload_kqv(true)
.with_use_mmap(true),
}
}
}
impl Default for LlamaParams {
fn default() -> Self {
Self {
model_path: PathBuf::new(),
model: LlamaModelParams::default(),
context: LlamaContextParams::default(),
}
}
}
#[doc(inline)]
pub use StopReason as _StopReasonShim;
#[cfg(test)]
mod tests {
use super::{LlamaParams, MobilePreset};
#[test]
fn mobile_preset_can_be_overridden() {
let params = LlamaParams::new("model.gguf")
.with_mobile_preset(MobilePreset::Balanced)
.with_n_ctx(1024)
.with_n_gpu_layers(0);
assert_eq!(params.context.build().n_ctx, 1024);
assert_eq!(params.model.n_gpu_layers(), 0);
}
#[test]
fn mobile_preset_parse_accepts_cli_names() {
assert_eq!("low-ram".parse(), Ok(MobilePreset::LowRam));
assert_eq!("balanced".parse(), Ok(MobilePreset::Balanced));
assert_eq!("gpu-max".parse(), Ok(MobilePreset::GpuMax));
assert!("fast".parse::<MobilePreset>().is_err());
}
}