Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/quota/quota_exceed_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Any exception that can occur when user does not have enough tokens available."""

# pylint: disable=line-too-long)


class QuotaExceedError(Exception):
"""Any exception that can occur when user does not have enough tokens available."""

def __init__(
self, subject_id: str, subject_type: str, available: int, needed: int = 0
) -> None:
"""Construct exception object."""
message: str = ""

if needed == 0 and available <= 0:
match subject_type:
case "u":
message = f"User {subject_id} has no available tokens"
case "c":
message = "Cluster has no available tokens"
case _:
message = f"Unknown subject {subject_id} has no available tokens"
else:
match subject_type:
case "u":
message = f"User {subject_id} has {available} tokens, but {needed} tokens are needed" # noqa: E501
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix line length violations flagged by pipeline.

Lines 24 and 28 exceed the 100-character limit despite having # noqa: E501 comments, indicating the linter is still flagging them.

Apply this diff to resolve the violations:

-                    message = f"User {subject_id} has {available} tokens, but {needed} tokens are needed"  # noqa: E501
+                    message = (
+                        f"User {subject_id} has {available} tokens, "
+                        f"but {needed} tokens are needed"
+                    )
-                    message = f"Unknown subject {subject_id} has {available} tokens, but {needed} tokens are needed"  # noqa: E501
+                    message = (
+                        f"Unknown subject {subject_id} has {available} tokens, "
+                        f"but {needed} tokens are needed"
+                    )

Also applies to: 28-28

🧰 Tools
🪛 GitHub Actions: Python linter

[error] 24-24: Pylint: Line too long (119/100).

🤖 Prompt for AI Agents
In src/quota/quota_exceed_error.py around lines 24 and 28, the long f-strings
exceed the 100-character limit and the trailing "# noqa: E501" is still being
flagged; shorten those log/message lines by breaking the message into smaller
parts or using intermediate variables so each physical line is under 100 chars,
remove the "# noqa: E501" comments, and rebuild the full message via
concatenation or format across multiple lines to preserve the same runtime text.

case "c":
message = f"Cluster has {available} tokens, but {needed} tokens are needed"
case _:
message = f"Unknown subject {subject_id} has {available} tokens, but {needed} tokens are needed" # noqa: E501

# call the base class constructor with the parameters it needs
super().__init__(message)

# custom attributes
self.subject_id = subject_id
self.available = available
self.needed = needed
49 changes: 49 additions & 0 deletions src/quota/quota_limiter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Abstract class that is parent for all quota limiter implementations."""

import logging
from abc import ABC, abstractmethod


logger = logging.getLogger(__name__)


class QuotaLimiter(ABC):
"""Abstract class that is parent for all quota limiter implementations."""

@abstractmethod
def available_quota(self, subject_id: str) -> int:
"""Retrieve available quota for given user."""

@abstractmethod
def revoke_quota(self) -> None:
"""Revoke quota for given user."""
Comment on lines +18 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Align docstrings with method signatures.

Both revoke_quota and increase_quota docstrings mention "for given user" but neither method accepts a user/subject parameter. Either add the parameter or update the docstrings to match the actual signatures.

If these methods should operate on a specific subject, apply this diff:

 @abstractmethod
-def revoke_quota(self) -> None:
-    """Revoke quota for given user."""
+def revoke_quota(self, subject_id: str = "") -> None:
+    """Revoke quota for given subject."""
 
 @abstractmethod
-def increase_quota(self) -> None:
-    """Increase quota for given user."""
+def increase_quota(self, subject_id: str = "") -> None:
+    """Increase quota for given subject."""

Otherwise, update docstrings to remove the subject reference:

-    """Revoke quota for given user."""
+    """Revoke quota."""
-    """Increase quota for given user."""
+    """Increase quota."""

Also applies to: 22-23

🤖 Prompt for AI Agents
In src/quota/quota_limiter.py around lines 18-19 and 22-23, the docstrings for
revoke_quota and increase_quota incorrectly say "for given user" while the
method signatures take no user/subject; update both docstrings to remove the
reference to a user and describe the actual behavior (e.g., "Revoke quota." and
"Increase quota." or a brief phrase describing the scope), or alternatively if
the methods should accept a subject, add the subject parameter to the signatures
and update call sites and docstrings accordingly—pick one approach and make the
code and docstrings consistent.


@abstractmethod
def increase_quota(self) -> None:
"""Increase quota for given user."""

@abstractmethod
def ensure_available_quota(self, subject_id: str = "") -> None:
"""Ensure that there's available quota left."""

@abstractmethod
def consume_tokens(
self, input_tokens: int, output_tokens: int, subject_id: str = ""
) -> None:
"""Consume tokens by given user."""

@abstractmethod
def __init__(self) -> None:
"""Initialize connection config."""

@abstractmethod
def _initialize_tables(self) -> None:
"""Initialize tables and indexes."""

# pylint: disable=W0201
def connect(self) -> None:
"""Initialize connection to database."""

def connected(self) -> bool:
"""Check if connection to cache is alive."""
return True
32 changes: 32 additions & 0 deletions src/quota/quota_limiter_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Quota limiter factory class."""

import logging

from models.config import QuotaHandlersConfiguration

from quota.quota_limiter import QuotaLimiter

logger = logging.getLogger(__name__)


# pylint: disable=too-few-public-methods


class QuotaLimiterFactory:
"""Quota limiter factory class."""

@staticmethod
def quota_limiters(config: QuotaHandlersConfiguration) -> list[QuotaLimiter]:
"""Create instances of quota limiters based on loaded configuration.

Returns:
List of instances of 'QuotaLimiter',
"""
limiters: list[QuotaLimiter] = []

limiters_config = config.limiters
if limiters_config is None:
logger.warning("Quota limiters are not specified in configuration")
return limiters

return limiters
Loading