From 39868e6e5c91d0e1694e33bc7ef303b2ca1eef34 Mon Sep 17 00:00:00 2001 From: Olaf Geibig Date: Tue, 14 Oct 2025 18:51:33 +0200 Subject: [PATCH] Added MAX_PASSAGE_LENGTH and missing deps mlx and psutil --- .env.example | 1 + app/models/requests.py | 38 +++++++++++++++++++++++--------------- pyproject.toml | 2 ++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.env.example b/.env.example index e2efdfb..fa74efc 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,7 @@ BATCH_SIZE=32 MAX_BATCH_SIZE=128 MAX_TEXTS_PER_REQUEST=100 MAX_PASSAGES_PER_RERANK=1000 +MAX_PASSAGE_LENGTH=4096 MAX_SEQUENCE_LENGTH=512 DEVICE_MEMORY_FRACTION=0.8 REQUEST_TIMEOUT=300 diff --git a/app/models/requests.py b/app/models/requests.py index aac9fd9..04f65df 100644 --- a/app/models/requests.py +++ b/app/models/requests.py @@ -2,6 +2,7 @@ Pydantic models for API requests. """ +import os from typing import List, Literal, Optional from pydantic import BaseModel, Field, field_validator @@ -30,9 +31,7 @@ class EmbedRequest(BaseModel): description="Whether to automatically truncate texts exceeding token limits", json_schema_extra={"example": True}, ) - truncation_strategy: Optional[ - Literal["smart_truncate", "truncate", "extract", "error"] - ] = Field( + truncation_strategy: Optional[Literal["smart_truncate", "truncate", "extract", "error"]] = Field( "smart_truncate", description=( "Strategy for handling long texts: smart_truncate (preserve sentences), " @@ -53,7 +52,7 @@ class EmbedRequest(BaseModel): json_schema_extra={"example": False}, ) - @field_validator('texts') + @field_validator("texts") @classmethod def validate_texts(cls, v, info): """Validate texts array with enhanced error handling.""" @@ -64,7 +63,7 @@ def validate_texts(cls, v, info): raise ValueError("texts cannot be empty") # Get auto_truncate setting from the same data - auto_truncate = info.data.get('auto_truncate', True) if info.data else True + auto_truncate = info.data.get("auto_truncate", True) if info.data else True for i, text in enumerate(v): if not isinstance(text, str): @@ -79,12 +78,12 @@ def validate_texts(cls, v, info): return v - @field_validator('batch_size') + @field_validator("batch_size") @classmethod def validate_batch_size(cls, v, info): """Validate batch size relative to number of texts.""" - if info.data and 'texts' in info.data and info.data['texts']: - num_texts = len(info.data['texts']) + if info.data and "texts" in info.data and info.data["texts"]: + num_texts = len(info.data["texts"]) if v > num_texts: # Adjust batch size to not exceed number of texts return num_texts @@ -121,7 +120,7 @@ class RerankRequest(BaseModel): True, description="Whether to return the original passage texts", json_schema_extra={"example": True} ) - @field_validator('query') + @field_validator("query") @classmethod def validate_query(cls, v): """Validate query text.""" @@ -133,7 +132,7 @@ def validate_query(cls, v): return v.strip() - @field_validator('passages') + @field_validator("passages") @classmethod def validate_passages(cls, v): """Validate passage inputs.""" @@ -147,17 +146,26 @@ def validate_passages(cls, v): if not passage.strip(): raise ValueError(f"Passage at index {i} cannot be empty or whitespace only") - if len(passage) > 4096: # Reasonable character limit for passages - raise ValueError(f"Passage at index {i} too long: {len(passage)} > 4096 characters") + max_passage_length = int(os.getenv("MAX_PASSAGE_LENGTH", "4096")) + + for i, passage in enumerate(v): + if not isinstance(passage, str): + raise ValueError(f"Passage at index {i} must be a string") + + if not passage.strip(): + raise ValueError(f"Passage at index {i} cannot be empty or whitespace only") + + if len(passage) > max_passage_length: + raise ValueError(f"Passage at index {i} too long: {len(passage)} > {max_passage_length} characters") return v - @field_validator('top_k') + @field_validator("top_k") @classmethod def validate_top_k(cls, v, info): """Validate top_k relative to number of passages.""" - if info.data and 'passages' in info.data and info.data['passages']: - num_passages = len(info.data['passages']) + if info.data and "passages" in info.data and info.data["passages"]: + num_passages = len(info.data["passages"]) if v > num_passages: # Adjust top_k to not exceed number of passages return num_passages diff --git a/pyproject.toml b/pyproject.toml index 5d9200e..0e8c9b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,8 @@ dependencies = [ "python-dotenv", "structlog", "prometheus-client", + "mlx>=0.29.2", + "psutil>=7.1.0", ] [project.optional-dependencies]