Download classes/base_models.py from marvin-cusm-chatbot/champ-chatbot: direct link, hf CLI and curl.
- Browser
- Download file 5.39 kB
-
https://huggingface.co/spaces/marvin-cusm-chatbot/champ-chatbot/resolve/main/classes/base_models.py
- Command line
-
hf download hf://spaces/marvin-cusm-chatbot/champ-chatbot/classes/base_models.py
-
curl -L -o base_models.py https://huggingface.co/spaces/marvin-cusm-chatbot/champ-chatbot/resolve/main/classes/base_models.py
5.39 kB
| from enum import StrEnum | |
| from typing import Literal, Set | |
| from uuid import UUID | |
| import nh3 | |
| from pydantic import BaseModel, Field, field_validator | |
| from constants import ( | |
| CHAMP_RAG_ENABLED, | |
| MAX_COMMENT_LENGTH, | |
| MAX_FILE_NAME_LENGTH, | |
| MAX_ID_LENGTH, | |
| MAX_MESSAGE_LENGTH, | |
| MAX_RESPONSE_LENGTH, | |
| ) | |
| class AgeGroup(StrEnum): | |
| AGE_0_17 = "0-17" | |
| AGE_18_24 = "18-24" | |
| AGE_25_34 = "25-34" | |
| AGE_35_44 = "35-44" | |
| AGE_45_54 = "45-54" | |
| AGE_55_64 = "55-64" | |
| AGE_65_PLUS = "65+" | |
| class Gender(StrEnum): | |
| MALE = "male" | |
| FEMALE = "female" | |
| NON_BINARY = "non-binary" | |
| OTHER = "other" | |
| PREFER_NOT = "prefer-not" | |
| class Role(StrEnum): | |
| PATIENT = "patient" | |
| CLINICIAN = "clinician" | |
| COMPUTER_SCIENTIST = "computer-scientist" | |
| RESEARCHER = "researcher" | |
| OTHER = "other" | |
| ModelType = Literal[ | |
| "champ", | |
| "openai", | |
| "google-conservative", | |
| "google-creative", | |
| "fake", | |
| "skills_wiki", | |
| ] | |
| class IdentifierBase(BaseModel): | |
| # 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 | |
| ) | |
| class ProfileBase(BaseModel): | |
| consent: bool | |
| age_group: AgeGroup | |
| gender: Gender | |
| roles: Set[Role] = 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 | |
| ) | |
| lang: Literal["en", "fr"] | |
| human_message: str = Field(min_length=1, max_length=MAX_MESSAGE_LENGTH) | |
| def sanitize_human_message(cls, human_message: str): | |
| """Remove HTML tags to prevent XSS""" | |
| return nh3.clean(human_message) | |
| class StartConversationRequest(BaseModel): | |
| model_type: ModelType | |
| def refuse_rag_champ_when_off(cls, model_type: str) -> str: | |
| """Refuse the RAG model type (champ) unless CHAMP_RAG_ENABLED turned it on. | |
| Args: | |
| model_type (str): The model type asked for. | |
| Raises: | |
| ValueError: When it is the RAG model type and that is off; answered with a 422. | |
| Returns: | |
| str: The model type. | |
| """ | |
| if model_type == "champ" and not CHAMP_RAG_ENABLED: | |
| raise ValueError( | |
| "the RAG model type 'champ' is turned off (CHAMP_RAG_ENABLED is not true)" | |
| ) | |
| return model_type | |
| class ClearConversationRequest(BaseModel): | |
| conversation_id: str = Field( | |
| pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH | |
| ) | |
| class RestoredTurn(BaseModel): | |
| """A turn as the user saw it, with the figures its reply showed.""" | |
| human_message: str | |
| reply: str | |
| reply_id: str | |
| gwp_kgcoeq: float | |
| water_L: float | |
| electricity_kWh: float | |
| n_tokens: int | |
| class CurrentConversation(BaseModel): | |
| """A conversation to show again: its turns as the user saw them, and its files' names.""" | |
| conversation_id: str | |
| model_type: str | |
| turns: list[RestoredTurn] | |
| file_names: list[str] | |
| class FeedbackRequest(IdentifierBase, ProfileBase): | |
| conversation_id: str = Field( | |
| pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH | |
| ) | |
| 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 | |
| def sanitize_comment(cls, comment: str): | |
| """Remove HTML tags to prevent XSS""" | |
| return nh3.clean(comment) | |
| 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) | |
| # The conversation on screen; None when the user hasn't sent a message yet. | |
| conversation_id: str | None = Field( | |
| default=None, pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH | |
| ) | |
| def sanitize_comment(cls, comment: str): | |
| """Remove HTML tags to prevent XSS""" | |
| return nh3.clean(comment) | |
| class DeleteFileRequest(IdentifierBase, ProfileBase): | |
| conversation_id: str = Field( | |
| pattern="^[a-zA-Z0-9_-]+$", min_length=1, max_length=MAX_ID_LENGTH | |
| ) | |
| 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, | |
| ) | |
| def reject_dot_names(cls, file_name: str): | |
| if file_name in {".", ".."}: | |
| raise ValueError('file_name cannot be "." or ".."') | |
| return file_name | |