Skip to content

Commit a518187

Browse files
authored
Merge pull request #4 from OthmaneBlial/working004
2 parents edc4880 + 54cba96 commit a518187

31 files changed

Lines changed: 1269 additions & 3131 deletions

.dockerignore

Lines changed: 10 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,15 @@
1-
# Git
21
.git
32
.gitignore
4-
5-
# Python
6-
__pycache__
7-
*.pyc
8-
*.pyo
9-
*.pyd
10-
.Python
11-
env
3+
__pycache__/
4+
*.py[cod]
5+
*.so
126
venv
13-
.venv
14-
pip-log.txt
15-
pip-delete-this-directory.txt
16-
.tox
17-
.coverage
18-
.coverage.*
19-
.cache
20-
nosetests.xml
7+
htmlcov
218
coverage.xml
22-
*.cover
23-
*.log
24-
.git
25-
.mypy_cache
26-
.pytest_cache
27-
.hypothesis
28-
29-
# Virtual environments
30-
.env
31-
.venv
32-
env/
33-
venv/
34-
ENV/
35-
env.bak/
36-
venv.bak/
37-
38-
# IDE
39-
.vscode/
40-
.idea/
41-
*.swp
42-
*.swo
43-
*~
44-
45-
# OS
46-
.DS_Store
47-
.DS_Store?
48-
._*
49-
.Spotlight-V100
50-
.Trashes
51-
ehthumbs.db
52-
Thumbs.db
53-
54-
# Node.js (frontend)
55-
node_modules/
56-
npm-debug.log*
57-
yarn-debug.log*
58-
yarn-error.log*
59-
.npm
60-
.yarn-integrity
61-
62-
# Build artifacts
63-
dist/
64-
build/
65-
*.egg-info/
66-
67-
# Documentation build
68-
docs/_build/
69-
70-
# Temporary files
71-
*.tmp
72-
*.temp
73-
74-
# Logs
75-
*.log
76-
77-
# Docker
78-
Dockerfile*
79-
docker-compose*.yml
80-
.dockerignore
81-
82-
# CI/CD
83-
.github/
84-
85-
# Test artifacts
86-
.coverage
87-
htmlcov/
88-
.tox/
89-
.nox/
90-
91-
# MyPy
92-
.mypy_cache/
93-
94-
# Jupyter Notebook
95-
.ipynb_checkpoints
96-
97-
# pyenv
98-
.python-version
99-
100-
# pipenv
101-
Pipfile.lock
102-
103-
# PEP 582
104-
__pypackages__/
105-
106-
# Celery stuff
107-
celerybeat-schedule
108-
celerybeat.pid
109-
110-
# SageMath parsed files
111-
*.sage.py
112-
113-
# Environments
9+
dist
10+
build
11+
*.egg-info
12+
Dockerfile.ci
13+
frontend/node_modules
14+
*.sqlite*
11415
.env
115-
.venv
116-
env
117-
venv
118-
ENV
119-
env.bak
120-
venv.bak
121-
122-
# Spyder project settings
123-
.spyderproject
124-
.spyproject
125-
126-
# Rope project settings
127-
.ropeproject
128-
129-
# mkdocs documentation
130-
/site
131-
132-
# mypy
133-
.mypy_cache/
134-
.dmypy.json
135-
dmypy.json
136-
137-
# Pyre type checker
138-
.pyre/
139-
140-
# Frontend build artifacts
141-
frontend/node_modules/
142-
frontend/dist/
143-
frontend/.next/
144-
frontend/out/
145-
146-
# Database files
147-
*.db
148-
*.sqlite
149-
*.sqlite3
150-
151-
# Alembic
152-
alembic/versions/*.pyc
153-
154-
# Linter script
155-
linter.sh
156-
157-
# Startup script
158-
startup.sh
159-
160-
# Reset DB script
161-
reset_db.py

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,5 @@ bin/
158158
*.temp
159159

160160

161+
coverage.xml
162+
dist/

Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# syntax=docker/dockerfile:1.6
2+
3+
ARG BASE_IMAGE=ragify-backend-base:latest
4+
FROM ${BASE_IMAGE} AS runtime
5+
6+
USER root
7+
WORKDIR /app
8+
9+
# Copy application source code
10+
COPY pyproject.toml README.md ./
11+
COPY backend ./backend
12+
COPY shared ./shared
13+
COPY frontend ./frontend
14+
15+
# Install the project using existing dependencies from the base image
16+
RUN pip install --no-cache-dir --no-deps .
17+
18+
RUN chown -R ragify:ragify /app
19+
USER ragify
20+
21+
EXPOSE 8000
22+
23+
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]

README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## ✨ Key Features
66

7-
- **🎯 Accurate Answers**: Never hallucinates - responses are grounded in your uploaded documents
7+
- **🎯 Grounded Answers**: Uses retrieved document context, surfaces document sources, and clearly says it cannot answer when evidence is missing
88
- **📚 Multiple Knowledge Bases**: Organize documents by topic, department, or project
99
- **🤖 100+ AI Models**: Access OpenAI, Anthropic, Google, and more through OpenRouter
1010
- **⚡ Real-time Streaming**: Get responses as they're generated
@@ -61,6 +61,32 @@ uvicorn backend.main:app --reload --host 0.0.0.0 --port 8000
6161
# Frontend: http://localhost:5173 (after cd frontend && npm run dev)
6262
```
6363

64+
## 🐳 Docker Quick Start
65+
66+
Prefer containers? The repository now ships with a full Docker setup for the API, frontend, PostgreSQL (with pgvector), and Redis.
67+
68+
```bash
69+
# Build (or rebuild) the base dependency image and start everything
70+
./startupdocker.sh --build
71+
72+
# Stream logs if you want to watch startup
73+
./startupdocker.sh --build --logs
74+
75+
# Stop the stack when you're done
76+
./startupdocker.sh --down
77+
```
78+
79+
Host ports for the Docker stack:
80+
81+
- Backend API: `http://localhost:18000`
82+
- Frontend UI: `http://localhost:15173`
83+
- PostgreSQL: `localhost:15432` (user/password: `ragify` / `RagifyStrongPass2023`)
84+
- Redis: `localhost:16379`
85+
86+
The script automatically maintains a `ragify-backend-base` image that contains all heavy Python dependencies (Torch, transformers, etc.). The first `--build` run prepares that base; subsequent builds reuse it so only your application code is rebuilt unless `requirements-docker.txt` changes.
87+
88+
Environment variables are loaded from the project’s `.env` file, so edit that file if you need to change keys, database credentials, or model configuration before running `./startupdocker.sh`.
89+
6490
## 🎯 Why RAGify?
6591

6692
**Traditional AI chatbots often hallucinate** - they make up information that sounds plausible but isn't accurate. RAGify eliminates this by:
@@ -110,4 +136,3 @@ black . && isort .
110136
# Start development server
111137
uvicorn backend.main:app --reload
112138
```
113-

backend/api/v1/endpoints/chat.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
get_application_knowledge_bases,
1313
create_chat_message,
1414
get_application_chat_history,
15+
clear_chat_history,
1516
)
1617
from backend.modules.rag.rag_pipeline import streaming_rag_pipeline, RAGQuery
1718

@@ -295,9 +296,12 @@ async def clear_conversation_history(
295296
if not application_data:
296297
raise HTTPException(status_code=404, detail="Application not found")
297298

298-
# In a real implementation, you'd delete chat messages
299-
# For now, just return success
300-
return {"message": "Conversation history cleared successfully"}
299+
deleted_count = await clear_chat_history(db, application_id)
300+
301+
return {
302+
"message": "Conversation history cleared successfully",
303+
"deleted_count": deleted_count,
304+
}
301305

302306
except HTTPException:
303307
raise

backend/core/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class Settings(BaseSettings):
1919
# Vector
2020
vector_dimension: int = 384 # Must match SentenceTransformer dimension
2121

22+
# Caching / message bus
23+
redis_url: Optional[str] = None
24+
2225
# Model Provider API Keys
2326
openai_api_key: Optional[str] = None
2427
anthropic_api_key: Optional[str] = None

backend/core/security.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,43 @@
1-
from passlib.context import CryptContext
2-
from jose import JWTError, jwt
31
from datetime import datetime, timedelta, timezone
42
from typing import Optional
3+
4+
from jose import JWTError, jwt
5+
from passlib.context import CryptContext
6+
from passlib.handlers import bcrypt as passlib_bcrypt
7+
58
from .config import settings
69

10+
11+
def _patch_passlib_bcrypt_wrap_detection() -> None:
12+
"""Work around passlib+bcrypt incompatibility seen on CI runners."""
13+
14+
patch_flag = "_ragify_wrap_detection_patched"
15+
if getattr(passlib_bcrypt, patch_flag, False):
16+
return
17+
18+
original_detect = getattr(passlib_bcrypt, "detect_wrap_bug", None)
19+
if original_detect is None:
20+
setattr(passlib_bcrypt, patch_flag, True)
21+
return
22+
23+
def detect_wrap_bug_safe(ident: bytes) -> bool: # type: ignore[override]
24+
try:
25+
return original_detect(ident)
26+
except ValueError as exc:
27+
message = str(exc).lower()
28+
if "password cannot be longer than 72 bytes" in message:
29+
# Newer versions of the bcrypt backend raise instead of truncating
30+
# long passwords during passlib's safety probe. Treat this as
31+
# "no wrap bug" so hashing can proceed normally.
32+
return False
33+
raise
34+
35+
passlib_bcrypt.detect_wrap_bug = detect_wrap_bug_safe # type: ignore[assignment]
36+
setattr(passlib_bcrypt, patch_flag, True)
37+
38+
39+
_patch_passlib_bcrypt_wrap_detection()
40+
741
if settings.secret_key == "your-secret-key-here":
842
raise RuntimeError("SECRET_KEY must be set")
943

backend/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ async def lifespan(app: FastAPI):
8686
"http://127.0.0.1:8501",
8787
"http://localhost:5173",
8888
"http://127.0.0.1:5173",
89+
"http://localhost:15173",
90+
"http://127.0.0.1:15173",
8991
], # Include new frontend port
9092
allow_credentials=True,
9193
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],

backend/modules/applications/crud.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ async def update_application_model_config(
320320
Returns:
321321
Updated application or None
322322
"""
323-
return await update_application(db, application_id, model_config=model_config)
323+
return await update_application(db, application_id, config=model_config)
324324

325325

326326
async def update_application_knowledge_bases(
@@ -411,3 +411,12 @@ async def get_application_chat_history(
411411
}
412412
for msg in messages
413413
]
414+
415+
416+
async def clear_chat_history(db: AsyncSession, application_id: UUID) -> int:
417+
"""Delete all chat messages for an application and return removed count."""
418+
result = await db.execute(
419+
delete(ChatMessage).where(ChatMessage.application_id == application_id)
420+
)
421+
await db.commit()
422+
return result.rowcount or 0

0 commit comments

Comments
 (0)