Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Started addressing and cleaning the backend to make it ready for futher extensions #4

Merged
merged 3 commits into from
Aug 4, 2024
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
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: FastAPI CI

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip'

- name: Install dependencies
run: |
cd backend
pip install -r requirements.txt

- name: Start FastAPI app
run: |
cd backend
nohup fastapi dev > /dev/null 2>&1 &
echo $! > uvicorn.pid
sleep 10

- name: Check FastAPI /health endpoint
run: |
response=$(curl -s http://127.0.0.1:8000/health)
echo "Response: $response"
if [ "$response" == '{"status":"Active"}' ]; then
echo "Health check passed"
else
echo "Health check failed"
exit 1
fi
Empty file added backend/api/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions backend/api/health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

from fastapi import APIRouter

health_router = APIRouter()


@health_router.get("/health")
async def check_health():
return {"status": "Active"}
31 changes: 31 additions & 0 deletions backend/api/query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from fastapi import APIRouter
from fastapi.responses import StreamingResponse

from backend.rag_llms_langchain import chain
from backend.embeddings.ingest import get_vectorstore

import json
import uuid


query_router = APIRouter()


@query_router.get("/query")
async def query(query: str):
# if current_user.role < 5:
# raise HTTPException(status_code=403, detail="Only admin users can delete other users")
store = get_vectorstore()
docs = store.invoke(query)

print(20*"*", "docs", 20*"*", "\n", docs)

async def stream_generator():
# Use the LangChain model to generate text
print(20*'*', "\n", query)
async for text in chain.astream({"input": query, "context": docs}):
yield json.dumps({"event_id": str(uuid.uuid4()), "data": text})

# TODO here we have to add the metadata/source

return StreamingResponse(stream_generator(), media_type="application/x-ndjson")
26 changes: 26 additions & 0 deletions backend/api/token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

from fastapi import APIRouter, Depends, HTTPException
from backend.pydantic_models import Token
from backend.oauth import authenticate_user, create_access_token
from backend.config import ACCESS_TOKEN_EXPIRE_MINUTES
from fastapi.security import OAuth2PasswordRequestForm
from datetime import timedelta


token_router = APIRouter()


@token_router.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=401,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
73 changes: 73 additions & 0 deletions backend/api/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

from fastapi import APIRouter, Depends, HTTPException
from backend.sqlalchemy_models import User
from backend.sessions import session
from backend.oauth import encrypt_password, get_current_user


user_router = APIRouter()



@user_router.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
return {"username": current_user.username, "role": current_user.role}


@user_router.get("/users/")
async def read_users(current_user: User = Depends(get_current_user)):
if current_user.role < 5:
raise HTTPException(
status_code=403, detail="Only admin users can view all users")
return [{"username": user.username, "role": user.role} for user in session.query(User).all()]


@user_router.get("/users/{user_id}")
async def read_user(user_id: int, current_user: User = Depends(get_current_user)):
if current_user.id != user_id and current_user.role < 5:
raise HTTPException(
status_code=403, detail="Only admin users can view other users")
user = session.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return {"username": user.username, "role": user.role}


@user_router.post("/users/")
async def create_user(username: str, password: str, role: int, current_user: User = Depends(get_current_user)):
if current_user.role < 5:
raise HTTPException(
status_code=403, detail="Only admin users can create new users")
user = User(username=username,
password_hash=encrypt_password(password), role=role)
session.add(user)
session.commit()
return {"username": user.username, "role": user.role}


@user_router.put("/users/{user_id}")
async def update_user(user_id: int, username: str, password: str, role: int, current_user: User = Depends(get_current_user)):
if current_user.id != user_id and current_user.role < 5:
raise HTTPException(
status_code=403, detail="Only admin users can update other users")
user = session.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
user.username = username
user.password = password
user.role = role
session.commit()
return {"username": user.username, "role": user.role}


@user_router.delete("/users/{user_id}")
async def delete_user(user_id: int, current_user: User = Depends(get_current_user)):
if current_user.id != user_id and current_user.role < 5:
raise HTTPException(
status_code=403, detail="Only admin users can delete other users")
user = session.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
session.delete(user)
session.commit()
return {"message": "User deleted"}
13 changes: 13 additions & 0 deletions backend/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# No origins are limited for now
# All origins are allowed for now and this
# must be changed
origins = [
"http://localhost",
"http://127.0.0.1:8000",
]


Empty file added backend/embeddings/__init__.py
Empty file.
File renamed without changes.
Loading
Loading