fix: 12 bug fixes from comprehensive review — 51 tok/s verified on RTX 5090

P0 fixes (blocking usability):
- FIX-01: thread-local cuBLAS handle (was creating/destroying per matmul)
- FIX-16: EOS token no longer leaks into API responses
- FIX-17: max_seq_len configurable via --max-seq-len (default 2048, was hardcoded 256)
- FIX-18: max_tokens clamped to available seq space, prompt overflow returns 400

P1 fixes (bugs & performance):
- FIX-07: CachingAllocator wired into all hot paths (to_device, embedding, rope, concat)
- FIX-08: CudaDeviceProp buffer increased to 32KB for CUDA 12.9 safety
- FIX-09: tokenizer byte_fallback graceful degradation (was panic)
- FIX-19: causal mask uses -INFINITY instead of -1e9 (BF16 supports inf)
- FIX-20: LayerNorm rewritten to numerically stable two-pass algorithm
- FIX-21: min block size guard (32 threads) for LayerNorm/RMSNorm launches

P2 fixes (improvements):
- FIX-22: Option<GpuKVCache> + take() eliminates dummy KV cache allocations
- FIX-23: RoPE cache no longer artificially capped at 8192 positions

Verified on dash5 (RTX 5090): 51 tok/s batch=1, 74 tok/s 2-concurrent, 1.7-3.3x HF transformers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 14:13:43 +08:00
parent a67e724119
commit 986a289616
14 changed files with 285 additions and 292 deletions

View File

@@ -1,5 +1,6 @@
use axum::Extension;
use axum::Json;
use axum::http::StatusCode;
use axum::response::sse::{Event, KeepAlive, Sse};
use axum::response::{IntoResponse, Response};
use serde::{Deserialize, Serialize};
@@ -73,13 +74,13 @@ pub async fn chat_completions(
Json(req): Json<ChatRequest>,
) -> Response {
if req.stream == Some(true) {
chat_stream(state, req).into_response()
chat_stream(state, req)
} else {
chat_non_stream(state, req).await.into_response()
chat_non_stream(state, req).await
}
}
async fn chat_non_stream(state: Arc<AppState>, req: ChatRequest) -> Json<serde_json::Value> {
async fn chat_non_stream(state: Arc<AppState>, req: ChatRequest) -> Response {
let id = format!("chatcmpl-{}", Uuid::new_v4());
let model_name = state.model_name.clone();
let created = unix_timestamp();
@@ -88,10 +89,21 @@ async fn chat_non_stream(state: Arc<AppState>, req: ChatRequest) -> Json<serde_j
let prompt_tokens = state.engine_tokenizer.lock().unwrap().encode(&prompt);
let prompt_token_count = prompt_tokens.len();
let max_seq_len = state.max_seq_len;
if prompt_token_count >= max_seq_len {
return (StatusCode::BAD_REQUEST, Json(serde_json::json!({
"error": {
"message": format!("prompt is {} tokens, exceeds max_seq_len {}", prompt_token_count, max_seq_len),
"type": "invalid_request_error"
}
}))).into_response();
}
let max_tokens = req.max_tokens.min(max_seq_len - prompt_token_count);
let (tx, mut rx) = tokio::sync::mpsc::channel::<GenerateEvent>(64);
let gen_req = GenerateRequest {
prompt_tokens,
max_tokens: req.max_tokens,
max_tokens,
sampling: sampling_params(&req),
sender: tx,
};
@@ -133,13 +145,13 @@ async fn chat_non_stream(state: Arc<AppState>, req: ChatRequest) -> Json<serde_j
"completion_tokens": completion_token_count,
"total_tokens": prompt_token_count + completion_token_count
}
}))
})).into_response()
}
fn chat_stream(
state: Arc<AppState>,
req: ChatRequest,
) -> Sse<impl tokio_stream::Stream<Item = Result<Event, Infallible>>> {
) -> Response {
let id = format!("chatcmpl-{}", Uuid::new_v4());
let model_name = state.model_name.clone();
let created = unix_timestamp();
@@ -147,10 +159,21 @@ fn chat_stream(
let prompt = build_prompt(&req.messages);
let prompt_tokens = state.engine_tokenizer.lock().unwrap().encode(&prompt);
let max_seq_len = state.max_seq_len;
if prompt_tokens.len() >= max_seq_len {
return (StatusCode::BAD_REQUEST, Json(serde_json::json!({
"error": {
"message": format!("prompt is {} tokens, exceeds max_seq_len {}", prompt_tokens.len(), max_seq_len),
"type": "invalid_request_error"
}
}))).into_response();
}
let max_tokens = req.max_tokens.min(max_seq_len - prompt_tokens.len());
let (engine_tx, engine_rx) = tokio::sync::mpsc::channel::<GenerateEvent>(64);
let gen_req = GenerateRequest {
prompt_tokens,
max_tokens: req.max_tokens,
max_tokens,
sampling: sampling_params(&req),
sender: engine_tx,
};
@@ -202,7 +225,7 @@ fn chat_stream(
}
});
Sse::new(ReceiverStream::new(sse_rx)).keep_alive(KeepAlive::default())
Sse::new(ReceiverStream::new(sse_rx)).keep_alive(KeepAlive::default()).into_response()
}
fn make_chunk(