Skip to content
Open
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ dist-ssr/
.DS_Store
Thumbs.db

.vscode/
.vscode/

__pycache__/
*.pyc
26 changes: 26 additions & 0 deletions ai-service/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
from dotenv import load_dotenv
from flask import Flask, request, jsonify
from model import get_embedding
from utils import build_text

load_dotenv()
app = Flask(__name__)

@app.route("/embed", methods=["POST"])
def embed():
try:
data = request.json
if not data:
return jsonify({"error": "Invalid input"}), 400
text = build_text(data)
embedding = get_embedding(text)
return jsonify({"embedding": embedding}), 200
Comment thread
coderabbitai[bot] marked this conversation as resolved.
except Exception as e:
print("Error in /embed:", str(e))
return jsonify({"error": "Internal server error"}), 500

if __name__ == "__main__":
PORT = int(os.getenv("PORT", 3000))
DEBUG = os.getenv("FLASK_DEBUG", "false").lower() in ["true", "1", "yes"]
app.run(port=PORT, debug=DEBUG)
Comment on lines +23 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add error handling for PORT environment variable parsing.

If the PORT environment variable contains a non-numeric value, int() will raise a ValueError and crash the application at startup. While this is an edge case, adding validation would make the service more robust.

🛡️ Proposed fix to handle invalid PORT values
 if __name__ == "__main__":
-    PORT = int(os.getenv("PORT", 3000))
+    try:
+        PORT = int(os.getenv("PORT", "3000"))
+    except ValueError:
+        PORT = 3000
     DEBUG = os.getenv("FLASK_DEBUG", "false").lower() in ["true", "1", "yes"]
     app.run(port=PORT, debug=DEBUG)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ai-service/app.py` around lines 23 - 26, Validate parsing of the PORT
environment variable in the __main__ block: replace the direct
int(os.getenv("PORT", 3000)) usage with code that attempts to parse
os.getenv("PORT") inside a try/except (or use str.isdigit checks), on ValueError
log a clear error (use the app or module logger), fall back to the default port
3000 (or exit if you prefer strict behavior), and then call app.run(port=PORT,
debug=DEBUG); update the section around the PORT variable assignment and the
app.run call to use the validated PORT.

6 changes: 6 additions & 0 deletions ai-service/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

def get_embedding(text):
return model.encode(text).tolist()
5 changes: 5 additions & 0 deletions ai-service/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
flask
sentence-transformers
numpy
scikit-learn
python-dotenv
21 changes: 21 additions & 0 deletions ai-service/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
def safe_join(value):
if isinstance(value, list):
return " ".join(value)
if isinstance(value, str):
return value
return ""
def build_text(data):
title = data.get("title", "")
description = data.get("description", "")
tags_text = safe_join(data.get("tags"))
tech_text = safe_join(data.get("techStack"))
difficulty = data.get("difficulty", "")

text = f"""
BUG TITLE: {title}. {title}.
BUG DESCRIPTION: {description}.
BUG TAGS: {tags_text}.
TECHNOLOGIES: {tech_text}.
DIFFICULTY LEVEL: {difficulty}.
"""
return text.lower().strip()
135 changes: 135 additions & 0 deletions core-services/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core-services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"license": "ISC",
"type": "commonjs",
"dependencies": {
"axios": "^1.14.0",
"cors": "^2.8.6",
"dotenv": "^17.3.1",
"express": "^5.2.1",
Expand Down
5 changes: 5 additions & 0 deletions core-services/src/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const express = require("express");
const cors = require("cors");
const vectorRouter = require("./routes/vector.Routes");
const recommendationRouter = require("./routes/recommendation.Routes");

const app = express();

Expand All @@ -10,4 +12,7 @@ app.get("/", (req, res) => {
res.send("Backend is running!!");
});

app.use("/api/vectors", vectorRouter);
app.use("/api/recommendations", recommendationRouter);

module.exports = app;
49 changes: 49 additions & 0 deletions core-services/src/controllers/recommendation.Controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const Bug = require("../models/Bug");
const Vector = require("../models/Vector");
const { cosineSimilarity } = require("../utils/similarityScore");
const mongoose = require("mongoose");

exports.getSimilarBugs = async (req, res) => {
try {
const bugId = req.params.id;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (!mongoose.Types.ObjectId.isValid(bugId)) {
return res.status(400).json({ message: "Invalid bugId" });
}

const currentVectorDoc = await Vector.findOne({ bugId });

if (!currentVectorDoc) {
return res.status(404).json({ message: "Vector not found" });
}
const currentVector = currentVectorDoc.vector;
const allVectors = await Vector.find({ bugId: { $ne: bugId } });
const similarities = allVectors.map((item) => ({
bugId: item.bugId,
score: cosineSimilarity(currentVector, item.vector)
}));
const top5 = similarities
.sort((a, b) => b.score - a.score)
.slice(0, 5);
const bugs = await Bug.find({
_id: { $in: top5.map(i => i.bugId) }
});
const bugMap = new Map();
bugs.forEach(b => bugMap.set(b._id.toString(), b));

const finalRecommendations = top5.map(item => {
const bug = bugMap.get(item.bugId.toString());
if (!bug) return null;
return {
...bug.toObject(),
similarityScore: item.score
};
}).filter(Boolean);
res.json({
recommendations: finalRecommendations
});

} catch (err) {
console.error(err);
res.status(500).json({ message: "Error fetching recommendations" });
}
};
46 changes: 46 additions & 0 deletions core-services/src/controllers/vector.Controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const mongoose = require("mongoose");
const Bug = require("../models/Bug");
const Vector = require("../models/Vector");
const { getEmbedding } = require("../services/embedding.Service");

exports.createVector = async (req, res) => {
try {
const { bugId } = req.body;

if (!bugId) {
return res.status(400).json({ message: "bugId is required" });
}

if (!mongoose.Types.ObjectId.isValid(bugId)) {
return res.status(400).json({ message: "Invalid bugId format" });
}

const bug = await Bug.findById(bugId);

if (!bug) {
return res.status(404).json({ message: "Bug not found" });
}

const embedding = await getEmbedding(bug);

const vectorDoc = await Vector.findOneAndUpdate(
{ bugId: bug._id },
{
bugId: bug._id,
vector: embedding,
modelVersion: "all-MiniLM-L6-v2",
updatedAt: new Date(),
},
{ upsert: true, returnDocument: "after"}
);

return res.status(200).json({
message: "Vector created successfully",
data: vectorDoc,
});

} catch (error) {
console.error("VECTOR ERROR:", error);
return res.status(500).json({ message: "Error creating vector" });
}
};
Loading