Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
3c504da
chore: update deps
palaniappan-r Oct 20, 2025
346a6ce
feat: add session id to input object
palaniappan-r Oct 21, 2025
7b453ca
feat: implement database models and CRUD operations
palaniappan-r Oct 21, 2025
175fa14
feat: add conversations API with DB integration
palaniappan-r Oct 21, 2025
970f26e
feat: add api docs
palaniappan-r Oct 21, 2025
b2b9e22
chore: move api docs
palaniappan-r Oct 21, 2025
046e80d
feat: delete unused chains api
palaniappan-r Oct 21, 2025
f618d05
feat: add database configuration to .env.example
palaniappan-r Oct 21, 2025
eab4938
chore: update deps
palaniappan-r Oct 21, 2025
faca754
feat: update dockerfile
palaniappan-r Oct 24, 2025
0eb2c0c
feat: Add postgresql and pgadmin services
palaniappan-r Oct 24, 2025
4b75461
feat: add response models for conversations endpoints
palaniappan-r Oct 24, 2025
d3660f5
chore: update deps
palaniappan-r Oct 24, 2025
0e02868
feat: include conversations router
palaniappan-r Oct 24, 2025
30759dc
feat: initialize database module
palaniappan-r Oct 24, 2025
045383a
fix: Expose core components in package init
palaniappan-r Oct 30, 2025
108d70d
feat: load environment variables and configure CUDA settings
palaniappan-r Nov 2, 2025
adc9ea9
chore: update deps
palaniappan-r Nov 2, 2025
a4b7ca4
fix: update embeddings type
palaniappan-r Nov 2, 2025
7b8ba17
refactor: move CUDA environment variable setup to main entry point
palaniappan-r Nov 2, 2025
6e4b3bf
fix: update healthcheck command to include database name
palaniappan-r Nov 2, 2025
7b6db80
refactor: replace session_id with conversation_id in db models
palaniappan-r Nov 2, 2025
a8c5f8e
refactor: update type hints
palaniappan-r Nov 2, 2025
83537bc
refactor: formatting changes
palaniappan-r Nov 2, 2025
4e85fbd
fix: handle incomplete message pairs
palaniappan-r Nov 2, 2025
ebf88f8
fix: remove hardcoded PostgreSQL environment variables
palaniappan-r Nov 2, 2025
d635686
Merge branch 'master' into backend-db
palaniappan-r Nov 2, 2025
2e9d22a
feat: update psycopg2 dependency to psycopg2-binary
palaniappan-r Nov 2, 2025
fbb0307
fix: update import from graphs to conversations
palaniappan-r Nov 2, 2025
6dd183e
chore: update google-cloud-storage dependency
palaniappan-r Nov 2, 2025
e8cf6f2
fix: use retriever graph
palaniappan-r Nov 2, 2025
43ac935
fix: revert google-cloud-storage dependency
palaniappan-r Nov 2, 2025
d28d45e
fix: add return type hints
palaniappan-r Nov 2, 2025
8c8ef8e
fix: add return type hint to lifespan function
palaniappan-r Nov 2, 2025
f14f31f
docs: add docstrings for endpoints
palaniappan-r Nov 6, 2025
76c7bc0
fix: untrack data folder
palaniappan-r Nov 7, 2025
7e28524
docs: update README with PostgreSQL setup instructions
palaniappan-r Nov 7, 2025
8cb48bb
remove api_docs
luarss Nov 7, 2025
2551efa
remove unused response_models
luarss Nov 7, 2025
b4d4c74
db models:
luarss Nov 7, 2025
9438d9a
specific docker-compose commands
luarss Nov 7, 2025
115a603
improve history string robustness
luarss Nov 7, 2025
e7a3146
fix LLM response extraction (docker compose)
luarss Nov 7, 2025
989df79
add unit tests
luarss Nov 7, 2025
8f32f66
reduce docstring verbosity
luarss Nov 7, 2025
dcbbb7f
feat: save streamed conversation messages to the database
palaniappan-r Nov 12, 2025
123aa95
fix streaming for more messages
luarss Nov 15, 2025
6205242
add streaming unit tests
luarss Nov 15, 2025
5e6ff82
fix lint
luarss Nov 15, 2025
1107a9c
fix test
luarss Nov 15, 2025
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: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.env
*.ipynb
__pycache__/
backend/data/*
backend/data/
backend/src/*.json
*.pyc
*.egg-info/
Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ check:

.PHONY: docker-up
docker-up:
@docker compose up --build --wait
@docker compose -f docker-compose.yml up --build --wait

.PHONY: docker-down
docker-down:
@docker compose down --remove-orphans
@docker compose -f docker-compose.yml down --remove-orphans

.PHONY: docker-dev
docker-dev:
@docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build --wait

# --- Development Commands ---
.PHONY: seed-credentials
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,31 @@ This setup involves the setting of both the frontend and backend components. We

### Backend Setup

#### Database Schema

The database automatically creates the following tables:
- `conversations` - Stores conversation metadata (id, user_id, title, timestamps)
- `messages` - Stores individual messages within conversations

#### Setting Up PostgreSQL Database Variables

The backend uses PostgreSQL for conversation persistence. Configure these database variables in your `.env` file:

- `POSTGRES_USER` - Database username (default: `orassistant`)
- `POSTGRES_PASSWORD` - Database password (default: `password`)
- `POSTGRES_DB` - Database name (default: `orassistant_db`)
- `POSTGRES_HOST` - Database host (default: `postgres` for Docker, `localhost` for local)
- `POSTGRES_PORT` - Database port (default: `5432`)

**For local development without Docker:**
1. Install PostgreSQL on your system
2. Create a database: `createdb orassistant_db`
3. Set `POSTGRES_HOST=localhost` in your `.env` file

**For Docker deployment:**
- The database is automatically configured via docker-compose
- Data persists in a Docker volume named `postgres_data`

#### Option 1 - Docker

Ensure you have `docker` and `docker-compose` installed in your system.
Expand Down
9 changes: 8 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ GOOGLE_GEMINI=2.0_flash

LLM_TEMP=1

EMBEDDINGS_TYPE=GOOGLE_VERTEXAI
EMBEDDINGS_TYPE=GOOGLE_GENAI
GOOGLE_EMBEDDINGS=text-embedding-004
HF_EMBEDDINGS=thenlper/gte-large
HF_RERANKER=BAAI/bge-reranker-base
Expand Down Expand Up @@ -70,3 +70,10 @@ HEALTHCHECK_INTERVAL=30s
HEALTHCHECK_TIMEOUT=10s
HEALTHCHECK_RETRIES=5
HEALTHCHECK_START_PERIOD=1200s

# PostgreSQL Database Configuration
POSTGRES_USER=orassistant
POSTGRES_PASSWORD=password
POSTGRES_DB=orassistant_db
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
13 changes: 12 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim

WORKDIR /ORAssistant-backend

RUN apt-get update && apt-get install -y pandoc git wget curl git-lfs && git lfs install
RUN apt-get update && apt-get install -y \
build-essential \
curl \
gcc \
git \
git-lfs \
libpq-dev \
pandoc \
postgresql-client \
wget && \
git lfs install && \
rm -rf /var/lib/apt/lists/*

RUN pip install uv

Expand Down
14 changes: 11 additions & 3 deletions backend/Dockerfile_slim
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
WORKDIR /ORAssistant-backend

RUN apt-get update && apt-get install -y \
pandoc git wget curl \
git-lfs
build-essential \
curl \
gcc \
git \
git-lfs \
libpq-dev \
pandoc \
postgresql-client \
wget && \
git lfs install && \
rm -rf /var/lib/apt/lists/*

RUN git lfs install
RUN pip install uv

COPY ./pyproject.toml /ORAssistant-backend/pyproject.toml
Expand Down
4 changes: 2 additions & 2 deletions backend/chatbot.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import logging
from src.api.routers import graphs
from src.api.routers import conversations


def get_history_str(chat_history: list[dict[str, str]]) -> str:
Expand All @@ -13,7 +13,7 @@ def get_history_str(chat_history: list[dict[str, str]]) -> str:
chat_history: list[dict[str, str]] = []

if __name__ == "__main__":
rg = graphs.rg
rg = conversations.rg
os.system("clear")

while True:
Expand Down
7 changes: 5 additions & 2 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
import uvicorn
from dotenv import load_dotenv

from src.api.main import app

load_dotenv()

if os.getenv("USE_CUDA", "false").lower() != "true":
os.environ["CUDA_VISIBLE_DEVICES"] = ""

from src.api.main import app # noqa: E402


def main() -> None:
uvicorn.run(
Expand Down
14 changes: 13 additions & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"asyncpg>=0.30.0",
"faiss-cpu==1.12.0",
"fastapi==0.116.1",
"fastmcp>=2.12.2",
"google-cloud-storage>=2.19.0",
"httpx>=0.28.1",
"huggingface-hub[cli]==0.34.4",
"langchain==0.3.27",
"langchain-community==0.3.27",
Expand All @@ -19,17 +22,22 @@ dependencies = [
"langgraph==0.6.6",
"markdown==3.8.2",
"myst-parser==4.0.1",
"nest-asyncio>=1.6.0",
"nltk==3.9.1",
"openai==1.100.2",
"pypdf==6.1.3",
"psycopg2-binary>=2.9.11",
"pydantic>=2.11.7",
"pypdf==6.0.0",
"rank-bm25==0.2.2",
"rich>=13.7.0",
"sentence-transformers>=5.1.0",
"sphinx==8.1.3",
"sphinx-autobuild==2024.10.3",
"sphinx-book-theme==1.1.4",
"sphinx-copybutton==0.5.2",
"sphinx-external-toc==1.0.1",
"sphinxcontrib-mermaid==1.0.0",
"sqlalchemy>=2.0.43",
"unstructured==0.18.13",
]

Expand Down Expand Up @@ -58,6 +66,10 @@ markers = [
]
asyncio_mode = "auto"

[[tool.mypy.overrides]]
module = "nest_asyncio"
ignore_missing_imports = true

[tool.mypy]
exclude = [
"^tests/",
Expand Down
21 changes: 15 additions & 6 deletions backend/src/agents/retriever_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,21 @@ def classify(self, state: AgentState) -> dict[str, list[str]]:
return {"agent_type": [response.content]} # type: ignore

def fork_route(self, state: AgentState) -> str:
# TODO: if more than one agent add handler
if not self.enable_mcp:
tmp = "rag_agent"
else:
tmp = "mcp_agent"
return tmp
"""Route to the appropriate agent based on classification"""
agent_type = state.get("agent_type", [])

if not agent_type:
# Default to RAG agent if no classification
return "rag_agent"

# Return the classified agent type
classified = agent_type[0]

# If MCP is disabled but classifier chose MCP, fall back to RAG
if classified == "mcp_agent" and not self.enable_mcp:
return "rag_agent"

return classified

def initialize(self) -> None:
self.workflow = StateGraph(AgentState)
Expand Down
5 changes: 4 additions & 1 deletion backend/src/agents/retriever_rag.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,11 @@ def rag_route(self, state: AgentState) -> str:
def rag_generate(self, state: AgentState) -> dict[str, list[AnyMessage]]:
query = state["messages"][-1].content
context = state["context"][-1].content
chat_history = state.get("chat_history", "")

ans = self.llm_chain.invoke({"context": context, "question": query})
ans = self.llm_chain.invoke(
{"context": context, "question": query, "chat_history": chat_history}
)

if ans is not None:
return {"messages": [ans]}
Expand Down
25 changes: 20 additions & 5 deletions backend/src/api/main.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
from fastapi import FastAPI
from .routers import graphs, healthcheck, helpers, ui
from .routers import healthcheck, helpers, ui, conversations
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from src.database.config import init_database
import logging
from typing import AsyncGenerator

app = FastAPI()
logger = logging.getLogger(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
"""Initialize database on startup."""
logger.info("Initializing database connection...")
init_database()
yield
logger.info("Shutting down...")


app = FastAPI(lifespan=lifespan)

# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://localhost:8001", # mock endpoint
"http://localhost:8001",
"http://127.0.0.1:8001",
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
app.include_router(healthcheck.router)
app.include_router(graphs.router)
app.include_router(helpers.router)
app.include_router(ui.router)
app.include_router(conversations.router)
41 changes: 34 additions & 7 deletions backend/src/api/models/response_model.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from pydantic import BaseModel
from typing import Optional
from pydantic import BaseModel, ConfigDict
from datetime import datetime
from uuid import UUID


class UserInput(BaseModel):
query: str
chat_history: list[dict[str, str]] = []
list_sources: bool = False
list_context: bool = False
conversation_uuid: Optional[UUID] = None


class ContextSource(BaseModel):
Expand All @@ -28,8 +31,32 @@ class ChatResponse(BaseModel):
tools: list[str] = []


class ChatToolResponse(BaseModel):
response: str
sources: list[str] = []
context: list[str] = []
tools: list[str] = []
class MessageResponse(BaseModel):
uuid: UUID
conversation_uuid: UUID
role: str
content: str
context_sources: Optional[dict] = None
tools: Optional[list] = None
created_at: datetime

model_config = ConfigDict(from_attributes=True)


class ConversationResponse(BaseModel):
uuid: UUID
title: Optional[str] = None
created_at: datetime
updated_at: datetime
messages: list[MessageResponse] = []

model_config = ConfigDict(from_attributes=True)


class ConversationListResponse(BaseModel):
uuid: UUID
title: Optional[str] = None
created_at: datetime
updated_at: datetime

model_config = ConfigDict(from_attributes=True)
Loading