diff --git a/README.md b/README.md
index 284ce97..b27813b 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
CiteMe is a modern, full-stack application designed to help researchers and academics manage their citations and references efficiently. The system provides intelligent citation suggestions, reference management, and seamless integration with academic databases.
+๐ **Live Demo**: [CiteMe Editor](https://cite-me-wpre.vercel.app/editor)
+
## ๐ Features
- **Smart Citation Suggestions**: AI-powered citation recommendations based on your research context
@@ -11,6 +13,23 @@ CiteMe is a modern, full-stack application designed to help researchers and acad
- **Modern UI**: Responsive and intuitive user interface
- **API Integration**: Seamless integration with academic databases and search engines
+## ๐ Project Structure
+
+```
+CiteMe/
+โโโ frontend/ # Vue.js 3 frontend application
+โ โโโ src/ # Source code
+โ โโโ public/ # Static assets
+โ โโโ e2e/ # End-to-end tests
+โ โโโ dist/ # Production build
+โโโ backend/
+โ โโโ mainService/ # Core citation service
+โ โโโ metricsService/ # Analytics and metrics service
+โโโ .github/ # GitHub workflows and templates
+โโโ docker-compose.yml # Docker services configuration
+โโโ README.md # Project documentation
+```
+
## ๐๏ธ Architecture
The application is built using a microservices architecture with three main components:
@@ -46,7 +65,7 @@ The application is built using a microservices architecture with three main comp
- Node.js 20+ (for local frontend development)
- Python 3.11+ (for local backend development)
-### Running with Docker
+### Running with Docker Compose (Recommended for Local Development)
1. Clone the repository:
```bash
@@ -54,21 +73,52 @@ git clone https://github.com/yourusername/citeme.git
cd citeme
```
-2. Create a `.env` file in the root directory with necessary environment variables:
-```env
-# Add your environment variables here
-```
+2. Create `.env` files in both service directories:
+ - `backend/mainService/.env`
+ - `backend/metricsService/.env`
-3. Build and run the backend services using Docker Compose:
+3. Build and run the services using Docker Compose:
```bash
docker-compose up --build
```
-The backend services will be available at:
-- Main Service: http://localhost:8000
-- Metrics Service: http://localhost:8001
+The services will be available at:
+- Main Service: http://localhost:9020
+- Metrics Service: http://localhost:9050
+
+### Running Services Individually
+
+If you need to run services separately:
+
+1. Create the Docker network:
+```bash
+docker network create cite_me
+```
+
+2. Run the Metrics Service:
+```bash
+cd backend/metricsService
+docker build -t metrics_service .
+docker run -p 9050:8000 \
+ --name ms \
+ --network cite_me \
+ --env-file .env \
+ metrics_service
+```
+
+3. Run the Main Service:
+```bash
+cd backend/mainService
+docker build -t main_service .
+docker run -p 9020:8000 \
+ --name mbs \
+ --network cite_me \
+ --env-file .env \
+ -e CREDIBILITY_API_URL=http://ms:8000/api/v1/credibility/batch \
+ main_service
+```
-### Local Development
+### Local Development Without Docker
#### Frontend
```bash
@@ -83,7 +133,7 @@ cd backend/mainService
python -m venv venv
source venv/bin/activate # On Windows: .\venv\Scripts\activate
pip install -r requirements.txt
-uvicorn main:app --reload
+uvicorn app:app --reload --port 9020
```
#### Metrics Service
@@ -92,14 +142,14 @@ cd backend/metricsService
python -m venv venv
source venv/bin/activate # On Windows: .\venv\Scripts\activate
pip install -r requirements.txt
-uvicorn main:app --reload
+uvicorn src.main:app --reload --port 9050
```
## ๐ API Documentation
Once the services are running, you can access the API documentation at:
-- Main Service: http://localhost:8000/docs
-- Metrics Service: http://localhost:8001/docs
+- Main Service: http://localhost:9020/docs
+- Metrics Service: http://localhost:9050/docs
## ๐งช Testing
diff --git a/backend/mainService/.env.example b/backend/mainService/.env.example
index 55d82ce..9b500c3 100644
--- a/backend/mainService/.env.example
+++ b/backend/mainService/.env.example
@@ -4,4 +4,46 @@ GROQ_API_KEY= # your groq api key
GOOGLE_API_KEY= # your gemini google api key
MIXBREAD_API_KEY= # your mixbread api key
PINECONE_API_KEY= # your pinecone api key
-AZURE_MODELS_ENDPOINT = # your azure model endpoint for citation generation
\ No newline at end of file
+AZURE_MODELS_ENDPOINT = # your azure model endpoint for citation generation
+CREDIBILITY_API_URL = # your credibility api url
+
+
+#NOTE:
+# CREDIBILITY_API_URL is the url of the credibility api that is used to get the credibility metrics for the sources
+# CREDIBILITY_API_URL is optional and is only used if the CREDIBILITY_API_URL environment variable is set
+# If the CREDIBILITY_API_URL environment variable is not set, the credibility metrics will not be fetched
+
+
+#AZURE_MODELS_ENDPOINT is the endpoint of the azure model that is used for citation generation
+#AZURE_MODELS_ENDPOINT is required and is used to generate the citations for the sources
+
+
+#MIXBREAD_API_KEY is the api key of the mixbread api that is used to rerank the sources
+#MIXBREAD_API_KEY is required and is used to rerank the sources
+
+
+#PINECONE_API_KEY is the api key of the pinecone api that is used to store the embeddings of the sources
+#PINECONE_API_KEY is required and is used to store the embeddings of the sources
+
+
+#GPSE_API_KEY is the api key of the google programmable search engine api that is used to search the web
+#GPSE_API_KEY is required and is used to search the web
+
+
+#GOOGLE_API_KEY is the api key for gemini google api
+#GOOGLE_API_KEY it is required and is used to merge the chunk of cited citations returned by the azure model
+
+#CX is the custom search engine id for google programmable search engine
+
+#All the above can be replaced by writing your own functions for the respective services
+#for instance, one could decide to use gemini to generate the intext citation and references rather than using an azure
+#hosted model. Hence all you need to do is write your own cite function/module and replace the azure cite function in the citation service file
+
+
+
+
+
+
+
+
+
diff --git a/backend/mainService/Dockerfile b/backend/mainService/Dockerfile
index 0e34659..08ab309 100644
--- a/backend/mainService/Dockerfile
+++ b/backend/mainService/Dockerfile
@@ -3,21 +3,36 @@ FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
+# Installs essential tools for compiling software from source, often needed for Python package dependencies.(build-essential)
+# Removes the package lists downloaded during the update to reduce the image size.
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
+# Set the PATH environment variable to include /app
+ENV PATH="/app:${PATH}"
+
# Copy requirements first to leverage Docker cache
COPY requirements.txt .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
-# Copy the rest of the application
-COPY . .
+# Copy the source code
+COPY ./scripts/ /app/scripts/
+COPY ./src/ /app/src/
+COPY ./app.py /app/app.py
+COPY ./__init__.py /app/__init__.py
+
+# Create a directory for runtime configuration
+RUN mkdir -p /app/config
+
+# Install playwright
+RUN playwright install && playwright install-deps
# Expose the port the app runs on
EXPOSE 8000
+
# Command to run the application
-CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
\ No newline at end of file
+CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
\ No newline at end of file
diff --git a/backend/mainService/app.py b/backend/mainService/app.py
index 7b4f7bd..4c965bb 100644
--- a/backend/mainService/app.py
+++ b/backend/mainService/app.py
@@ -12,6 +12,9 @@
from src.scraper.async_content_scraper import AsyncContentScraper
from fastapi.middleware.cors import CORSMiddleware
import nltk
+from src.utils.concurrent_resources import cleanup_resources
+
+
origins = [
"http://localhost:5173", # Frontend running on localhost (React, Vue, etc.)
@@ -37,6 +40,7 @@ async def startup_event(app: FastAPI):
await app.state.playwright_driver.quit()
await app.state.pc.cleanup()
await AsyncHTTPClient.close_session()
+ cleanup_resources() # Clean up thread pool and other concurrent resources
app = FastAPI(lifespan=startup_event)
diff --git a/backend/mainService/requirements.txt b/backend/mainService/requirements.txt
index 261afd3..a39a8eb 100644
--- a/backend/mainService/requirements.txt
+++ b/backend/mainService/requirements.txt
@@ -23,4 +23,5 @@ urllib3==2.3.0
lxml==5.3.0
google-genai
redis>=4.2.0
+uvicorn
diff --git a/backend/mainService/src/config/config.py b/backend/mainService/src/config/config.py
index d8fcfbd..c70b91d 100644
--- a/backend/mainService/src/config/config.py
+++ b/backend/mainService/src/config/config.py
@@ -98,18 +98,17 @@ class LlmConfig:
# Concurrency and Performance
@dataclass
class ConcurrencyConfig:
- """Configuration class for concurrency settings.
+ """Configuration class for concurrency settings."""
- Contains settings that control parallel processing and thread management."""
- """Configuration for concurrency and performance settings."""
-
- """
- This is the number of concurrent workers that will be used for parallel and concurrent operations.
- """
+ # General concurrency settings
DEFAULT_CONCURRENT_WORKERS: int = (os.cpu_count() // 2) + 1
-
HANDLE_INDEX_DELETE_WORKERS: int = 2
+ # Credibility service specific settings
+ CREDIBILITY_MAX_THREADS: int = 4 # Maximum threads for credibility calculations
+ CREDIBILITY_MAX_CONCURRENT: int = 8 # Maximum concurrent operations
+ CREDIBILITY_BATCH_SIZE: int = 4 # Size of processing batches
+
@dataclass
class ModelConfig:
diff --git a/backend/mainService/src/scraper/async_content_scraper.py b/backend/mainService/src/scraper/async_content_scraper.py
index 22dbc38..1f63f6a 100644
--- a/backend/mainService/src/scraper/async_content_scraper.py
+++ b/backend/mainService/src/scraper/async_content_scraper.py
@@ -99,7 +99,7 @@ async def __aenter__(self):
async def __aexit__(self, exc_type, exc_val, exc_tb):
try:
if self._context:
- self.scraper_driver.quit()
+ await self.scraper_driver.quit()
await self._context.close()
except Exception as e:
# Log the exception even if it occurred during cleanup
diff --git a/backend/mainService/src/services/citation_service.py b/backend/mainService/src/services/citation_service.py
index 1d2e0f0..c7a79c7 100644
--- a/backend/mainService/src/services/citation_service.py
+++ b/backend/mainService/src/services/citation_service.py
@@ -368,64 +368,58 @@ async def _generate_citations(
max_tokens=LLMEC.QUERY_TOKEN_SIZE,
overlap_percent=5
)
- # RAG + Rerank
+ # RAG + Rerank
results = await self.process_queries(queries)
- logger.info(f"size of reranked results:{len(results)}\n\n")
filtered_results = filter_mixbread_results(results)
- logger.info(f"size of filtered results:{len(filtered_results)}\n\n")
- # Generrate citation
+
+ sources_with_scores = [
+ {
+ "title": result.get("title", ""),
+ "link": result.get("link", "") or result.get("url", ""),
+ "domain": result.get("domain", ""),
+ "journal": result.get("journal_title", ""),
+ "citation_doi": result.get("citation_doi", ""),
+ "citation_references": result.get("references", [""]),
+ "publication_date": result.get("publication_date", ""),
+ "author_name": result.get("author_name", "") or result.get("author", "") or result.get("authors", ""),
+ "abstract": result.get("abstract", ""),
+ "issn": result.get("issn", ""),
+ "type": result.get("type", ""),
+ "rerank_score": result.get("score", 0)
+ } for result in filtered_results
+ ]
+
+ credibility_task = get_credibility_metrics(sources_with_scores)
citation_task = Citation(source=filtered_results).cite(
text=queries,
citation_style=style
)
- sources_for_credibility = [
- {
- "title": result.get(
- "title", ""), "link": result.get(
- "link", "") or result.get(
- "url", ""), "domain": result.get(
- "domain", ""), "journal": result.get(
- "journal_title", ""), "citation_doi": result.get(
- "citation_doi", ""), "citation_references": result.get(
- "references", [""]), "publication_date": result.get(
- "publication_date", ""), "author_name": result.get(
- "author_name", "") or result.get(
- "author", "") or result.get(
- "authors", ""), "abstract": result.get(
- "abstract", ""), "issn": result.get(
- "issn", ""), "type": result.get(
- "type", "")} for result in filtered_results]
- credibility_task = get_credibility_metrics(sources_for_credibility)
-
- # Wait for both tasks to complete
- citation_result, credibility_metrics = await asyncio.gather(
- citation_task,
- credibility_task,
- return_exceptions=True
- )
+ # Start both tasks but handle credibility metrics first
+ credibility_metrics = await asyncio.gather(credibility_task, return_exceptions=True)
+
+ if isinstance(credibility_metrics[0], Exception):
+ logger.exception(f"Credibility metrics failed: {str(credibility_metrics[0])}")
+ credibility_metrics = []
+ else:
+ credibility_metrics = credibility_metrics[0]
+
+ # Calculate scores immediately after getting credibility metrics
+ scores = await calculate_overall_score(credibility_metrics, sources_with_scores,
+ rerank_weight=0.6, credibility_weight=0.4)
+ sources = [
+ item["data"] for item in credibility_metrics if item["status"] == "success"
+ ] if credibility_metrics else []
+
+ citation_result = await citation_task
if isinstance(citation_result, Exception):
logger.exception(f"Citation generation failed: {str(citation_result)}")
raise CitationGenerationError("Failed to generate citations")
- if isinstance(credibility_metrics, Exception):
- logger.exception(f"Credibility metrics failed: {str(credibility_metrics)}")
- credibility_metrics = []
-
- # Calculate overall credibility score
- overall_score = calculate_overall_score(credibility_metrics)
-
- # Extract source details from credibility metrics
- sources = []
- if credibility_metrics:
- sources = [
- item["data"] for item in credibility_metrics if item["status"] == "success"]
-
- # Structure the final response
return {
"result": citation_result,
- "overall_score": overall_score,
+ "overall_score": scores["overall_score"],
"sources": sources
}
@@ -436,13 +430,3 @@ async def _generate_citations(
logger.exception(f"Unexpected error in citation generation: {str(e)}")
return False
-
-# TODO: store unique top 2 reranked results in a set
-# TODO: feed the above to an llm as context to generate a citation
-# TODO: Break the user content into large batches and ask the llm to generate a citation for each sentence/paragraph in a batch in the requested format
-# TODO: store the citation in a database
-# TODO: return the content with intext citations and reference list in json format
-# TODO: annotate code
-# TODO: clean up code: Get rid of code smells,magic numbers and bottlenecks
-# TODO: add tests
-# TODO: add more error handling
diff --git a/backend/mainService/src/services/source_credibility_metric_service.py b/backend/mainService/src/services/source_credibility_metric_service.py
index 3b2a138..012b0f5 100644
--- a/backend/mainService/src/services/source_credibility_metric_service.py
+++ b/backend/mainService/src/services/source_credibility_metric_service.py
@@ -1,15 +1,52 @@
-from typing import List, Dict
+from typing import List, Dict, Any
import aiohttp
from src.config.log_config import setup_logging
import os
+from functools import partial
+import asyncio
+from src.config.config import concurrency_config
+from src.utils.concurrent_resources import credibility_executor, credibility_semaphore
filename = os.path.basename(__file__)
logger = setup_logging(filename=filename)
+def _calculate_source_score(metric: Dict, source: Dict,
+ rerank_weight: float, credibility_weight: float) -> tuple[float, Dict]:
+ """
+ Calculate weighted score for a single source in a separate thread.
+ Uses semaphore to limit concurrent calculations.
+
+ Args:
+ metric (Dict): Credibility metric for the source
+ source (Dict): Source with rerank score
+ rerank_weight (float): Weight for rerank score
+ credibility_weight (float): Weight for credibility score
+
+ Returns:
+ tuple[float, Dict]: Tuple of (weighted_score, updated_metric)
+ """
+ with credibility_semaphore:
+ if metric["status"] != "success":
+ return 0.00, metric
+
+ credibility_score = metric["data"]["credibility_score"]
+ rerank_score = source["rerank_score"]
+
+ # Normalize rerank score to 0-1 range
+ normalized_rerank = min(max(rerank_score, 0), 1)
+
+ # Calculate weighted score and normalize to 0-100 range
+ weighted_score = round((normalized_rerank * rerank_weight +
+ credibility_score * credibility_weight) * 100, 2)
+
+ # Update the credibility score in the metric data
+ metric["data"]["credibility_score"] = weighted_score
+ return weighted_score, metric
async def get_credibility_metrics(sources: List[Dict]) -> List[Dict]:
"""
Call the credibility API to get metrics for sources.
+ Uses connection pooling and timeout handling for better performance.
Args:
sources (List[Dict]): List of source metadata
@@ -17,9 +54,17 @@ async def get_credibility_metrics(sources: List[Dict]) -> List[Dict]:
Returns:
List[Dict]: Credibility metrics for each source
"""
- credibility_metrics_api = 'http://localhost:9050/api/v1/credibility/batch'
+ credibility_metrics_api = os.getenv('CREDIBILITY_API_URL','')
+ if not credibility_metrics_api:
+ logger.error("CREDIBILITY_API_URL is not set")
+ return []
+
+ # Configure timeout and connection settings
+ timeout = aiohttp.ClientTimeout(total=10) # 10 seconds total timeout
+ connector = aiohttp.TCPConnector(limit=10) # Limit concurrent connections
+
try:
- async with aiohttp.ClientSession() as session:
+ async with aiohttp.ClientSession(timeout=timeout, connector=connector) as session:
async with session.post(
credibility_metrics_api,
json={'sources': sources},
@@ -30,37 +75,61 @@ async def get_credibility_metrics(sources: List[Dict]) -> List[Dict]:
else:
logger.error(f"Credibility API error: {response.status}")
return []
+ except asyncio.TimeoutError:
+ logger.error("Credibility API request timed out")
+ return []
except Exception:
logger.exception("Error calling credibility API")
return []
+ finally:
+ connector.close()
-
-def calculate_overall_score(credibility_metrics: List[Dict]) -> float:
+async def calculate_overall_score(credibility_metrics: List[Dict], sources_with_scores: List[Dict],
+ rerank_weight: float = 0.6, credibility_weight: float = 0.4) -> Dict[str, Any]:
"""
- Calculate the weighted average of credibility scores.
-
+ Calculate weighted scores for each source and overall mean score using parallel processing.
+ Uses configured thread pool and semaphore for concurrent calculations.
+
Args:
- credibility_metrics (List[Dict]): List of credibility metric responses
-
+ credibility_metrics (List[Dict]): List of credibility metrics for each source
+ sources_with_scores (List[Dict]): List of sources with their rerank scores
+ rerank_weight (float): Weight for rerank score (default 0.6)
+ credibility_weight (float): Weight for credibility score (default 0.4)
+
Returns:
- float: Weighted average score rounded to 2 decimal places
+ Dict[str, Any]: Dictionary containing source scores and overall mean score
"""
- try:
- # Filter successful responses and extract scores
- valid_scores = [
- item["data"]["credibility_score"]
- for item in credibility_metrics
- if item["status"] == "success" and "data" in item
- ]
-
- if not valid_scores:
- return 0.0
-
- # Calculate simple average (can be modified to use weights if needed)
- average_score = sum(valid_scores) / len(valid_scores)
+ if not credibility_metrics or not sources_with_scores:
+ return {"overall_score": 0.00, "source_scores": []}
- return round(average_score, 2)
-
- except Exception:
- logger.exception("Error calculating overall score")
- return 0.0
+ try:
+ calculate_score = partial(_calculate_source_score,
+ rerank_weight=rerank_weight,
+ credibility_weight=credibility_weight)
+
+ # Process in batches using configured size
+ source_scores = []
+ for i in range(0, len(sources_with_scores), concurrency_config.CREDIBILITY_BATCH_SIZE):
+ batch_metrics = credibility_metrics[i:i + concurrency_config.CREDIBILITY_BATCH_SIZE]
+ batch_sources = sources_with_scores[i:i + concurrency_config.CREDIBILITY_BATCH_SIZE]
+
+ # Calculate batch scores
+ batch_results = list(credibility_executor.map(
+ lambda x: calculate_score(x[0], x[1]),
+ zip(batch_metrics, batch_sources)
+ ))
+
+ scores, updated_metrics = zip(*batch_results) if batch_results else ([], [])
+ source_scores.extend(scores)
+ credibility_metrics[i:i + concurrency_config.CREDIBILITY_BATCH_SIZE] = updated_metrics
+
+ overall_mean = round(sum(source_scores) / len(source_scores), 2) if source_scores else 0.00
+
+ return {
+ "overall_score": overall_mean,
+ "source_scores": source_scores
+ }
+
+ except Exception as e:
+ logger.exception(f"Error in score calculation: {str(e)}")
+ return {"overall_score": 0.00, "source_scores": []}
diff --git a/backend/mainService/src/utils/concurrent_resources.py b/backend/mainService/src/utils/concurrent_resources.py
new file mode 100644
index 0000000..0d33028
--- /dev/null
+++ b/backend/mainService/src/utils/concurrent_resources.py
@@ -0,0 +1,15 @@
+from concurrent.futures import ThreadPoolExecutor
+from threading import Semaphore
+from src.config.config import concurrency_config
+
+# Create thread pool for credibility calculations
+credibility_executor = ThreadPoolExecutor(
+ max_workers=concurrency_config.CREDIBILITY_MAX_THREADS
+)
+
+# Create semaphore for limiting concurrent operations
+credibility_semaphore = Semaphore(concurrency_config.CREDIBILITY_MAX_CONCURRENT)
+
+def cleanup_resources():
+ """Cleanup all concurrent resources"""
+ credibility_executor.shutdown(wait=True)
diff --git a/backend/mainService/src/utils/format_rerank_result.py b/backend/mainService/src/utils/format_rerank_result.py
index 0056a00..a70e3f5 100644
--- a/backend/mainService/src/utils/format_rerank_result.py
+++ b/backend/mainService/src/utils/format_rerank_result.py
@@ -111,6 +111,8 @@ def filter_mixbread_results(
doc: dict = result.input.get("metadata")
if doc.get("id") not in seen_ids and result.score >= benchmark:
seen_ids.add(doc.pop("id"))
+ doc["score"] = result.score
+ print(f"doc: {doc}")
unique_results.append(doc)
# with open("sample_output\\rerank_result_mixbread.json", "a") as f:
# json.dump(unique_results, f, indent=4)
diff --git a/backend/metricsService/Dockerfile b/backend/metricsService/Dockerfile
index b985497..5cf1198 100644
--- a/backend/metricsService/Dockerfile
+++ b/backend/metricsService/Dockerfile
@@ -3,21 +3,28 @@ FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
+# Installs essential tools for compiling software from source, often needed for Python package dependencies.(build-essential)
+# Removes the package lists downloaded during the update to reduce the image size.
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
+# Set the PATH environment variable to include /app
+ENV PATH="/app:${PATH}"
+
# Copy requirements first to leverage Docker cache
COPY requirements.txt .
# Install Python dependencies
-RUN pip install --no-cache-dir -r requirements.txt
+RUN pip install --no-cache-dir -r requirements.txt
# Copy the rest of the application
-COPY . .
+COPY ./src/ /app/src/
+
+RUN cd /app/src
# Expose the port the app runs on
-EXPOSE 8001
+EXPOSE 8000
# Command to run the application
-CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001"]
\ No newline at end of file
+CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
\ No newline at end of file
diff --git a/backend/metricsService/requirements.txt b/backend/metricsService/requirements.txt
index 0c38bee..183bb12 100644
--- a/backend/metricsService/requirements.txt
+++ b/backend/metricsService/requirements.txt
@@ -6,3 +6,4 @@ pytest==8.3.4
python-dotenv==1.0.1
Requests==2.32.3
scholarly==1.7.11
+uvicorn
diff --git a/backend/metricsService/src/__init__.py b/backend/metricsService/src/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/metricsService/src/api/endpoints.py b/backend/metricsService/src/api/endpoints.py
index f9bd46d..ecdf15a 100644
--- a/backend/metricsService/src/api/endpoints.py
+++ b/backend/metricsService/src/api/endpoints.py
@@ -44,14 +44,27 @@ async def compute_credibility(
"""Calculate credibility score for a single source"""
try:
result = await calculate_credibility(request)
+ # Store the total score before modifying the result
+ total_score = result.get("total_score", 0)
+
if detailed:
- credibility_score = result.pop("total_score", 0)
- return {"status": "success", "data": {"credibility_score": credibility_score,"component": result, "url": request.domain, "title": request.title, "type": request.type}}
+ # Don't pop the total_score, just create a new dict without it for components
+ components = {k: v for k, v in result.items() if k != "total_score"}
+ return {
+ "status": "success",
+ "data": {
+ "credibility_score": total_score,
+ "component": components,
+ "url": request.domain,
+ "title": request.title,
+ "type": request.type
+ }
+ }
else:
return {
"status": "success",
"data": {
- "credibility_score": result["total_score"],
+ "credibility_score": total_score,
"url": request.domain,
"title": request.title,
"type": request.type
diff --git a/backend/metricsService/src/services/credibility_service.py b/backend/metricsService/src/services/credibility_service.py
index a85dd7e..6edc27b 100644
--- a/backend/metricsService/src/services/credibility_service.py
+++ b/backend/metricsService/src/services/credibility_service.py
@@ -106,6 +106,10 @@ async def calculate_credibility(request: CredibilityRequest) -> Dict[str, Any]:
"authorship_reputation": results.get('author', 0)
}
+ # Debug logging
+ logger.info(f"Final result before caching: {result}")
+ logger.info(f"Total score calculation: {total_score} (total_weight: {total_weight})")
+
# Cache the result
await set_cache(cache_key, result)
diff --git a/docker-compose.yml b/docker-compose.yml
index 704a54d..fd369d7 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,32 +1,36 @@
version: '3.8'
services:
- main-service:
+ main_service:
build:
context: ./backend/mainService
dockerfile: Dockerfile
ports:
- - "8000:8000"
+ - "9020:8000"
+ env_file:
+ - ./backend/mainService/.env
environment:
- - ENVIRONMENT=production
+ - CREDIBILITY_API_URL=http://metrics_service:8000/api/v1/credibility/batch
volumes:
- ./backend/mainService:/app
networks:
- - citeme-network
+ - cite_me
+ depends_on:
+ - metrics_service
- metrics-service:
+ metrics_service:
build:
context: ./backend/metricsService
dockerfile: Dockerfile
ports:
- - "8001:8001"
- environment:
- - ENVIRONMENT=production
+ - "9050:8000"
+ env_file:
+ - ./backend/metricsService/.env
volumes:
- ./backend/metricsService:/app
networks:
- - citeme-network
+ - cite_me
networks:
- citeme-network:
+ cite_me:
driver: bridge
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
index f365e77..0c9c565 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,7 +2,7 @@