Files
DS4Server/src/engine.rs
2026-07-26 10:56:19 +02:00

1361 lines
44 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
mod gguf;
#[cfg(target_os = "macos")]
mod kvstore;
#[cfg(target_os = "macos")]
mod metal;
mod tokenizer;
mod validation;
#[cfg(target_os = "macos")]
use crate::metrics::{KvLookup, Metrics};
use crate::model::ModelChoice;
use crate::settings::TurnSettings;
use crate::settings::{EngineSettings, ReasoningMode};
use gguf::{F16, F32, Gguf, I32, IQ2_XXS, Q2_K, Q4_0, Q4_K, Q5_K, Q6_K, Q8_0, Tensor, Value};
#[cfg(target_os = "macos")]
use kvstore::{KvStore, StoreReason};
use sha2::{Digest, Sha256};
use std::path::{Path, PathBuf};
#[cfg(target_os = "macos")]
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(target_os = "macos")]
use std::time::Instant;
use tokenizer::Tokenizer;
use validation::{validate_dspark, validate_main};
pub(crate) use validation::validate_model_artifact;
#[cfg(target_os = "macos")]
pub(crate) use kvstore::sweep_unreachable as sweep_transient_cache;
#[cfg(target_os = "macos")]
pub(crate) use metal::configure_sources as configure_metal_sources;
const DENSE: &[u32] = &[Q8_0, Q4_K, Q4_0];
const ROUTED: &[u32] = &[Q8_0, IQ2_XXS, Q2_K, Q4_K, Q5_K, Q6_K];
const PLAIN: &[u32] = &[F16, F32];
const DSPARK_DENSE: &[u32] = &[F16, F32, Q8_0];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ModelFamily {
DeepSeek,
Glm,
}
#[derive(Clone, Copy)]
struct Shape {
model: ModelChoice,
family: ModelFamily,
layers: u32,
embd: u64,
vocab: u64,
heads: u64,
head_kv: u64,
head_dim: u64,
value_dim: u64,
rot: u64,
out_groups: u64,
lora_q: u64,
lora_o: u64,
experts: u64,
experts_used: u64,
expert_shared: u64,
ff_expert: u64,
ff_dense: u64,
hash_layers: u32,
sliding_window: u64,
indexer_heads: u64,
indexer_head_dim: u64,
indexer_top_k: u64,
hc: u64,
hc_sinkhorn: u64,
nextn: u32,
leading_dense: u32,
kv_lora: u64,
key_mla: u64,
value_mla: u64,
rms_epsilon: f32,
hc_epsilon: f32,
expert_weight_scale: f32,
swiglu_clamp: f32,
rope_base: f32,
rope_scale: f32,
rope_beta_fast: f32,
rope_beta_slow: f32,
compress_rope_base: f32,
original_context: u64,
}
const FLASH: Shape = Shape {
model: ModelChoice::DeepSeekV4Flash,
family: ModelFamily::DeepSeek,
layers: 43,
embd: 4096,
vocab: 129_280,
heads: 64,
head_kv: 1,
head_dim: 512,
value_dim: 512,
rot: 64,
out_groups: 8,
lora_q: 1024,
lora_o: 1024,
experts: 256,
experts_used: 6,
expert_shared: 1,
ff_expert: 2048,
ff_dense: 0,
hash_layers: 3,
sliding_window: 128,
indexer_heads: 64,
indexer_head_dim: 128,
indexer_top_k: 512,
hc: 4,
hc_sinkhorn: 20,
nextn: 0,
leading_dense: 0,
kv_lora: 0,
key_mla: 0,
value_mla: 0,
rms_epsilon: 1.0e-6,
hc_epsilon: 1.0e-6,
expert_weight_scale: 1.5,
swiglu_clamp: 10.0,
rope_base: 10_000.0,
rope_scale: 16.0,
rope_beta_fast: 32.0,
rope_beta_slow: 1.0,
compress_rope_base: 160_000.0,
original_context: 65_536,
};
const PRO: Shape = Shape {
model: ModelChoice::DeepSeekV4Pro,
layers: 61,
embd: 7168,
heads: 128,
out_groups: 16,
lora_q: 1536,
experts: 384,
ff_expert: 3072,
indexer_top_k: 1024,
expert_weight_scale: 2.5,
..FLASH
};
const GLM: Shape = Shape {
model: ModelChoice::Glm52,
family: ModelFamily::Glm,
layers: 79,
embd: 6144,
vocab: 154_880,
heads: 64,
head_kv: 1,
head_dim: 576,
value_dim: 512,
rot: 64,
out_groups: 0,
lora_q: 2048,
lora_o: 0,
experts: 256,
experts_used: 8,
expert_shared: 1,
ff_expert: 2048,
ff_dense: 12_288,
hash_layers: 0,
sliding_window: 0,
indexer_heads: 32,
indexer_head_dim: 128,
indexer_top_k: 2048,
hc: 0,
hc_sinkhorn: 0,
nextn: 1,
leading_dense: 3,
kv_lora: 512,
key_mla: 256,
value_mla: 256,
rms_epsilon: 1.0e-5,
hc_epsilon: 0.0,
expert_weight_scale: 2.5,
swiglu_clamp: 0.0,
rope_base: 8_000_000.0,
rope_scale: 1.0,
rope_beta_fast: 0.0,
rope_beta_slow: 0.0,
compress_rope_base: 0.0,
original_context: 1_048_576,
};
pub(crate) struct Model {
main: Gguf,
support: Option<Gguf>,
shape: Shape,
tokenizer: Tokenizer,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct ModelSummary {
pub(crate) model: ModelChoice,
pub(crate) mapped_bytes: u64,
pub(crate) tensor_count: usize,
pub(crate) vocabulary_size: usize,
pub(crate) support_loaded: bool,
}
impl Model {
#[allow(dead_code)]
pub(crate) fn open(settings: &EngineSettings) -> Result<Self, String> {
let mut model = Self::open_main(&settings.artifacts.model, settings.model)?;
if let Some(path) = &settings.artifacts.mtp {
let support = Gguf::open(path)?;
validate_dspark(&support, &model.shape)?;
model.support = Some(support);
}
Ok(model)
}
fn open_main(path: &Path, expected: ModelChoice) -> Result<Self, String> {
let main = Gguf::open(path)?;
let shape = validate_main(&main, expected)?;
let tokenizer = Tokenizer::load(&main, shape.family)?;
if tokenizer.vocab_size() != shape.vocab as usize {
return Err(format!(
"tokenizer has {} entries, expected {}",
tokenizer.vocab_size(),
shape.vocab
));
}
Ok(Self {
main,
support: None,
shape,
tokenizer,
})
}
pub(crate) fn summary(&self) -> ModelSummary {
ModelSummary {
model: self.shape.model,
mapped_bytes: self.main.len() + self.support.as_ref().map_or(0, Gguf::len),
tensor_count: self.main.tensors.len()
+ self
.support
.as_ref()
.map_or(0, |support| support.tensors.len()),
vocabulary_size: self.tokenizer.vocab_size(),
support_loaded: self.support.is_some(),
}
}
fn checkpoint_identity(&self) -> [u8; 32] {
let mut hash = Sha256::new();
hash.update(b"DS4Server model checkpoint identity v1");
hash.update(self.main.checkpoint_identity());
if let Some(support) = &self.support {
hash.update(support.checkpoint_identity());
}
hash.finalize().into()
}
pub(crate) fn tokenize(&self, text: &str) -> Vec<i32> {
self.tokenizer.tokenize(text)
}
pub(crate) fn render_prompt(
&self,
system: &str,
prompt: &str,
reasoning: ReasoningMode,
) -> Vec<i32> {
self.tokenizer.encode_chat(system, prompt, reasoning)
}
fn render_conversation(
&self,
system: &str,
messages: &[ChatTurn],
reasoning: ReasoningMode,
) -> Vec<i32> {
self.tokenizer
.encode_conversation(system, messages, reasoning)
}
fn render_history(
&self,
system: &str,
messages: &[ChatTurn],
reasoning: ReasoningMode,
) -> Vec<i32> {
self.tokenizer.encode_history(system, messages, reasoning)
}
fn render_continuation(
&self,
prompt: &str,
reasoning: ReasoningMode,
skip_previous_eos: bool,
) -> Vec<i32> {
self.tokenizer
.encode_continuation(prompt, reasoning, skip_previous_eos)
}
pub(crate) fn token_bytes(&self, token: i32) -> Option<Vec<u8>> {
self.tokenizer.token_bytes(token)
}
pub(crate) fn eos_token(&self) -> i32 {
self.tokenizer.eos()
}
pub(crate) fn is_stop_token(&self, token: i32) -> bool {
self.tokenizer.is_stop(token)
}
pub(crate) fn is_think_start_token(&self, token: i32) -> bool {
self.tokenizer.is_think_start(token)
}
pub(crate) fn is_think_end_token(&self, token: i32) -> bool {
self.tokenizer.is_think_end(token)
}
pub(crate) fn is_stop_token_for_reasoning(&self, token: i32, reasoning: ReasoningMode) -> bool {
self.is_stop_token(token)
|| (reasoning == ReasoningMode::Direct
&& (self.is_think_start_token(token) || self.is_think_end_token(token)))
}
pub(crate) fn tensor_data(&self, name: &str) -> Result<&[u8], String> {
self.main.tensor_data(name)
}
}
#[cfg(target_os = "macos")]
pub(crate) struct Generator {
executor: metal::Executor,
checkpoint: Option<PathBuf>,
/// Token frontier of the last transient store, so continued checkpoints are
/// spaced like ds4's `continued_last_store_tokens`.
last_store_tokens: u32,
metrics: Arc<Metrics>,
}
#[derive(Clone)]
pub(crate) struct ChatTurn {
pub(crate) user: bool,
pub(crate) tool: bool,
pub(crate) skip_previous_eos: bool,
pub(crate) reasoning: Option<String>,
pub(crate) reasoning_complete: bool,
pub(crate) content: String,
}
pub(crate) struct GenerationOutput {
pub(crate) message: ChatTurn,
pub(crate) prompt_tokens: u32,
pub(crate) cached_tokens: u32,
pub(crate) completion_tokens: u32,
pub(crate) finish_reason: &'static str,
pub(crate) previous_checkpoint_bytes: Option<u64>,
pub(crate) checkpoint_bytes: u64,
}
#[cfg(target_os = "macos")]
pub(crate) struct CompactionOutput {
pub(crate) summary: String,
pub(crate) tail: Vec<ChatTurn>,
pub(crate) context_tokens: u32,
pub(crate) checkpoint: PathBuf,
}
#[cfg(target_os = "macos")]
impl Generator {
pub(crate) fn open(settings: &EngineSettings, metrics: Arc<Metrics>) -> Result<Self, String> {
if settings.model != ModelChoice::DeepSeekV4Flash {
return Err("local generation currently supports DeepSeek V4 Flash only".into());
}
if settings.speculative.dspark || settings.ssd.enabled || settings.steering.file.is_some() {
return Err(
"DSpark, SSD streaming, and steering are not yet available in the Rust executor"
.into(),
);
}
let model = Model::open(settings)?;
let executor = metal::Executor::open(
model,
settings.context_tokens.max(1) as u32,
settings.execution.quality,
settings.execution.prefill_chunk,
)?;
Ok(Self {
executor,
checkpoint: None,
last_store_tokens: 0,
metrics,
})
}
pub(crate) fn summary(&self) -> ModelSummary {
self.executor.model().summary()
}
pub(crate) fn generate(
&mut self,
checkpoint: &Path,
messages: &[ChatTurn],
settings: &TurnSettings,
cancelled: &AtomicBool,
mut emit: impl FnMut(bool, String),
mut progress: impl FnMut(u32, u32, Option<f32>),
) -> Result<GenerationOutput, String> {
let history = messages
.split_last()
.map_or(messages, |(_, history)| history);
self.select_checkpoint(
checkpoint,
conversation_tag(&settings.system_prompt, settings.reasoning_mode, history),
)?;
let (mut output, prompt_complete) =
self.generate_inner(messages, settings, cancelled, &mut emit, &mut progress)?;
let mut completed = messages.to_vec();
completed.push(output.message.clone());
output.previous_checkpoint_bytes =
std::fs::metadata(checkpoint).ok().map(|item| item.len());
self.save_checkpoint(
checkpoint,
if prompt_complete {
conversation_tag(&settings.system_prompt, settings.reasoning_mode, &completed)
} else {
[0; 32]
},
)?;
output.checkpoint_bytes = std::fs::metadata(checkpoint)
.map(|item| item.len())
.unwrap_or(0);
Ok(output)
}
pub(crate) fn generate_transient(
&mut self,
directory: &Path,
messages: &[ChatTurn],
settings: &TurnSettings,
cancelled: &AtomicBool,
mut emit: impl FnMut(bool, String),
mut progress: impl FnMut(u32, u32, Option<f32>),
) -> Result<GenerationOutput, String> {
let history = messages
.split_last()
.map_or(messages, |(_, history)| history);
let store = KvStore::open(directory, settings.kv_cache.budget_bytes)?;
let history_key =
conversation_key(&settings.system_prompt, settings.reasoning_mode, history);
let history_tag: [u8; 32] = Sha256::digest(&history_key).into();
let mut previous_checkpoint = self.checkpoint.clone();
if self.executor.checkpoint_tag() != history_tag {
if let Some(entry) = store.find(&history_key, self.executor.context()) {
if self.select_checkpoint(&entry.checkpoint, entry.tag)? {
store.touch(&entry)?;
self.last_store_tokens = entry.tokens;
previous_checkpoint = Some(entry.checkpoint);
} else {
store.discard(&entry);
self.last_store_tokens = 0;
previous_checkpoint = None;
}
} else {
self.executor.reset()?;
self.checkpoint = None;
self.last_store_tokens = 0;
previous_checkpoint = None;
self.metrics.kv_lookup(KvLookup::Miss);
}
} else {
self.metrics.kv_lookup(KvLookup::MemoryHit);
}
let (mut output, prompt_complete) =
self.generate_inner(messages, settings, cancelled, &mut emit, &mut progress)?;
let mut completed = messages.to_vec();
completed.push(output.message.clone());
output.previous_checkpoint_bytes = previous_checkpoint
.as_deref()
.and_then(|path| std::fs::metadata(path).ok())
.map(|item| item.len());
if prompt_complete {
let completed_key =
conversation_key(&settings.system_prompt, settings.reasoning_mode, &completed);
let completed_tag: [u8; 32] = Sha256::digest(&completed_key).into();
if !settings.kv_cache.stores(
self.executor.position(),
history.is_empty(),
self.last_store_tokens,
) {
// A gated store still leaves the finished conversation in the
// live KV, so mark it and drop the stale file association.
self.executor.note_checkpoint_tag(completed_tag);
self.checkpoint = None;
return Ok(output);
}
let completed_checkpoint = store.checkpoint_path(&completed_key);
self.save_checkpoint(&completed_checkpoint, completed_tag)?;
let retained = store.record(
&completed_checkpoint,
&completed_key,
completed_tag,
self.executor.position(),
self.executor.context(),
if history.is_empty() {
StoreReason::Cold
} else {
StoreReason::Continued
},
)?;
output.checkpoint_bytes = retained
.then(|| std::fs::metadata(&completed_checkpoint).ok())
.flatten()
.map_or(0, |item| item.len());
if retained {
self.last_store_tokens = self.executor.position();
}
self.checkpoint = retained.then_some(completed_checkpoint);
}
Ok(output)
}
pub(crate) fn compact(
&mut self,
messages: &[ChatTurn],
settings: &TurnSettings,
reason: &str,
checkpoint: &Path,
cancelled: &AtomicBool,
mut progress: impl FnMut(u32, u32, Option<f32>),
) -> Result<CompactionOutput, String> {
let _ = std::fs::remove_file(checkpoint);
self.executor.reset()?;
self.checkpoint = None;
let result = (|| {
let mut private_messages = messages.to_vec();
private_messages.push(ChatTurn {
user: true,
tool: false,
skip_previous_eos: false,
reasoning: None,
reasoning_complete: true,
content: crate::compaction::summary_prompt(reason),
});
let mut private_settings = settings.clone();
private_settings.reasoning_mode = ReasoningMode::Direct;
private_settings.max_generated_tokens = crate::compaction::SUMMARY_MAX_TOKENS;
private_settings.temperature = 0.0;
private_settings.stops.extend([
"<DSML".into(),
"<DSML".into(),
"<tool_call>".into(),
"<think>".into(),
"</think>".into(),
]);
let (output, prompt_complete) = self.generate_inner(
&private_messages,
&private_settings,
cancelled,
&mut |_, _| {},
&mut progress,
)?;
if cancelled.load(Ordering::Relaxed) || !prompt_complete {
return Err("context compaction interrupted".into());
}
let summary = crate::compaction::sanitize_summary(&output.message.content);
if summary.is_empty() {
return Err("context compaction produced an empty summary".into());
}
// The private request must never become the rebuilt session prefix.
self.executor.reset()?;
self.checkpoint = None;
let full = self.executor.model().render_conversation(
&settings.system_prompt,
messages,
settings.reasoning_mode,
);
let mut starts = Vec::with_capacity(messages.len());
for index in 0..messages.len() {
starts.push(
self.executor
.model()
.render_conversation(
&settings.system_prompt,
&messages[..index],
settings.reasoning_mode,
)
.len() as u32,
);
}
let start = crate::compaction::tail_start(
messages,
&starts,
full.len() as u32,
crate::compaction::tail_budget(self.executor.context()),
);
let tail = messages[start..].to_vec();
let rebuilt_system =
crate::compaction::summary_system_prompt(&settings.system_prompt, Some(&summary));
let history_tokens = self.executor.model().render_history(
&rebuilt_system,
&tail,
settings.reasoning_mode,
);
if history_tokens.len() >= self.executor.context() as usize {
return Err("compacted context does not fit the configured context".into());
}
let context = self.executor.context();
let completed = self.executor.prefill(&history_tokens, |used| {
progress(used, context, None);
!cancelled.load(Ordering::Relaxed)
})?;
if completed != history_tokens.len() || cancelled.load(Ordering::Relaxed) {
return Err("context compaction interrupted during rebuild".into());
}
let tag = conversation_tag(&rebuilt_system, settings.reasoning_mode, &tail);
self.save_checkpoint(checkpoint, tag)?;
Ok(CompactionOutput {
summary,
tail,
context_tokens: history_tokens.len() as u32,
checkpoint: checkpoint.to_owned(),
})
})();
if result.is_err() {
let _ = std::fs::remove_file(checkpoint);
self.executor.reset()?;
self.checkpoint = None;
} else {
self.checkpoint = Some(checkpoint.to_owned());
}
result
}
fn select_checkpoint(
&mut self,
checkpoint: &Path,
expected_tag: [u8; 32],
) -> Result<bool, String> {
if self.checkpoint.as_deref() == Some(checkpoint) {
if !checkpoint.is_file() {
self.executor.reset()?;
self.checkpoint = None;
self.metrics.kv_lookup(KvLookup::Miss);
return Ok(false);
}
let found = self.executor.checkpoint_tag() == expected_tag;
self.metrics.kv_lookup(if found {
KvLookup::MemoryHit
} else {
KvLookup::Miss
});
return Ok(found);
}
self.executor.reset()?;
self.metrics.kv_read_started();
let started = Instant::now();
let loaded = self
.executor
.load_checkpoint(checkpoint, &mut |bytes| self.metrics.kv_read_bytes(bytes));
self.metrics
.kv_read_finished(started.elapsed(), loaded.is_err());
let found = matches!(loaded, Ok(true)) && self.executor.checkpoint_tag() == expected_tag;
let lookup = match loaded {
Ok(true) if found => KvLookup::DiskHit,
Ok(true) | Ok(false) => KvLookup::Miss,
Err(_) => {
self.executor.reset()?;
let _ = std::fs::remove_file(checkpoint);
KvLookup::Invalid
}
};
self.metrics.kv_lookup(lookup);
self.checkpoint = Some(checkpoint.to_owned());
Ok(found)
}
fn save_checkpoint(&mut self, checkpoint: &Path, tag: [u8; 32]) -> Result<(), String> {
self.metrics.kv_write_started();
let started = Instant::now();
let result = self
.executor
.save_checkpoint(checkpoint, tag, &mut |bytes| {
self.metrics.kv_write_bytes(bytes);
});
self.metrics
.kv_write_finished(started.elapsed(), result.is_err());
result
}
fn generate_inner(
&mut self,
messages: &[ChatTurn],
settings: &TurnSettings,
cancelled: &AtomicBool,
emit: &mut impl FnMut(bool, String),
progress: &mut impl FnMut(u32, u32, Option<f32>),
) -> Result<(GenerationOutput, bool), String> {
let tokens = match messages.split_last() {
Some((latest, history))
if latest.user
&& self.executor.checkpoint_tag()
== conversation_tag(
&settings.system_prompt,
settings.reasoning_mode,
history,
) =>
{
let mut tokens = self.executor.tokens().to_vec();
tokens.extend(self.executor.model().render_continuation(
&latest.content,
settings.reasoning_mode,
latest.skip_previous_eos,
));
tokens
}
_ => self.executor.model().render_conversation(
&settings.system_prompt,
messages,
settings.reasoning_mode,
),
};
if tokens.is_empty() {
return Err("the rendered prompt is empty".into());
}
let max_context = self.executor.context() as usize;
if tokens.len() >= max_context {
return Err(format!(
"Prompt has {} tokens, but the configured context size is {max_context} tokens",
tokens.len()
));
}
let reused = self.executor.align_prompt(&tokens)?;
self.metrics.kv_prefix_reused(reused);
progress(self.executor.position(), self.executor.context(), None);
let mut rng = Rng::new(settings.seed.unwrap_or(0x4453_3453_4552_5645));
let mut reasoning = settings.reasoning_mode != ReasoningMode::Direct;
let mut generated = ChatTurn {
user: false,
tool: false,
skip_previous_eos: false,
reasoning: reasoning.then(String::new),
reasoning_complete: !reasoning,
content: String::new(),
};
let mut emitted_reasoning = 0;
let mut emitted_content = 0;
let mut pending_utf8 = Vec::new();
let prompt_tokens = tokens.len();
let suffix = &tokens[reused..];
let completed = if (reused == 0 && tokens.len() > 1) || suffix.len() >= 4 {
let context = self.executor.context();
self.executor.prefill(suffix, |used| {
progress(used, context, None);
!cancelled.load(Ordering::Relaxed)
})?
} else {
let mut completed = 0;
for &token in suffix {
if cancelled.load(Ordering::Relaxed) {
break;
}
self.executor.eval(token)?;
completed += 1;
progress(self.executor.position(), self.executor.context(), None);
}
completed
};
if completed != suffix.len() {
return Ok((
GenerationOutput {
message: generated,
prompt_tokens: prompt_tokens as u32,
cached_tokens: reused as u32,
completion_tokens: 0,
finish_reason: "stop",
previous_checkpoint_bytes: None,
checkpoint_bytes: 0,
},
false,
));
}
progress(self.executor.position(), self.executor.context(), Some(0.0));
let generation_started = Instant::now();
let mut generated_tokens = 0_u32;
for _ in 0..settings
.max_generated_tokens
.max(0)
.min((max_context - self.executor.position() as usize) as i32)
{
if cancelled.load(Ordering::Relaxed) {
append_generated_bytes(&mut generated, reasoning, &mut pending_utf8, &[], true);
flush_generated(
&mut generated,
&mut emitted_reasoning,
&mut emitted_content,
&settings.stops,
emit,
);
return Ok((
GenerationOutput {
message: generated,
prompt_tokens: prompt_tokens as u32,
cached_tokens: reused as u32,
completion_tokens: generated_tokens,
finish_reason: "stop",
previous_checkpoint_bytes: None,
checkpoint_bytes: 0,
},
true,
));
}
let token = sample(
self.executor.logits(),
settings.temperature,
settings.top_p,
settings.min_p,
settings.top_k,
&mut rng,
);
if self
.executor
.model()
.is_stop_token_for_reasoning(token, settings.reasoning_mode)
{
append_generated_bytes(&mut generated, reasoning, &mut pending_utf8, &[], true);
flush_generated(
&mut generated,
&mut emitted_reasoning,
&mut emitted_content,
&settings.stops,
emit,
);
return Ok((
GenerationOutput {
message: generated,
prompt_tokens: prompt_tokens as u32,
cached_tokens: reused as u32,
completion_tokens: generated_tokens,
finish_reason: "stop",
previous_checkpoint_bytes: None,
checkpoint_bytes: 0,
},
true,
));
}
if self.executor.model().is_think_start_token(token) {
append_generated_bytes(&mut generated, reasoning, &mut pending_utf8, &[], true);
reasoning = true;
generated.reasoning.get_or_insert_default();
} else if self.executor.model().is_think_end_token(token) {
append_generated_bytes(&mut generated, reasoning, &mut pending_utf8, &[], true);
if let Some(reasoning_text) = &mut generated.reasoning
&& emit_safe_text(
reasoning_text,
&mut emitted_reasoning,
&settings.stops,
true,
true,
emit,
)
{
return Ok((
GenerationOutput {
message: generated,
prompt_tokens: prompt_tokens as u32,
cached_tokens: reused as u32,
completion_tokens: generated_tokens + 1,
finish_reason: "stop",
previous_checkpoint_bytes: None,
checkpoint_bytes: 0,
},
false,
));
}
reasoning = false;
generated.reasoning_complete = true;
emit(false, String::new());
} else if let Some(bytes) = self.executor.model().token_bytes(token) {
append_generated_bytes(&mut generated, reasoning, &mut pending_utf8, &bytes, false);
let stopped = if reasoning {
let text = generated.reasoning.get_or_insert_default();
emit_safe_text(
text,
&mut emitted_reasoning,
&settings.stops,
false,
true,
emit,
)
} else {
generated.reasoning_complete = true;
emit_safe_text(
&mut generated.content,
&mut emitted_content,
&settings.stops,
false,
false,
emit,
)
};
if stopped {
return Ok((
GenerationOutput {
message: generated,
prompt_tokens: prompt_tokens as u32,
cached_tokens: reused as u32,
completion_tokens: generated_tokens + 1,
finish_reason: "stop",
previous_checkpoint_bytes: None,
checkpoint_bytes: 0,
},
false,
));
}
}
self.executor.eval(token)?;
generated_tokens += 1;
progress(
self.executor.position(),
self.executor.context(),
Some(
generated_tokens as f32
/ generation_started.elapsed().as_secs_f32().max(1.0e-6),
),
);
}
append_generated_bytes(&mut generated, reasoning, &mut pending_utf8, &[], true);
flush_generated(
&mut generated,
&mut emitted_reasoning,
&mut emitted_content,
&settings.stops,
emit,
);
Ok((
GenerationOutput {
message: generated,
prompt_tokens: prompt_tokens as u32,
cached_tokens: reused as u32,
completion_tokens: generated_tokens,
finish_reason: "length",
previous_checkpoint_bytes: None,
checkpoint_bytes: 0,
},
true,
))
}
}
#[cfg(target_os = "macos")]
fn append_generated_bytes(
generated: &mut ChatTurn,
reasoning: bool,
pending: &mut Vec<u8>,
bytes: &[u8],
final_flush: bool,
) {
let text = if reasoning {
generated.reasoning.get_or_insert_default()
} else {
&mut generated.content
};
pending.extend_from_slice(bytes);
loop {
match std::str::from_utf8(pending) {
Ok(valid) => {
text.push_str(valid);
pending.clear();
return;
}
Err(error) => {
let valid = error.valid_up_to();
text.push_str(std::str::from_utf8(&pending[..valid]).unwrap());
pending.drain(..valid);
match error.error_len() {
Some(length) => {
text.push('\u{fffd}');
pending.drain(..length);
}
None if final_flush => {
text.push_str(&String::from_utf8_lossy(pending));
pending.clear();
return;
}
None => return,
}
}
}
}
}
#[cfg(target_os = "macos")]
fn flush_generated(
generated: &mut ChatTurn,
emitted_reasoning: &mut usize,
emitted_content: &mut usize,
stops: &[String],
emit: &mut impl FnMut(bool, String),
) {
if let Some(reasoning) = &mut generated.reasoning {
let _ = emit_safe_text(reasoning, emitted_reasoning, stops, true, true, emit);
}
let _ = emit_safe_text(
&mut generated.content,
emitted_content,
stops,
true,
false,
emit,
);
}
#[cfg(target_os = "macos")]
fn emit_safe_text(
text: &mut String,
emitted: &mut usize,
stops: &[String],
final_flush: bool,
reasoning: bool,
emit: &mut impl FnMut(bool, String),
) -> bool {
let stop = stops
.iter()
.filter_map(|stop| {
text[*emitted..]
.find(stop)
.map(|position| *emitted + position)
})
.min();
if let Some(stop) = stop {
if stop > *emitted {
emit(reasoning, text[*emitted..stop].to_owned());
}
text.truncate(stop);
*emitted = stop;
return true;
}
let hold = if final_flush {
0
} else {
stops
.iter()
.map(|stop| stop.len().saturating_sub(1))
.max()
.unwrap_or(0)
};
let mut safe = text.len().saturating_sub(hold);
while safe > *emitted && !text.is_char_boundary(safe) {
safe -= 1;
}
if safe > *emitted {
emit(reasoning, text[*emitted..safe].to_owned());
*emitted = safe;
}
false
}
#[cfg(target_os = "macos")]
fn conversation_key(system: &str, reasoning: ReasoningMode, messages: &[ChatTurn]) -> Vec<u8> {
fn text(output: &mut Vec<u8>, value: &str) {
output.extend_from_slice(&(value.len() as u64).to_le_bytes());
output.extend_from_slice(value.as_bytes());
}
let mut output = b"DS4Server chat checkpoint v3".to_vec();
text(&mut output, system);
output.push(match reasoning {
ReasoningMode::Direct => 0,
ReasoningMode::High => 1,
ReasoningMode::Max => 2,
});
for message in messages {
output.push(u8::from(message.user));
output.push(u8::from(message.tool));
output.push(u8::from(message.skip_previous_eos));
match &message.reasoning {
Some(reasoning) => {
output.push(1);
text(&mut output, reasoning);
}
None => output.push(0),
}
output.push(u8::from(message.reasoning_complete));
text(&mut output, &message.content);
}
output
}
#[cfg(target_os = "macos")]
fn conversation_tag(system: &str, reasoning: ReasoningMode, messages: &[ChatTurn]) -> [u8; 32] {
Sha256::digest(conversation_key(system, reasoning, messages)).into()
}
#[cfg(target_os = "macos")]
fn sample(
logits: &[f32],
temperature: f32,
top_p: f32,
min_p: f32,
top_k: i32,
rng: &mut Rng,
) -> i32 {
if temperature <= 0.0 {
return logits
.iter()
.enumerate()
.max_by(|a, b| a.1.total_cmp(b.1))
.map_or(0, |(index, _)| index as i32);
}
let maximum = logits
.iter()
.copied()
.filter(|value| value.is_finite())
.fold(f32::NEG_INFINITY, f32::max);
if !maximum.is_finite() {
return 0;
}
let top_p = if top_p <= 0.0 || top_p > 1.0 {
1.0
} else {
top_p
};
let min_p = min_p.max(0.0);
let mut probabilities: Vec<(usize, f32)> = logits
.iter()
.enumerate()
.filter(|(_, logit)| logit.is_finite())
.map(|(index, logit)| (index, ((*logit - maximum) / temperature).exp()))
.filter(|(_, probability)| *probability >= min_p)
.collect();
if probabilities.is_empty() {
return logits
.iter()
.enumerate()
.max_by(|a, b| a.1.total_cmp(b.1))
.map_or(0, |(index, _)| index as i32);
}
if top_p < 1.0 || top_k > 0 {
probabilities.sort_unstable_by(|a, b| b.1.total_cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
if top_k > 0 {
probabilities.truncate(probabilities.len().min(top_k as usize));
}
}
if top_p < 1.0 {
let total: f32 = probabilities
.iter()
.map(|(_, probability)| probability)
.sum();
let mut kept = 0.0;
let count = probabilities
.iter()
.position(|(_, probability)| {
kept += *probability;
kept / total >= top_p
})
.map_or(probabilities.len(), |index| index + 1);
probabilities.truncate(count);
}
let kept_total: f32 = probabilities.iter().map(|(_, p)| p).sum();
let mut choice = rng.unit() * kept_total;
for (token, probability) in &probabilities {
choice -= probability;
if choice <= 0.0 {
return *token as i32;
}
}
probabilities.last().map_or(0, |(token, _)| *token as i32)
}
#[cfg(target_os = "macos")]
struct Rng(u64);
#[cfg(target_os = "macos")]
impl Rng {
fn new(seed: u64) -> Self {
Self(seed.max(1))
}
fn unit(&mut self) -> f32 {
let mut value = self.0;
if value == 0 {
value = 0x9e37_79b9_7f4a_7c15;
}
value ^= value >> 12;
value ^= value << 25;
value ^= value >> 27;
self.0 = value;
let value = value.wrapping_mul(0x2545_f491_4f6c_dd1d);
((value >> 40) & 0xff_ffff) as f32 / 16_777_216.0
}
}
#[cfg(all(test, target_os = "macos"))]
mod sampling_tests {
use super::*;
#[test]
fn zero_temperature_is_greedy() {
let mut rng = Rng::new(1);
assert_eq!(sample(&[1.0, 4.0, 2.0], 0.0, 1.0, 0.0, 0, &mut rng), 1);
}
#[test]
fn top_k_and_stream_stops_are_applied_before_output() {
let mut rng = Rng::new(1);
assert_eq!(sample(&[1.0, 4.0, 2.0], 1.0, 1.0, 0.0, 1, &mut rng), 1);
let mut text = "hello STOP hidden".to_owned();
let mut emitted = 0;
let mut chunks = Vec::new();
assert!(emit_safe_text(
&mut text,
&mut emitted,
&["STOP".into()],
false,
false,
&mut |_, chunk| chunks.push(chunk),
));
assert_eq!(text, "hello ");
assert_eq!(chunks, ["hello "]);
}
#[test]
fn split_utf8_token_bytes_are_joined_before_decoding() {
let mut generated = ChatTurn {
user: false,
tool: false,
skip_previous_eos: false,
reasoning: None,
reasoning_complete: true,
content: String::new(),
};
let mut pending = Vec::new();
append_generated_bytes(&mut generated, false, &mut pending, &[0xf0, 0x9f], false);
assert!(generated.content.is_empty());
append_generated_bytes(&mut generated, false, &mut pending, &[0x98, 0x8a], false);
assert_eq!(generated.content, "😊");
assert!(pending.is_empty());
}
#[test]
fn checkpoint_tag_covers_the_canonical_chat_state() {
let mut messages = vec![ChatTurn {
user: true,
tool: false,
skip_previous_eos: false,
reasoning: None,
reasoning_complete: true,
content: "Hello".into(),
}];
let tag = conversation_tag("System", ReasoningMode::High, &messages);
assert_eq!(
tag,
conversation_tag("System", ReasoningMode::High, &messages)
);
assert_ne!(
tag,
conversation_tag("Changed", ReasoningMode::High, &messages)
);
assert_ne!(
tag,
conversation_tag("System", ReasoningMode::Direct, &messages)
);
messages[0].skip_previous_eos = true;
assert_ne!(
tag,
conversation_tag("System", ReasoningMode::High, &messages)
);
let prefix = conversation_key("System", ReasoningMode::High, &messages);
messages.push(ChatTurn {
user: false,
tool: false,
skip_previous_eos: false,
reasoning: Some("because".into()),
reasoning_complete: true,
content: "Hi".into(),
});
assert!(conversation_key("System", ReasoningMode::High, &messages).starts_with(&prefix));
}
#[test]
#[ignore = "requires the 80 GiB Flash checkpoint and Apple Metal"]
fn metal_executes_real_flash_token() {
configure_metal_sources().unwrap();
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(
"../ds4/gguf/DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix.gguf",
);
let model = Model::open_main(&path, ModelChoice::DeepSeekV4Flash).unwrap();
let tokens = model.render_prompt(
"You are a helpful assistant",
"Hello",
ReasoningMode::Direct,
);
assert_eq!(tokens.len(), 10);
let mut executor = metal::Executor::open(model, 32_768, false, 0).unwrap();
assert_eq!(executor.context(), 32_768);
for &token in &tokens {
executor.eval(token).unwrap();
}
assert!(executor.logits().iter().all(|logit| logit.is_finite()));
let argmax = executor
.logits()
.iter()
.enumerate()
.max_by(|a, b| a.1.total_cmp(b.1))
.unwrap();
eprintln!(
"Rust logits: argmax={} value={} logit0={}",
argmax.0,
argmax.1,
executor.logits()[0]
);
assert_eq!(argmax.0, 19_923);
assert!((executor.logits()[0] - -7.675_424).abs() < 0.1);
let next = argmax.0 as i32;
let checkpoint =
std::env::temp_dir().join(format!("ds4-rust-kv-{}.bin", std::process::id()));
executor
.save_checkpoint(&checkpoint, [7; 32], &mut |_| {})
.unwrap();
executor.eval(next).unwrap();
let continued_logit = executor.logits()[0];
let continued_argmax = executor
.logits()
.iter()
.enumerate()
.max_by(|a, b| a.1.total_cmp(b.1))
.unwrap()
.0;
drop(executor);
let model = Model::open_main(&path, ModelChoice::DeepSeekV4Flash).unwrap();
let mut restored = metal::Executor::open(model, 32_768, false, 0).unwrap();
assert!(restored.load_checkpoint(&checkpoint, &mut |_| {}).unwrap());
assert_eq!(restored.position(), tokens.len() as u32);
assert_eq!(restored.tokens(), tokens);
assert_eq!(restored.checkpoint_tag(), [7; 32]);
restored.eval(next).unwrap();
assert_eq!(
restored
.logits()
.iter()
.enumerate()
.max_by(|a, b| a.1.total_cmp(b.1))
.unwrap()
.0,
continued_argmax
);
assert!((restored.logits()[0] - continued_logit).abs() < 1.0e-5);
std::fs::remove_file(checkpoint).unwrap();
}
}