#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_gpu_model_tokenizer_missing_error() {
use crate::gpu::{GpuModel, GpuModelConfig};
let config = GpuModelConfig {
vocab_size: 10, hidden_dim: 64,
num_heads: 4,
num_kv_heads: 4,
num_layers: 2,
intermediate_dim: 128,
eps: 1e-5,
rope_theta: 10000.0,
explicit_head_dim: None,
layer_types: None,
linear_key_head_dim: None,
linear_value_head_dim: None,
linear_num_key_heads: None,
linear_num_value_heads: None,
linear_conv_kernel_dim: None,
constraints: None,
num_experts: None,
num_experts_per_tok: None,
expert_intermediate_size: None,
};
let gpu_model = GpuModel::new(config).expect("create GPU model");
let state = AppState::with_gpu_model(gpu_model).expect("create AppState");
let app = create_router(state);
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Hello world!"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
let status = response.status();
assert!(
status == StatusCode::OK
|| status == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::NOT_FOUND
|| status == StatusCode::BAD_REQUEST,
"Should handle gracefully, got {}",
status
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_quantized_model_empty_messages_error() {
use crate::gguf::GGUFConfig;
let config = GGUFConfig {
architecture: "llama".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("llama"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 2,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 256,
context_length: 128,
rope_theta: 10000.0,
eps: 1e-5,
rope_type: 0,
explicit_head_dim: None,
query_pre_attn_scalar: None,
bos_token_id: None,
eos_token_id: None,
};
let model = create_test_quantized_model(&config);
let state = AppState::with_quantized_model(model).expect("create AppState");
let app = create_router(state);
let req_body = serde_json::json!({
"model": "default",
"messages": []
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
let status = response.status();
assert!(
status == StatusCode::BAD_REQUEST
|| status == StatusCode::OK
|| status == StatusCode::INTERNAL_SERVER_ERROR,
"Empty messages should be handled, got {}",
status
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_quantized_model_temperature_zero() {
use crate::gguf::GGUFConfig;
let config = GGUFConfig {
architecture: "llama".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("llama"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 2,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 256,
context_length: 128,
rope_theta: 10000.0,
eps: 1e-5,
rope_type: 0,
explicit_head_dim: None,
query_pre_attn_scalar: None,
bos_token_id: None,
eos_token_id: None,
};
let model = create_test_quantized_model(&config);
let state = AppState::with_quantized_model(model).expect("create AppState");
let app = create_router(state);
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Hi"}],
"temperature": 0.0
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::INTERNAL_SERVER_ERROR,
"Temperature=0 should use greedy sampling, got {}",
status
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_quantized_model_max_tokens() {
use crate::gguf::GGUFConfig;
let config = GGUFConfig {
architecture: "llama".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("llama"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 2,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 256,
context_length: 128,
rope_theta: 10000.0,
eps: 1e-5,
rope_type: 0,
explicit_head_dim: None,
query_pre_attn_scalar: None,
bos_token_id: None,
eos_token_id: None,
};
let model = create_test_quantized_model(&config);
let state = AppState::with_quantized_model(model).expect("create AppState");
let app = create_router(state);
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Hi"}],
"max_tokens": 5
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::INTERNAL_SERVER_ERROR,
"max_tokens should be respected, got {}",
status
);
}
#[tokio::test]
async fn test_registry_fallback_path() {
use crate::api::test_helpers::create_test_app_shared;
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Hello"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_registry_streaming_fallback() {
use crate::api::test_helpers::create_test_app_shared;
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Hi"}],
"stream": true
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_stream_handler_endpoint() {
use crate::api::test_helpers::create_test_app_shared;
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Test"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions/stream")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
let status = response.status();
crate::api::test_helpers::assert_no_model_status(status);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_empty_prompt_after_tokenization() {
use crate::gguf::GGUFConfig;
let config = GGUFConfig {
architecture: "llama".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("llama"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 2,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 256,
context_length: 128,
rope_theta: 10000.0,
eps: 1e-5,
rope_type: 0,
explicit_head_dim: None,
query_pre_attn_scalar: None,
bos_token_id: None,
eos_token_id: None,
};
let model = create_test_quantized_model(&config);
let state = AppState::with_quantized_model(model).expect("create AppState");
let app = create_router(state);
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": " "}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
let status = response.status();
assert!(
status == StatusCode::OK
|| status == StatusCode::BAD_REQUEST
|| status == StatusCode::INTERNAL_SERVER_ERROR,
"Whitespace-only should be handled, got {}",
status
);
}
#[tokio::test]
#[cfg(feature = "gpu")]
async fn test_finish_reason_length() {
use crate::gguf::GGUFConfig;
let config = GGUFConfig {
architecture: "llama".to_string(),
constraints: crate::gguf::ArchConstraints::from_architecture("llama"),
hidden_dim: 64,
intermediate_dim: 128,
num_layers: 2,
num_heads: 4,
num_kv_heads: 4,
vocab_size: 256,
context_length: 128,
rope_theta: 10000.0,
eps: 1e-5,
rope_type: 0,
explicit_head_dim: None,
query_pre_attn_scalar: None,
bos_token_id: None,
eos_token_id: None,
};
let model = create_test_quantized_model(&config);
let state = AppState::with_quantized_model(model).expect("create AppState");
let app = create_router(state);
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Hi"}],
"max_tokens": 1
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("build"),
)
.await
.expect("send");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::INTERNAL_SERVER_ERROR,
"Should handle max_tokens=1, got {}",
status
);
}