Skip to content

Entities

src.entities

src.entities.Conversation

SQLAlchemy entity for conversation sessions with LLM generation parameters.

Represents a chat session with a specific LLM, storing messages, generation settings, and conversation metadata. Enforces parameter validation via SQLAlchemy validators.

Relationships
  • messages: One-to-many with Message (cascade delete).
  • llm: Many-to-one with Llm (which model is used).
Example

from src.entities.Conversation import Conversation

conv = Conversation( llm_id=42, name="Python Programming Help", temperature=0.7, top_p=0.9, max_tokens=2048 )

Conversation

Bases: Base

SQLAlchemy model for conversation sessions with generation parameter validation.

Stores chat sessions with LLM-specific generation settings. Each conversation maintains its own temperature, top_p, and max_tokens parameters. Messages are stored separately with cascade delete.

Attributes:

Name Type Description
id

Primary key (auto-increment).

llm_id

Foreign key to Llm, NULLABLE (server-side SET NULL on model delete). A conversation survives the deletion of its model and can be switched to another one (#225).

created_at

Conversation creation timestamp (server-stamped).

updated_at

Last modification timestamp (server-stamped, auto-updated).

temperature

Sampling temperature (0.0-2.0, validated).

top_p

Nucleus sampling threshold (0.0-1.0, validated).

max_tokens

Maximum tokens to generate (1-32768, validated).

custom_prompt

Optional additional system instructions.

name

Conversation title (default "New Conversation").

web_search_enabled

Per-conversation web-search toggle.

reasoning_effort

How much the model may deliberate (validated).

compaction_threshold

Share of the working window, in percent, at which the conversation compacts (50-90 step 5, validated).

messages

Relationship to Message entities (ordered by id = insertion order).

llm

Relationship to Llm entity (which model).

message_count int

Hybrid property returning number of messages.

last_message_time Optional[datetime]

Hybrid property returning last message timestamp.

Example

conv = Conversation(llm_id=42, name="Debug Session", temperature=0.3, max_tokens=4096) print(conv.message_count) # 0 (messages are added via MessageRepository)

__repr__()

String representation of the conversation.

Source code in backend/src/entities/Conversation.py
def __repr__(self) -> str:
    """String representation of the conversation."""
    return (
        f"<Conversation(id={self.id}, name='{self.name}', " f"messages={self.message_count})>"
    )

last_message_time()

Get timestamp of most recent message.

Returns:

Type Description
Optional[datetime]

Last message timestamp, or conversation created_at if no messages.

Source code in backend/src/entities/Conversation.py
@hybrid_property
def last_message_time(self) -> Optional[datetime]:
    """Get timestamp of most recent message.

    Returns:
        Last message timestamp, or conversation created_at if no messages.
    """
    if self.messages:
        return max(msg.timestamp for msg in self.messages)
    return self.created_at

message_count()

Get total number of messages in this conversation.

Returns:

Type Description
int

Message count (user + assistant messages).

Source code in backend/src/entities/Conversation.py
@hybrid_property
def message_count(self) -> int:
    """Get total number of messages in this conversation.

    Returns:
        Message count (user + assistant messages).
    """
    return len(self.messages)

validate_compaction_threshold(key, compaction_threshold)

Validate the compaction threshold is a whole percent on the scale.

Parameters:

Name Type Description Default
key str

Column name ("compaction_threshold").

required
compaction_threshold int

Percentage to validate.

required

Returns:

Type Description
int

Validated percentage.

Raises:

Type Description
ValueError

If it is not an int in COMPACTION_THRESHOLDS (50-90, step 5); a bool, a float or a string is rejected.

Source code in backend/src/entities/Conversation.py
@validates("compaction_threshold")
def validate_compaction_threshold(self, key: str, compaction_threshold: int) -> int:
    """Validate the compaction threshold is a whole percent on the scale.

    Args:
        key: Column name ("compaction_threshold").
        compaction_threshold: Percentage to validate.

    Returns:
        Validated percentage.

    Raises:
        ValueError: If it is not an int in COMPACTION_THRESHOLDS (50-90,
            step 5); a bool, a float or a string is rejected.
    """
    if not is_compaction_threshold(compaction_threshold):
        raise ValueError(f"Compaction threshold must be one of {COMPACTION_THRESHOLDS}")
    return compaction_threshold

validate_max_tokens(key, max_tokens)

Validate max_tokens parameter is within allowed range.

Parameters:

Name Type Description Default
key str

Column name ("max_tokens").

required
max_tokens int

Max_tokens value to validate.

required

Returns:

Type Description
int

Validated max_tokens value.

Raises:

Type Description
ValueError

If max_tokens not in range [1, 32768].

Source code in backend/src/entities/Conversation.py
@validates("max_tokens")
def validate_max_tokens(self, key: str, max_tokens: int) -> int:
    """Validate max_tokens parameter is within allowed range.

    Args:
        key: Column name ("max_tokens").
        max_tokens: Max_tokens value to validate.

    Returns:
        Validated max_tokens value.

    Raises:
        ValueError: If max_tokens not in range [1, 32768].
    """
    if not 1 <= max_tokens <= 32768:
        raise ValueError("Max tokens must be between 1 and 32768")
    return max_tokens

validate_reasoning_effort(key, reasoning_effort)

Validate the reasoning effort is one of the five levels.

Parameters:

Name Type Description Default
key str

Column name ("reasoning_effort").

required
reasoning_effort str

Level to validate.

required

Returns:

Type Description
str

Validated level.

Raises:

Type Description
ValueError

If the level is not in REASONING_EFFORT_LEVELS.

Source code in backend/src/entities/Conversation.py
@validates("reasoning_effort")
def validate_reasoning_effort(self, key: str, reasoning_effort: str) -> str:
    """Validate the reasoning effort is one of the five levels.

    Args:
        key: Column name ("reasoning_effort").
        reasoning_effort: Level to validate.

    Returns:
        Validated level.

    Raises:
        ValueError: If the level is not in REASONING_EFFORT_LEVELS.
    """
    if reasoning_effort not in REASONING_EFFORT_LEVELS:
        raise ValueError(f"Reasoning effort must be one of {REASONING_EFFORT_LEVELS}")
    return reasoning_effort

validate_temperature(key, temperature)

Validate temperature parameter is within allowed range.

Parameters:

Name Type Description Default
key str

Column name ("temperature").

required
temperature float

Temperature value to validate.

required

Returns:

Type Description
float

Validated temperature value.

Raises:

Type Description
ValueError

If temperature not in range [0.0, 2.0].

Source code in backend/src/entities/Conversation.py
@validates("temperature")
def validate_temperature(self, key: str, temperature: float) -> float:
    """Validate temperature parameter is within allowed range.

    Args:
        key: Column name ("temperature").
        temperature: Temperature value to validate.

    Returns:
        Validated temperature value.

    Raises:
        ValueError: If temperature not in range [0.0, 2.0].
    """
    if not 0.0 <= temperature <= 2.0:
        raise ValueError("Temperature must be between 0.0 and 2.0")
    return temperature

validate_top_p(key, top_p)

Validate top_p parameter is within allowed range.

Parameters:

Name Type Description Default
key str

Column name ("top_p").

required
top_p float

Top_p value to validate.

required

Returns:

Type Description
float

Validated top_p value.

Raises:

Type Description
ValueError

If top_p not in range [0.0, 1.0].

Source code in backend/src/entities/Conversation.py
@validates("top_p")
def validate_top_p(self, key: str, top_p: float) -> float:
    """Validate top_p parameter is within allowed range.

    Args:
        key: Column name ("top_p").
        top_p: Top_p value to validate.

    Returns:
        Validated top_p value.

    Raises:
        ValueError: If top_p not in range [0.0, 1.0].
    """
    if not 0.0 <= top_p <= 1.0:
        raise ValueError("Top_p must be between 0.0 and 1.0")
    return top_p

src.entities.DownloadJob

SQLAlchemy entity for LLM download job tracking with progress and ETA.

Represents a background download task for fetching and quantizing LLMs from HuggingFace. Tracks progress, timing, file paths, and error states.

Example

from src.entities.DownloadJob import DownloadJobModel

job = DownloadJobModel( remote_model_id="meta-llama/Llama-3-8B-Instruct", remote_model_link="https://huggingface.co/meta-llama/Llama-3-8B-Instruct", local_model_id=42, status="pending" )

DownloadJobModel

Bases: Base

SQLAlchemy model for LLM download jobs with progress tracking and file paths.

Tracks the lifecycle of downloading and quantizing an LLM from HuggingFace, including progress percentage, ETA, temp/final file paths, and error messages.

Attributes:

Name Type Description
id

Primary key (auto-increment).

remote_model_id

HuggingFace repo ID (e.g., "meta-llama/Llama-3-8B-Instruct").

local_model_id

Database ID of temp Llm entry created during download.

remote_model_link

HuggingFace URL or API endpoint.

temp_local_model_link

Temp directory for full-precision download.

final_local_model_link

Final directory for quantized model.

status

Job state - "pending", "running", "completed", "failed", "cancelled".

error_message

Exception message if status="failed".

created_at

Job creation timestamp (server default).

updated_at

Last status update timestamp (auto-updated).

total_bytes

Total download size in bytes.

progress

Download progress percentage (0.0-100.0).

total_time_elapsed

Seconds elapsed since job start.

time_left

Estimated seconds remaining.

Constraints
  • status must be one of: pending, running, completed, failed, cancelled.
  • progress must be between 0.0 and 100.0.
  • total_bytes, total_time_elapsed, time_left must be non-negative.
  • remote_model_id and remote_model_link must not be empty.
Example

job = DownloadJobModel(remote_model_id="Qwen/Qwen2.5-7B-Instruct", status="pending") job.progress = 50.0 job.time_left = 120.5

validate_non_negative(key, value)

Ensure numeric fields are non-negative.

Parameters:

Name Type Description Default
key

Column name being validated (one of: total_bytes, total_time_elapsed, time_left).

required
value

Proposed numeric value.

required

Returns:

Name Type Description
float

The validated non-negative value.

Raises:

Type Description
ValueError

If value is negative.

Source code in backend/src/entities/DownloadJob.py
@validates("total_bytes", "total_time_elapsed", "time_left")
def validate_non_negative(self, key, value):
    """Ensure numeric fields are non-negative.

    Args:
        key: Column name being validated (one of: total_bytes, total_time_elapsed, time_left).
        value: Proposed numeric value.

    Returns:
        float: The validated non-negative value.

    Raises:
        ValueError: If value is negative.
    """
    if value < 0:
        raise ValueError(f"{key} must be non-negative, got {value}")
    return value

validate_not_empty(key, value)

Ensure required string fields are not empty.

Parameters:

Name Type Description Default
key

Column name being validated (one of: remote_model_id, remote_model_link).

required
value

Proposed string value.

required

Returns:

Name Type Description
str

The validated stripped string value.

Raises:

Type Description
ValueError

If value is empty or whitespace-only.

Source code in backend/src/entities/DownloadJob.py
@validates("remote_model_id", "remote_model_link")
def validate_not_empty(self, key, value):
    """Ensure required string fields are not empty.

    Args:
        key: Column name being validated (one of: remote_model_id, remote_model_link).
        value: Proposed string value.

    Returns:
        str: The validated stripped string value.

    Raises:
        ValueError: If value is empty or whitespace-only.
    """
    if not value or not value.strip():
        raise ValueError(f"{key} cannot be empty")
    return value.strip() if isinstance(value, str) else value

validate_progress(key, value)

Ensure progress is between 0.0 and 100.0.

Parameters:

Name Type Description Default
key

Column name being validated ('progress').

required
value

Proposed float value for download progress percentage.

required

Returns:

Name Type Description
float

The validated progress value.

Raises:

Type Description
ValueError

If progress is outside 0.0-100.0 range.

Source code in backend/src/entities/DownloadJob.py
@validates("progress")
def validate_progress(self, key, value):
    """Ensure progress is between 0.0 and 100.0.

    Args:
        key: Column name being validated ('progress').
        value: Proposed float value for download progress percentage.

    Returns:
        float: The validated progress value.

    Raises:
        ValueError: If progress is outside 0.0-100.0 range.
    """
    if not (0.0 <= value <= 100.0):
        raise ValueError(f"Progress must be between 0.0 and 100.0, got {value}")
    return value

validate_status(key, value)

Ensure status is one of the allowed values.

Parameters:

Name Type Description Default
key

Column name being validated ('status').

required
value

Proposed string value for job status.

required

Returns:

Name Type Description
str

The validated status value.

Raises:

Type Description
ValueError

If status is not in allowed list.

Source code in backend/src/entities/DownloadJob.py
@validates("status")
def validate_status(self, key, value):
    """Ensure status is one of the allowed values.

    Args:
        key: Column name being validated ('status').
        value: Proposed string value for job status.

    Returns:
        str: The validated status value.

    Raises:
        ValueError: If status is not in allowed list.
    """
    allowed = ["pending", "running", "completed", "failed", "cancelled"]
    if value not in allowed:
        raise ValueError(f"Invalid status: {value}. Must be one of {allowed}")
    return value

src.entities.KBJob

SQLAlchemy entity for Knowledge Base creation/update job tracking.

Tracks background ingestion tasks (extract → chunk → embed → index) during KB assistant creation or update. Monitors status and error messages.

Example

from src.entities.KBJob import KBJobModel

job = KBJobModel( base_model_id=42, new_model_id=108, kb_id=15, status="pending" )

KBJobModel

Bases: Base

SQLAlchemy model for Knowledge Base creation/update job tracking.

The id columns are real foreign keys with ON DELETE SET NULL: a job is an audit record of its state machine and must survive the deletion of the entities it produced (cleanup of a failed creation deletes the specialized LLM and the KB — the failed job remains, refs nulled by the server).

Attributes:

Name Type Description
id

Primary key (auto-increment).

base_model_id

Base LLM used as foundation (nulled if deleted).

new_model_id

Specialized LLM created for this KB; equals base_model_id for update jobs (nulled if deleted).

kb_id

KnowledgeBase being created/updated (nulled if deleted).

status

Job state - "pending", "running", "completed", "failed".

error_message

Exception message if status="failed".

created_at

Job creation timestamp (server-stamped).

updated_at

Last status update timestamp (server-stamped).

Example

job = KBJobModel(base_model_id=42, new_model_id=108, kb_id=15, status="running")

src.entities.KnowledgeBase

SQLAlchemy entity for Knowledge Base registry.

A KnowledgeBase is the business-level container for a RAG corpus: it owns the ingested source files (documents → KnowledgeDocument rows) and the specialized assistant built on top of it (llm, one-to-one). The chunks + embeddings themselves live in the pgvector-managed rag.kb_chunks table (langchain-postgres), filtered by kb_id at retrieval time.

Relationships
  • documents: One-to-many with KnowledgeDocument (server-side CASCADE).
  • llm: One-to-one with Llm (specialized assistant using this KB).

KnowledgeBase

Bases: Base

Registry row for one Knowledge Base.

Attributes:

Name Type Description
id

Primary key (auto-increment).

created_at

Server-stamped creation timestamp.

documents

Ingested source files (KnowledgeDocument rows).

llm

Specialized Llm entity attached to this KB (one-to-one).

src.entities.KnowledgeDocument

SQLAlchemy entity for documents ingested into a Knowledge Base.

One row per source file dropped into a KB. Replaces the FAISS-era KnowledgeBase.file_names_list JSON blob: documents are first-class rows with a content hash (dedup), a size, and a per-file ingestion status. The actual text chunks + embeddings live in the pgvector-managed rag.kb_chunks table (langchain-postgres), keyed back here through a document_id metadata column.

Statuses
  • active: extracted, chunked, and indexed — retrievable.
  • empty: extracted but produced zero indexable chunks (blank/whitespace file or a parser that yielded no text); nothing searchable was added.
  • failed: ingestion raised; error surfaced via the KB job.
  • pending_vision: image or scanned PDF accepted but not yet readable (OCR/VLM tiers land in a later release); zero chunks indexed.

KnowledgeDocument

Bases: Base

A single source file tracked inside a Knowledge Base.

Attributes:

Name Type Description
id

Primary key (auto-increment).

kb_id

Owning KnowledgeBase (server-side ON DELETE CASCADE).

name

Original file name (display + extension routing).

content_hash_sha256

SHA-256 of the file bytes; UNIQUE per KB (dedup).

size_bytes

File size at ingestion time.

status

Per-file ingestion state (see module docstring).

created_at

Server-stamped ingestion timestamp.

validate_status(key, value)

Ensure status is one of the allowed ingestion states.

Source code in backend/src/entities/KnowledgeDocument.py
@validates("status")
def validate_status(self, key, value):
    """Ensure status is one of the allowed ingestion states."""
    if value not in ALLOWED_STATUSES:
        raise ValueError(f"Invalid status: {value}. Must be one of {list(ALLOWED_STATUSES)}")
    return value

src.entities.Llm

SQLAlchemy entity for LLM model catalog and metadata.

This entity represents the complete lifecycle of an LLM model in Erudi: - Remote models: Browsable from HuggingFace (local=0). - Downloading: Temporary placeholder during download (local=2). - Local models: Downloaded and ready for inference (local=1). - KB-attached: Specialized assistants with Knowledge Base (is_attached_to_kb=True).

Relationships
  • kb: One-to-one with KnowledgeBase (if is_attached_to_kb=True).
Example

from src.entities.Llm import Llm

Create remote model entry

llm = Llm( name="Llama-3-8B-Instruct", local=0, link="meta-llama/Meta-Llama-3-8B-Instruct", type="llama", param_size=8.0, quantized=False )

Llm

Bases: Base

SQLAlchemy model for LLM catalog entries with download state and KB attachment.

Represents a single LLM model in the database, tracking its download status, metadata, and optional Knowledge Base attachment. Models can be remote (browsable), downloading (temporary), or local (ready for inference).

Attributes:

Name Type Description
id

Primary key (auto-increment).

name

Human-readable model name (e.g., "Llama-3-8B-Instruct").

local

Download state - 0=remote (HuggingFace), 1=local (ready), 2=downloading.

link

HuggingFace repo ID (if remote) or local filesystem path (if local).

type

Model family (e.g., "llama", "qwen", "mistral", "gemma").

description

Optional user annotation or HuggingFace description.

model_metadata

JSON string with additional metadata (vocab size, context length).

quantized

Boolean - False=not quantized (full precision), True=pre-quantized (MLX/GGUF format).

param_size

Model size in billions of parameters (2, 4, 8, 16, 70, etc.), or None when the size could not be measured (#201).

is_attached_to_kb

Boolean - False=standalone model, True=specialized KB assistant.

kb_id

Foreign key to KnowledgeBase (if is_attached_to_kb=True).

kb

Relationship to KnowledgeBase entity (one-to-one).

Constraints
  • local must be 0 (remote), 1 (local), or 2 (downloading).
  • param_size must be positive when known (None allowed for unknown).
  • name must not be empty.
Example

llm = Llm(name="Qwen2.5-7B", local=1, link="backend/data/models/42", type="qwen", param_size=7.0) db.add(llm) db.commit()

validate_artifact_size_bytes(key, value)

A known artifact size is a positive byte count; None means unknown.

Zero or negative is never a measurement (an empty directory walk, a listing without sizes) and must not masquerade as one -- callers store None in that case so the UI keeps its estimate.

Source code in backend/src/entities/Llm.py
@validates("artifact_size_bytes")
def validate_artifact_size_bytes(self, key, value):
    """A known artifact size is a positive byte count; None means unknown.

    Zero or negative is never a measurement (an empty directory walk, a
    listing without sizes) and must not masquerade as one -- callers store
    None in that case so the UI keeps its estimate.
    """
    if value is None:
        return None
    if value <= 0:
        raise ValueError(f"artifact_size_bytes must be positive, got {value}")
    return int(value)

validate_local(key, value)

Ensure local is 0 (remote), 1 (ready), or 2 (downloading).

Parameters:

Name Type Description Default
key

Column name being validated ('local').

required
value

Proposed integer value for download state.

required

Returns:

Name Type Description
int

The validated local state value.

Raises:

Type Description
ValueError

If value is not 0, 1, or 2.

Source code in backend/src/entities/Llm.py
@validates("local")
def validate_local(self, key, value):
    """Ensure local is 0 (remote), 1 (ready), or 2 (downloading).

    Args:
        key: Column name being validated ('local').
        value: Proposed integer value for download state.

    Returns:
        int: The validated local state value.

    Raises:
        ValueError: If value is not 0, 1, or 2.
    """
    if value not in [0, 1, 2]:
        raise ValueError(
            f"Invalid local state: {value}. Must be 0 (remote), 1 (ready), or 2 (downloading)"
        )
    return value

validate_name(key, value)

Ensure name is not empty.

Parameters:

Name Type Description Default
key

Column name being validated ('name').

required
value

Proposed value for the name field.

required

Returns:

Name Type Description
str

Stripped name value if valid.

Raises:

Type Description
ValueError

If name is empty or whitespace-only.

Source code in backend/src/entities/Llm.py
@validates("name")
def validate_name(self, key, value):
    """Ensure name is not empty.

    Args:
        key: Column name being validated ('name').
        value: Proposed value for the name field.

    Returns:
        str: Stripped name value if valid.

    Raises:
        ValueError: If name is empty or whitespace-only.
    """
    if not value or not value.strip():
        raise ValueError("LLM name cannot be empty")
    return value.strip()

validate_param_size(key, value)

Ensure a known param_size is positive; allow None for unknown (#201).

Parameters:

Name Type Description Default
key

Column name being validated ('param_size').

required
value

Proposed float value for parameter size in billions, or None when the size could not be measured.

required

Returns:

Type Description

Optional[float]: The validated parameter size, or None.

Raises:

Type Description
ValueError

If a non-null param_size is zero or negative.

Source code in backend/src/entities/Llm.py
@validates("param_size")
def validate_param_size(self, key, value):
    """Ensure a known param_size is positive; allow None for unknown (#201).

    Args:
        key: Column name being validated ('param_size').
        value: Proposed float value for parameter size in billions, or None
            when the size could not be measured.

    Returns:
        Optional[float]: The validated parameter size, or None.

    Raises:
        ValueError: If a non-null param_size is zero or negative.
    """
    if value is None:
        return None
    if value <= 0:
        raise ValueError(f"param_size must be positive, got {value}")
    return value

validate_type(key, value)

Ensure type is not empty.

Parameters:

Name Type Description Default
key

Column name being validated ('type').

required
value

Proposed string value for model family type.

required

Returns:

Name Type Description
str

Stripped type value if valid.

Raises:

Type Description
ValueError

If type is empty or whitespace-only.

Source code in backend/src/entities/Llm.py
@validates("type")
def validate_type(self, key, value):
    """Ensure type is not empty.

    Args:
        key: Column name being validated ('type').
        value: Proposed string value for model family type.

    Returns:
        str: Stripped type value if valid.

    Raises:
        ValueError: If type is empty or whitespace-only.
    """
    if not value or not value.strip():
        raise ValueError("LLM type cannot be empty")
    return value.strip()

src.entities.Message

SQLAlchemy entity for individual messages within conversations.

Represents a single user or assistant message with metadata for starring and timestamp tracking. Messages belong to a Conversation; insertion order is the primary key (PostgreSQL's now() is frozen per transaction, so timestamps can collide within one request — never order by them).

Relationships
  • conversation: Many-to-one with Conversation (parent session).
Example

from src.entities.Message import Message

msg = Message( conversation_id=42, sender="user", content="What is quantum computing?", starred=False )

Message

Bases: Base

SQLAlchemy model for conversation messages with starring support.

Stores individual messages within conversations, tracking sender, content, server-stamped timestamp, and starred status (for memory injection).

Attributes:

Name Type Description
id

Primary key (auto-increment) — also the insertion order.

conversation_id

Foreign key to Conversation (server-side CASCADE).

sender

Message sender - "user" or "llm" (validated).

content

Message text content (non-empty, no length cap).

timestamp

Message creation timestamp (server-stamped).

starred

True if user starred for importance (used in memory injection).

trace

Optional JSON list of the assistant turn's non-answer stream events (thinking / tool_call / tool_result, and the compaction done events), for replay on reload.

conversation

Relationship to Conversation entity.

Example

msg = Message(conversation_id=42, sender="user", content="Hello!") print(msg.starred) # False (starring is done via MessageRepository)

__repr__()

String representation of the message.

Source code in backend/src/entities/Message.py
def __repr__(self) -> str:
    """String representation of the message."""
    return (
        f"<Message(id={self.id}, sender='{self.sender}', "
        f"content='{self.content[:50]}...', starred={self.starred})>"
    )

validate_content(key, content)

Validate content is non-empty.

Parameters:

Name Type Description Default
key str

Column name ("content").

required
content str

Content value to validate.

required

Returns:

Type Description
str

Validated content value.

Raises:

Type Description
ValueError

If content is empty or whitespace-only.

Source code in backend/src/entities/Message.py
@validates("content")
def validate_content(self, key: str, content: str) -> str:
    """Validate content is non-empty.

    Args:
        key: Column name ("content").
        content: Content value to validate.

    Returns:
        Validated content value.

    Raises:
        ValueError: If content is empty or whitespace-only.
    """
    if not content or len(content.strip()) == 0:
        raise ValueError("Message content cannot be empty")
    return content

validate_sender(key, sender)

Validate sender is either 'user' or 'llm'.

Parameters:

Name Type Description Default
key str

Column name ("sender").

required
sender str

Sender value to validate.

required

Returns:

Type Description
str

Validated sender value.

Raises:

Type Description
ValueError

If sender not in ['user', 'llm'].

Source code in backend/src/entities/Message.py
@validates("sender")
def validate_sender(self, key: str, sender: str) -> str:
    """Validate sender is either 'user' or 'llm'.

    Args:
        key: Column name ("sender").
        sender: Sender value to validate.

    Returns:
        Validated sender value.

    Raises:
        ValueError: If sender not in ['user', 'llm'].
    """
    if sender not in ["user", "llm"]:
        raise ValueError("Sender must be either 'user' or 'llm'")
    return sender

src.entities.StartupVariables

SQLAlchemy entity for application startup state tracking.

Stores persistent UI state flags across app restarts, such as whether the welcome popup has been shown, and model seeding state to avoid unnecessary API calls.

Example

from src.entities.StartupVariables import StartupVariables from datetime import datetime

vars = StartupVariables( welcome_popup_has_already_displayed=True, models_seeded=True, last_seeded_at=datetime.now() )

StartupVariables

Bases: Base

SQLAlchemy model for application startup state persistence.

Stores UI state flags and model seeding state that persist across app restarts. Singleton entity (only one row in table).

Attributes:

Name Type Description
id

Primary key (singleton - only one row).

welcome_popup_has_already_displayed

Boolean - True if welcome popup shown to user.

models_seeded

Boolean - True if base/derived models have been seeded.

last_seeded_at

DateTime - Timestamp of last successful model seeding.

offline_mode

Boolean - True if last startup detected no internet connection.

Constraints
  • welcome_popup_has_already_displayed must be Boolean (enforced by validator).
  • models_seeded must be Boolean (enforced by validator).
  • offline_mode must be Boolean (enforced by validator).
Example

vars = db.query(StartupVariables).first() if not vars.welcome_popup_has_already_displayed: ... show_welcome_popup() ... vars.welcome_popup_has_already_displayed = True ... db.commit()

Check if models need reseeding (every 3 days)

from datetime import datetime, timedelta if not vars.models_seeded or (datetime.now() - vars.last_seeded_at) > timedelta(days=3): ... await seed_models() ... vars.models_seeded = True ... vars.last_seeded_at = datetime.now() ... db.commit()

validate_boolean_flags(key, value)

Ensure boolean flags are actually Boolean type.

Parameters:

Name Type Description Default
key

Column name being validated.

required
value

Proposed Boolean value.

required

Returns:

Name Type Description
bool

The validated Boolean value.

Raises:

Type Description
ValueError

If value is not a Boolean.

Example

vars.models_seeded = True # Valid vars.models_seeded = "yes" # Raises ValueError

Source code in backend/src/entities/StartupVariables.py
@validates("welcome_popup_has_already_displayed", "models_seeded", "offline_mode")
def validate_boolean_flags(self, key, value):
    """Ensure boolean flags are actually Boolean type.

    Args:
        key: Column name being validated.
        value: Proposed Boolean value.

    Returns:
        bool: The validated Boolean value.

    Raises:
        ValueError: If value is not a Boolean.

    Example:
        >>> vars.models_seeded = True  # Valid
        >>> vars.models_seeded = "yes"  # Raises ValueError
    """
    if not isinstance(value, bool):
        raise ValueError(f"{key} must be a Boolean, got {type(value)}")
    return value

src.entities.HardwareProfile

SQLAlchemy entity for backend-agnostic hardware profile.

Stores comprehensive hardware specifications and performance scores for all backends (MLX, CUDA, CPU). Uses discriminated fields with backend_type to determine which fields are populated. All backend-specific fields are nullable.

Architecture
  • Common fields: Populated for all backends (CPU, memory, scores)
  • MLX-specific: chip_model, mlx_gpu_cores, mps_available, neural_engine_tops
  • CUDA-specific: cuda_cores, cuda_version, compute_capability, vram_*
  • CPU-only: Minimal fields (no GPU/accelerator)
Example

from src.entities.HardwareProfile import HardwareProfile

MLX backend

hw_mlx = HardwareProfile( backend_type="mlx", cpu_model="Apple M3 Max", total_memory_gb=128.0, mlx_chip_model="M3 Max", mlx_gpu_cores=40, mps_available=True, unified_memory=True )

CUDA backend

hw_cuda = HardwareProfile( backend_type="cuda", cpu_model="Intel Xeon", total_memory_gb=64.0, cuda_cores=10752, cuda_version="12.1", vram_total_gb=24.0 )

HardwareProfile

Bases: Base

SQLAlchemy model for backend-agnostic hardware profile (singleton).

Comprehensive hardware specifications supporting MLX (Apple Silicon), CUDA (NVIDIA), and CPU backends. Backend-specific fields are nullable and populated based on backend_type discriminator.

Table Design
  • Singleton pattern: Only one row in database (id=1)
  • backend_type determines which optional fields are populated
  • Frontend checks backend_type to render appropriate UI

Common Attributes (All Backends): id: Primary key (singleton, always 1). backend_type: Backend discriminator ("mlx", "cuda", "cpu"). cpu_model: CPU model string. total_memory_gb: Total system memory in GB. available_memory_gb: Currently available memory in GB. disk_total_gb: Total disk space in GB. disk_available_gb: Available disk space in GB. global_inference_score: Inference performance score (0-100). global_inference_label: Inference label (Poor/Medium/Good/Very Good). cpu_score: CPU-specific score (0-100). memory_score: Memory-specific score (0-100). system_platform: OS platform (Darwin/Linux/Windows).

Common Accelerator Attributes (MLX & CUDA): gpu_name: Accelerator name (Apple GPU / NVIDIA GPU model). estimated_tflops: Estimated peak TFLOPS (FP32). memory_bandwidth_gbs: Memory bandwidth in GB/s. architecture: Architecture identifier (3nm/5nm for MLX, Ampere/Ada for CUDA). gpu_score: GPU/accelerator performance score (0-100).

MLX-Specific Attributes (Apple Silicon): mlx_chip_model: Apple chip model (M1/M2/M3/M4 + variant). mlx_gpu_cores: Number of GPU cores (8-76). mps_available: Metal Performance Shaders availability. neural_engine_tops: Neural Engine performance (TOPS). unified_memory: Unified memory architecture flag.

CUDA-Specific Attributes (NVIDIA): cuda_cores: Number of CUDA cores. cuda_version: CUDA runtime version (e.g., "12.1"). compute_capability: Compute capability (e.g., "8.6"). vram_total_gb: Total dedicated VRAM in GB. vram_available_gb: Available VRAM in GB.

Performance Tracking

cpu_performance_units: CPU performance metric (computed). performance_breakdown: Detailed JSON with component scores. created_at: Profile creation timestamp. updated_at: Last update timestamp.

Constraints
  • backend_type must be "mlx", "cuda", or "cpu"
  • Singleton enforced at application level (id=1)
Example

MLX profile

hw = HardwareProfile( ... backend_type="mlx", ... cpu_model="Apple M3 Max", ... mlx_chip_model="M3 Max", ... mlx_gpu_cores=40, ... unified_memory=True ... ) db.add(hw) db.commit()

src.entities.UserSettings

SQLAlchemy entity for global user settings.

Singleton table (one row) holding app-wide user preferences, mirroring the StartupVariables singleton pattern. First occupant: the global web-search toggle (issue #310) — the DEFAULT for new conversations. Each conversation copies this value at creation and owns its flag afterwards; changing the global setting never retro-affects existing conversations. It also carries the interface language, the automatic-update preference, the inference backend the app runs models on, and the other defaults new conversations copy (reasoning effort, compaction threshold).

Example

from src.entities.UserSettings import UserSettings

settings = UserSettings(web_search_enabled=False)

UserSettings

Bases: Base

SQLAlchemy model for the user-settings singleton.

Attributes:

Name Type Description
id

Primary key (singleton - only one row).

web_search_enabled

Boolean - global default for the web_search agent tool (#310). False by default: a web search egresses the user's query, so the local-first product keeps it strictly opt-in.

language

Interface language code (#385), one of SUPPORTED_LANGUAGES. "en" by default; the frontend derives the first value from the OS locale and persists it here so it survives restarts.

auto_update_enabled

Boolean - whether the Electron main process may check for, download and install a new version on its own. True by default, so an install that never touches the setting behaves as it always has; turning it off stops the update traffic entirely.

inference_backend

One of INFERENCE_BACKENDS. "auto" by default, so hardware detection decides; "cpu" makes the app run models on the CPU build even when an NVIDIA GPU is present. Read once per boot, after the migrations, so the choice needs an app restart to apply.

default_reasoning_effort

One of REASONING_EFFORT_LEVELS, the GLOBAL default new conversations copy at creation. "medium" by default: the level at which a model that reasons naturally behaves exactly as it did before the setting existed. Arena panels, which have no conversation row, read this value on every turn.

default_compaction_threshold

One of COMPACTION_THRESHOLDS (50-90, step 5), the GLOBAL share of the working window, in percent, at which new conversations compact; each copies it at creation. 80 by default, the value every row that predates the column holds. Arena panels never compact, so nothing else reads it.

Constraints
  • web_search_enabled must be a Boolean (enforced by validator).
  • auto_update_enabled must be a Boolean (enforced by validator).
  • language must be one of SUPPORTED_LANGUAGES (enforced by validator).
  • inference_backend must be one of INFERENCE_BACKENDS (validator).
  • default_reasoning_effort must be one of REASONING_EFFORT_LEVELS.
  • default_compaction_threshold must be one of COMPACTION_THRESHOLDS.

validate_boolean_flags(key, value)

Ensure boolean flags are actually Boolean type.

Parameters:

Name Type Description Default
key

Column name being validated.

required
value

Proposed Boolean value.

required

Returns:

Name Type Description
bool

The validated Boolean value.

Raises:

Type Description
ValueError

If value is not a Boolean.

Source code in backend/src/entities/UserSettings.py
@validates("web_search_enabled", "auto_update_enabled")
def validate_boolean_flags(self, key, value):
    """Ensure boolean flags are actually Boolean type.

    Args:
        key: Column name being validated.
        value: Proposed Boolean value.

    Returns:
        bool: The validated Boolean value.

    Raises:
        ValueError: If value is not a Boolean.
    """
    if not isinstance(value, bool):
        raise ValueError(f"{key} must be a Boolean, got {type(value)}")
    return value

validate_default_compaction_threshold(key, value)

Ensure the global compaction threshold is a whole percent on the scale.

Raises:

Type Description
ValueError

If value is not an int in COMPACTION_THRESHOLDS (a bool, a float or a string is rejected).

Source code in backend/src/entities/UserSettings.py
@validates("default_compaction_threshold")
def validate_default_compaction_threshold(self, key, value):
    """Ensure the global compaction threshold is a whole percent on the scale.

    Raises:
        ValueError: If value is not an int in COMPACTION_THRESHOLDS (a
            bool, a float or a string is rejected).
    """
    if not is_compaction_threshold(value):
        raise ValueError(f"{key} must be one of {COMPACTION_THRESHOLDS}, got {value!r}")
    return value

validate_default_reasoning_effort(key, value)

Ensure the global reasoning effort is one of the five levels.

Raises:

Type Description
ValueError

If value is not in REASONING_EFFORT_LEVELS.

Source code in backend/src/entities/UserSettings.py
@validates("default_reasoning_effort")
def validate_default_reasoning_effort(self, key, value):
    """Ensure the global reasoning effort is one of the five levels.

    Raises:
        ValueError: If value is not in REASONING_EFFORT_LEVELS.
    """
    if value not in REASONING_EFFORT_LEVELS:
        raise ValueError(f"{key} must be one of {REASONING_EFFORT_LEVELS}, got {value!r}")
    return value

validate_inference_backend(key, value)

Ensure the inference backend is one of the supported values.

Raises:

Type Description
ValueError

If value is not in INFERENCE_BACKENDS.

Source code in backend/src/entities/UserSettings.py
@validates("inference_backend")
def validate_inference_backend(self, key, value):
    """Ensure the inference backend is one of the supported values.

    Raises:
        ValueError: If value is not in INFERENCE_BACKENDS.
    """
    if value not in INFERENCE_BACKENDS:
        raise ValueError(f"{key} must be one of {INFERENCE_BACKENDS}, got {value!r}")
    return value

validate_language(key, value)

Ensure the language is one of the supported interface languages.

Raises:

Type Description
ValueError

If value is not in SUPPORTED_LANGUAGES.

Source code in backend/src/entities/UserSettings.py
@validates("language")
def validate_language(self, key, value):
    """Ensure the language is one of the supported interface languages.

    Raises:
        ValueError: If value is not in SUPPORTED_LANGUAGES.
    """
    if value not in SUPPORTED_LANGUAGES:
        raise ValueError(f"{key} must be one of {SUPPORTED_LANGUAGES}, got {value!r}")
    return value