champ-chatbot / classes /base_models.py
qyle's picture
Deploy from GitLab e4ca3570
a4c6e98
Raw
History Blame Contribute Delete
3.72 kB
import nh3
from constants import (
MAX_COMMENT_LENGTH,
MAX_FILE_NAME_LENGTH,
MAX_ID_LENGTH,
MAX_MESSAGE_LENGTH,
MAX_RESPONSE_LENGTH,
)
from pydantic import BaseModel, Field, field_validator
from typing import Literal, Set
from uuid import UUID
class IdentifierBase(BaseModel):
user_id: str = Field(
pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH
)
# Participant ID could be in ProfileBase instead. It doesn't really matter.
participant_id: str = Field(
pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH
)
session_id: str = Field(
pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH
)
class ProfileBase(BaseModel):
consent: bool
age_group: Literal["0-18", "18-24", "25-34", "35-44", "45-54", "55-64", "65+"]
gender: Literal["M", "F"]
roles: Set[
Literal["patient", "clinician", "computer-scientist", "researcher", "other"]
] = Field(min_length=1, max_length=5)
class ChatRequest(IdentifierBase, ProfileBase):
conversation_id: str = Field(
pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH
)
model_type: Literal[
"champ",
"openai",
"google-conservative",
"google-creative",
"qwen",
"fake",
"skills_wiki",
]
lang: Literal["en", "fr"]
human_message: str = Field(min_length=1, max_length=MAX_MESSAGE_LENGTH)
@field_validator("human_message")
def sanitize_human_message(cls, human_message: str):
"""Remove HTML tags to prevent XSS"""
return nh3.clean(human_message)
class FeedbackRequest(IdentifierBase, ProfileBase):
message_index: int = Field(ge=0, le=10_000)
rating: Literal["like", "dislike", "mixed"]
comment: str = Field(min_length=0, max_length=MAX_COMMENT_LENGTH)
reply_content: str = Field(min_length=1, max_length=MAX_RESPONSE_LENGTH)
reply_id: UUID
@field_validator("comment")
def sanitize_comment(cls, comment: str):
"""Remove HTML tags to prevent XSS"""
return nh3.clean(comment)
@field_validator("reply_content")
def sanitize_reply_content(cls, reply_content: str):
"""Remove HTML tags to prevent XSS"""
return nh3.clean(reply_content)
class CommentRequest(IdentifierBase, ProfileBase):
comment: str = Field(min_length=1, max_length=MAX_COMMENT_LENGTH)
@field_validator("comment")
def sanitize_comment(cls, comment: str):
"""Remove HTML tags to prevent XSS"""
return nh3.clean(comment)
class DeleteFileRequest(IdentifierBase, ProfileBase):
file_name: str = Field(
# Matches is_valid_filename in helpers/file_helper.py: block only
# control characters, path separators, and HTML/attribute-injection
# characters — everything else a real filename can contain (commas,
# apostrophes, colons, etc.) is allowed, so anything accepted on
# upload can also be deleted. The "." / ".." exclusion lives in the
# validator below — pydantic-core's regex engine has no look-ahead.
pattern=r'^[^\x00-\x1f\\/<>"]+$',
min_length=1,
max_length=MAX_FILE_NAME_LENGTH,
)
@field_validator("file_name")
def reject_dot_names(cls, file_name: str):
if file_name in {".", ".."}:
raise ValueError('file_name cannot be "." or ".."')
return file_name
class ClearConversationRequest(BaseModel):
old_session_id: str = Field(
pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH
)
new_session_id: str = Field(
pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH
)