Skip to main content

bge_m3_embedding_server/handler/
common.rs

1// Copyright (c) 2026 J. Patrick Fulton
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Shared input validation and service-readiness helpers used by all handlers.
16
17use std::sync::atomic::Ordering;
18
19use crate::error::AppError;
20use crate::state::AppState;
21
22/// Maximum characters allowed per individual input string (SEC-3).
23pub(super) const MAX_STRING_CHARS: usize = 32_768;
24
25/// Validates a batch of input texts against size and length constraints.
26///
27/// Returns [`AppError::InvalidRequest`] if:
28/// - `texts` is empty
29/// - `texts.len() > max_batch`
30/// - any individual text exceeds [`MAX_STRING_CHARS`] characters
31pub(super) fn validate_input(texts: &[String], max_batch: usize) -> Result<(), AppError> {
32    if texts.is_empty() {
33        return Err(AppError::InvalidRequest(
34            "input must not be empty".to_string(),
35        ));
36    }
37    if texts.len() > max_batch {
38        return Err(AppError::InvalidRequest(format!(
39            "batch size {} exceeds maximum {}",
40            texts.len(),
41            max_batch
42        )));
43    }
44    for (i, text) in texts.iter().enumerate() {
45        let char_count = text.chars().count();
46        if char_count > MAX_STRING_CHARS {
47            return Err(AppError::InvalidRequest(format!(
48                "input[{i}] length {char_count} exceeds maximum {MAX_STRING_CHARS} characters"
49            )));
50        }
51    }
52    Ok(())
53}
54
55/// Checks whether the service is ready to handle embedding requests.
56pub(super) fn check_ready(state: &AppState) -> Result<(), AppError> {
57    if !state.ready.load(Ordering::Acquire) {
58        return Err(AppError::ServiceUnavailable("model not ready".to_string()));
59    }
60    if state.pool.live_worker_count() == 0 {
61        return Err(AppError::ServiceUnavailable(
62            "no workers available".to_string(),
63        ));
64    }
65    Ok(())
66}