Skip to content
Open
4 changes: 2 additions & 2 deletions backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ class ApiKey(Base):
class WorkspaceInvitation(Base):
__tablename__ = "workspace_invitations"

id = Column(String, primary_key=True, default=generate_uuid)
id = Column(GUID, primary_key=True, default=uuid.uuid4)
email = Column(String(120), nullable=False, index=True)
token_hash = Column(String(255), nullable=False, unique=True, index=True)
inviter_id = Column(
String,
GUID,
ForeignKey("users.id"),
nullable=False,
index=True,
Expand Down
13 changes: 7 additions & 6 deletions backend/app/services/layout_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@

import fitz # PyMuPDF
import pymupdf4llm
from google import (
genai, # Since the repo uses Gemini, we'll swap to Gemini 2.5 Flash for vision tasks!
)

# Initialize Gemini Client
client = genai.Client()


class AdvancedPDFParser:
Expand Down Expand Up @@ -49,6 +43,13 @@ def process_embedded_images(self, page_num: int, page_obj: fitz.Page) -> List[st
image_descriptions = []
image_list = page_obj.get_images(full=True)

try:
from google import genai
client = genai.Client()
except Exception as e:
print(f"Gemini client init failed, skipping vision: {e}")
return image_descriptions

for img_index, img in enumerate(image_list):
xref = img[0]
base_image = self.doc.extract_image(xref)
Expand Down
4 changes: 2 additions & 2 deletions backend/app/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ def process_document(
pass

# 4. Mark document pipeline processing as completely successful
doc.status = "completed"
doc.status = "ready"
doc.processing_progress = 100
db.commit()

return {"document_id": document_id, "status": "completed"}
return {"document_id": document_id, "status": "ready"}

except Exception as exc:
logger.error("Document %s processing failed (attempt %s): %s", document_id, self.request.retries + 1, exc)
Expand Down
3 changes: 3 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ httpx

# Document Processing
PyMuPDF
pymupdf4llm
google-generativeai
google-genai
pdfplumber
python-docx
unstructured[pdf]
Expand Down
12 changes: 8 additions & 4 deletions backend/tests/test_celery_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,22 @@ def test_process_document_ingestion_pipeline(db_session):
mock_session_factory.return_value.__enter__.return_value = db_session
mock_session_factory.return_value = db_session

# Patch the factory globally, and patch ingest_document right where app.tasks calls it
# Patch the factory globally, and patch AdvancedPDFParser constructor and ingest_document
# right where app.tasks calls it
with patch("app.database.SessionLocal", mock_session_factory, create=True), \
patch("app.services.document_ingestion.SessionLocal", mock_session_factory, create=True), \
patch("app.tasks.ingest_document") as mock_ingest:

patch("app.tasks.AdvancedPDFParser.__init__", return_value=None) as mock_init, \
patch("app.tasks.AdvancedPDFParser.ingest_document") as mock_ingest:

# Simulate what the underlying service does upon a successful processing run
def simulate_successful_ingestion(*args, **kwargs):
doc = db_session.query(Document).filter_by(id="test-doc-123").first()
if doc:
doc.status = "ready"
db_session.commit()
return {"status": "success"}
return [
{"page_number": 1, "text": "Sample text", "type": "text_layout"}
]

mock_ingest.side_effect = simulate_successful_ingestion

Expand Down
16 changes: 6 additions & 10 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ services:
environment:
- SECRET_KEY=${SECRET_KEY:-dev-secret-key-change-me}
- HF_TOKEN=${HF_TOKEN}
- DATABASE_URL=postgresql://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- DATABASE_URL=postgresql+psycopg://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- UPLOAD_DIR=/app/data/uploads
- CHROMA_PERSIST_DIR=/app/data/chroma_db
- GRAPH_PERSIST_DIR=/app/data/graphs
Expand Down Expand Up @@ -90,7 +90,7 @@ services:
environment:
- SECRET_KEY=${SECRET_KEY:-dev-secret-key-change-me}
- HF_TOKEN=${HF_TOKEN}
- DATABASE_URL=postgresql://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- DATABASE_URL=postgresql+psycopg://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- UPLOAD_DIR=/app/data/uploads
- CHROMA_PERSIST_DIR=/app/data/chroma_db
- GRAPH_PERSIST_DIR=/app/data/graphs
Expand Down Expand Up @@ -123,15 +123,13 @@ services:
container_name: pdf_rag_worker
profiles: ["cpu"]
logging: *default-logging
command: >
sh -c "cd /app/backend &&
celery -A app.celery_app.celery_app worker --loglevel=info"
command: celery -A app.celery_app.celery_app worker --loglevel=info
volumes:
- app_data:/app/data
environment:
- SECRET_KEY=${SECRET_KEY:-dev-secret-key-change-me}
- HF_TOKEN=${HF_TOKEN}
- DATABASE_URL=postgresql://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- DATABASE_URL=postgresql+psycopg://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- UPLOAD_DIR=/app/data/uploads
- CHROMA_PERSIST_DIR=/app/data/chroma_db
- GRAPH_PERSIST_DIR=/app/data/graphs
Expand All @@ -151,15 +149,13 @@ services:
container_name: pdf_rag_worker
profiles: ["gpu"]
logging: *default-logging
command: >
sh -c "cd /app/backend &&
celery -A app.celery_app.celery_app worker --loglevel=info"
command: celery -A app.celery_app.celery_app worker --loglevel=info
volumes:
- app_data:/app/data
environment:
- SECRET_KEY=${SECRET_KEY:-dev-secret-key-change-me}
- HF_TOKEN=${HF_TOKEN}
- DATABASE_URL=postgresql://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- DATABASE_URL=postgresql+psycopg://${POSTGRES_USER:-pdf_rag_user}:${POSTGRES_PASSWORD:-pdf_rag_pass}@postgres:5432/${POSTGRES_DB:-pdf_rag}
- UPLOAD_DIR=/app/data/uploads
- CHROMA_PERSIST_DIR=/app/data/chroma_db
- GRAPH_PERSIST_DIR=/app/data/graphs
Expand Down
31 changes: 25 additions & 6 deletions frontend/src/app/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import { useAuth } from "@/lib/auth";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription,
} from "@/components/ui/card";
import { Brain } from "lucide-react";
import Link from "next/link";
import GoogleSignInButton from "@/components/auth/GoogleSignInButton";
Expand Down Expand Up @@ -50,7 +56,8 @@ export default function RegisterPage() {
setSuccess(result.message);
setVerificationUrl(result.verification_url ?? "");
} catch (err: unknown) {
const message = err instanceof Error ? err.message : t("register.fallbackError");
const message =
err instanceof Error ? err.message : t("register.fallbackError");
setError(message);
} finally {
setLoading(false);
Expand All @@ -68,7 +75,9 @@ export default function RegisterPage() {
<Brain className="w-6 h-6 text-primary" />
</div>
</div>
<CardTitle className="text-2xl font-bold">{t("register.title")}</CardTitle>
<CardTitle className="text-2xl font-bold">
{t("register.title")}
</CardTitle>
<CardDescription>{t("register.description")}</CardDescription>
</CardHeader>

Expand Down Expand Up @@ -97,7 +106,10 @@ export default function RegisterPage() {
{t("register.verifyMessage", { email: registeredEmail })}
</p>
{verificationUrl && (
<Link href={verificationUrl} className="inline-flex mt-3 font-medium underline">
<Link
href={verificationUrl}
className="inline-flex mt-3 font-medium underline"
>
{t("register.openVerification")}
</Link>
)}
Expand Down Expand Up @@ -145,7 +157,11 @@ export default function RegisterPage() {
disabled={Boolean(success)}
/>

<Button type="submit" className="w-full h-11 text-base" disabled={loading || Boolean(success)}>
<Button
type="submit"
className="w-full h-11 text-base"
disabled={loading || Boolean(success)}
>
{loading ? (
<span className="flex items-center gap-2">
<span className="w-4 h-4 border-2 border-primary-foreground/30 border-t-primary-foreground rounded-full animate-spin" />
Expand All @@ -159,7 +175,10 @@ export default function RegisterPage() {

<p className="text-center text-sm text-muted-foreground mt-6">
{t("register.hasAccount")}{" "}
<Link href="/login" className="text-primary hover:underline font-medium">
<Link
href="/login"
className="text-primary hover:underline font-medium"
>
{t("register.signIn")}
</Link>
</p>
Expand Down
Loading
Loading