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
2 changes: 2 additions & 0 deletions lightspeed-stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ user_data_collection:
feedback_storage: "/tmp/data/feedback"
transcripts_disabled: false
transcripts_storage: "/tmp/data/transcripts"
authentication:
module: "noop"
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ license = {file = "LICENSE"}
dependencies = [
"fastapi>=0.115.6",
"uvicorn>=0.34.3",
"kubernetes>=30.1.0",
"llama-stack>=0.2.13",
"rich>=14.0.0",
"expiringdict>=1.2.2",
"cachetools>=6.1.0",
]

[tool.pyright]
exclude = [
# TODO(lucasagomes): This module was copied from road-core
# service/ols/src/auth/k8s.py and currently has 58 Pyright issues. It
# might need to be rewritten down the line.
"src/auth/k8s.py",
]

[tool.pdm]
distribution = true

Expand Down
3 changes: 2 additions & 1 deletion src/app/endpoints/feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@

from fastapi import APIRouter, Request, HTTPException, Depends, status

from auth import get_auth_dependency
from configuration import configuration
from models.responses import FeedbackResponse, StatusResponse
from models.requests import FeedbackRequest
from utils.suid import get_suid
from utils.auth import auth_dependency
from utils.common import retrieve_user_id

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/feedback", tags=["feedback"])
auth_dependency = get_auth_dependency()

# Response for the feedback endpoint
feedback_response: dict[int | str, dict[str, Any]] = {
Expand Down
3 changes: 2 additions & 1 deletion src/app/endpoints/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@
from models.responses import QueryResponse
from models.requests import QueryRequest, Attachment
import constants
from utils.auth import auth_dependency
from auth import get_auth_dependency
from utils.common import retrieve_user_id
from utils.endpoints import check_configuration_loaded
from utils.suid import get_suid

logger = logging.getLogger("app.endpoints.handlers")
router = APIRouter(tags=["query"])
auth_dependency = get_auth_dependency()

# Global agent registry to persist agents across requests
_agent_cache: TTLCache[str, Agent] = TTLCache(maxsize=1000, ttl=3600)
Expand Down
3 changes: 2 additions & 1 deletion src/app/endpoints/streaming_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
from fastapi import APIRouter, HTTPException, Request, Depends, status
from fastapi.responses import StreamingResponse

from auth import get_auth_dependency
from client import get_async_llama_stack_client
from configuration import configuration
from models.requests import QueryRequest
import constants
from utils.auth import auth_dependency
from utils.endpoints import check_configuration_loaded
from utils.common import retrieve_user_id
from utils.suid import get_suid
Expand All @@ -34,6 +34,7 @@

logger = logging.getLogger("app.endpoints.handlers")
router = APIRouter(tags=["streaming_query"])
auth_dependency = get_auth_dependency()

# Global agent registry to persist agents across requests
_agent_cache: TTLCache[str, AsyncAgent] = TTLCache(maxsize=1000, ttl=3600)
Expand Down
38 changes: 38 additions & 0 deletions src/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""This package contains authentication code and modules."""

import logging

from auth.interface import AuthInterface
from auth import noop, noop_with_token, k8s
from configuration import configuration
import constants


logger = logging.getLogger(__name__)


def get_auth_dependency(
virtual_path: str = constants.DEFAULT_VIRTUAL_PATH,
) -> AuthInterface:
"""Select the configured authentication dependency interface."""
module = configuration.authentication_configuration.module # pyright: ignore

logger.debug(
"Initializing authentication dependency: module='%s', virtual_path='%s'",
module,
virtual_path,
)

match module:
case constants.AUTH_MOD_NOOP:
return noop.NoopAuthDependency(virtual_path=virtual_path)
case constants.AUTH_MOD_NOOP_WITH_TOKEN:
return noop_with_token.NoopWithTokenAuthDependency(
virtual_path=virtual_path
)
case constants.AUTH_MOD_K8S:
return k8s.K8SAuthDependency(virtual_path=virtual_path)
case _:
err_msg = f"Unsupported authentication module '{module}'"
logger.error(err_msg)
raise ValueError(err_msg)
13 changes: 13 additions & 0 deletions src/auth/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Abstract base class for authentication methods."""

from abc import ABC, abstractmethod

from fastapi import Request


class AuthInterface(ABC): # pylint: disable=too-few-public-methods
"""Base class for all authentication method implementations."""

@abstractmethod
async def __call__(self, request: Request) -> tuple[str, str, str]:
"""Validate FastAPI Requests for authentication and authorization."""
Loading