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
16 changes: 16 additions & 0 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Linting

on:
pull_request:

jobs:
ruff:
name: Ruff Linting
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Ruff
uses: astral-sh/ruff-action@v1
with:
args: check --output-format=github
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
venv
.venv
.env
__pycache__/
*.pyc
Expand All @@ -10,4 +11,8 @@ chroma_db/
ml/mlruns
ml/mlflow.db
mlflow.db
mlartifacts
mlartifacts

.pytest_cache/
.mypy_cache/
.ruff_cache/
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
3 changes: 2 additions & 1 deletion app/RAG/ingest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os

from dotenv import load_dotenv
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.Doduments import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter

load_dotenv()
HF_TOKEN = os.getenv("HF_TOKEN")
Expand Down
8 changes: 4 additions & 4 deletions app/RAG/query.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import os

from dotenv import load_dotenv
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_huggingface import HuggingFaceEmbeddings

load_dotenv()
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
Expand Down
19 changes: 9 additions & 10 deletions app/api/routers/auth.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
from fastapi import Depends, HTTPException, status, Request
from datetime import timedelta

from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session

from ...authentication.auth import (
get_password_hash,
authenticate_user,
create_access_token,
get_current_user,
get_password_hash,
)
from ...schemas.user_schema import UserSchema, UserCreate
from ...models.user_model import User
from ...db.database import get_db
from fastapi.security import OAuth2PasswordRequestForm
from datetime import timedelta
from fastapi import Response
from fastapi import APIRouter
from ...config import settings

from ...db.database import get_db
from ...models.user_model import User
from ...schemas.user_schema import UserCreate, UserSchema

router = APIRouter(prefix="/api/v1/auth", tags=["Authentication routes"])

Expand Down
13 changes: 7 additions & 6 deletions app/api/routers/rag.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import os
import time
import mlflow
from pathlib import Path
import json

import mlflow
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session

from app.authentication.auth import get_current_user
from ...schemas.user_schema import UserSchema, QueryRequest, QueryResponse
from ml.cluster_model import ClusterModel

from ...db.database import get_db
from sqlalchemy.orm import Session
from ...models.user_model import Query
from ml.cluster_model import ClusterModel
from ...schemas.user_schema import QueryRequest, QueryResponse, UserSchema

router = APIRouter(prefix="/api/v1/rag", tags=["RAG Routes"])

Expand All @@ -33,7 +35,6 @@ async def get_rag_answer(
raise HTTPException(status_code=503, detail="RAG Assistant is still loading.")

with mlflow.start_run(run_name="rag_query"):

mlflow.log_param("user_id", current_user.id)
mlflow.log_param("rag_version", "v1")

Expand Down
25 changes: 13 additions & 12 deletions app/authentication/auth.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from datetime import UTC, datetime, timedelta

import jwt
from fastapi import Depends, HTTPException, status, Request
from fastapi import Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordBearer
from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext
from ..models.user_model import User
from ..schemas.user_schema import TokenData
from ..db import database
from sqlalchemy.orm import Session
from datetime import timedelta, datetime, timezone
from typing import Optional
from jwt.exceptions import InvalidTokenError

from ..config import settings
from ..db import database
from ..models.user_model import User
from ..schemas.user_schema import TokenData

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_schema = OAuth2PasswordBearer(tokenUrl="token")
Expand All @@ -31,12 +32,12 @@ def authenticate_user(db: Session, username: str, password: str):
return user


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
def create_access_token(data: dict, expires_delta: timedelta | None = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
expire = datetime.now(UTC) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=20)
expire = datetime.now(UTC) + timedelta(minutes=20)

to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(
Expand Down Expand Up @@ -64,8 +65,8 @@ def get_current_user(
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except InvalidTokenError:
raise credentials_exception
except InvalidTokenError as err:
raise credentials_exception from err

user = db.query(User).filter(User.username == token_data.username).first()
if user is None:
Expand Down
3 changes: 1 addition & 2 deletions app/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from pydantic import ConfigDict, Field
from pydantic_settings import BaseSettings
from pydantic import Field
from pydantic import ConfigDict


class Settings(BaseSettings):
Expand Down
3 changes: 1 addition & 2 deletions app/db/database.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import os
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from ..config import settings

from ..config import settings

SQLALCHEMY_DATABASE_URL = f"postgresql://{settings.DATABASE_USER}:{settings.DATABASE_PASSWORD}@{settings.DATABASE_HOST}:{settings.DATABASE_PORT}/{settings.DATABASE_NAME}"

Expand Down
21 changes: 12 additions & 9 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
from contextlib import asynccontextmanager

import mlflow
from fastapi import FastAPI
from .db.database import engine, Base
from fastapi.middleware.cors import CORSMiddleware

from .api.routers import auth, rag
from .config import settings
import mlflow
from .db.database import Base, engine
from .RAG.query import ITSmartAssistant
from contextlib import asynccontextmanager


@asynccontextmanager
async def lifespan(app: FastAPI):
print("Creating database tables...")
Base.metadata.create_all(bind=engine)
print("Connecting to MLflow...")
mlflow.set_tracking_uri(settings.MLFLOW_TRACKING_URI)
mlflow.set_experiment("rag-queries")
print("Loading RAG Model weights...")
app.state.rag_assistant = ITSmartAssistant()

yield
print("Shutting down...")

Expand All @@ -20,7 +28,7 @@ async def lifespan(app: FastAPI):
title="It Support RAG API",
lifespan=lifespan,
description=(
"This API provides endpoints for users to authenticate and as questions about it support"
"This API provides endpoints for users to authenticate and ask questions about IT support"
),
)

Expand All @@ -33,11 +41,6 @@ async def lifespan(app: FastAPI):
allow_headers=["*"],
)

Base.metadata.create_all(bind=engine)

mlflow.set_tracking_uri(settings.MLFLOW_TRACKING_URI)
mlflow.set_experiment("rag-queries")

app.include_router(auth.router)
app.include_router(rag.router)

Expand Down
3 changes: 2 additions & 1 deletion app/models/user_model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from sqlalchemy import Column, Integer, String, Boolean, Float
from sqlalchemy import Boolean, Column, Float, Integer, String
from sqlalchemy.sql import func
from sqlalchemy.types import DateTime

from ..db.database import Base


Expand Down
1 change: 1 addition & 0 deletions app/schemas/user_schema.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime

from pydantic import BaseModel, ConfigDict


Expand Down
Loading