Spaces:
Sleeping
Sleeping
Spandan Roy
commited on
Commit
Β·
dc673ed
1
Parent(s):
23046d5
Phase 1 Complete: Triage Agent with zero-shot classification
Browse files- Added Neo4j schema and client
- Added Qdrant vector store setup
- Implemented Triage Agent with BART model
- Updated Gradio UI with bug classification tab
- Added sample bug dataset
- Project structure complete
- agents/triage/triage_agent.py +166 -0
- app.py +123 -49
- data/sample/bugs_sample.csv +11 -0
- knowledge-graph/neo4j_client.py +82 -0
- knowledge-graph/schemas/bug_schema.cypher +56 -0
- rag-system/vectorstore/qdrant_client.py +103 -0
agents/triage/triage_agent.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Triage Agent: Automated Bug Classification and Priority Assignment
|
| 3 |
+
"""
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from transformers import pipeline
|
| 6 |
+
from typing import Dict, List
|
| 7 |
+
import logging
|
| 8 |
+
|
| 9 |
+
logging.basicConfig(level=logging.INFO)
|
| 10 |
+
logger = logging.getLogger(__name__)
|
| 11 |
+
|
| 12 |
+
class TriageAgent:
|
| 13 |
+
"""
|
| 14 |
+
Agent responsible for:
|
| 15 |
+
1. Classifying bugs into categories (UI, API, Database, Performance)
|
| 16 |
+
2. Assigning priority levels (P0-P4)
|
| 17 |
+
3. Determining severity (Critical, High, Medium, Low)
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
def __init__(self):
|
| 21 |
+
"""Initialize the triage agent with ML models"""
|
| 22 |
+
self.classifier = None
|
| 23 |
+
self.categories = ['UI', 'API', 'Database', 'Performance', 'Backend']
|
| 24 |
+
self.priorities = ['P0', 'P1', 'P2', 'P3', 'P4']
|
| 25 |
+
self.severities = ['Critical', 'High', 'Medium', 'Low']
|
| 26 |
+
|
| 27 |
+
def load_model(self):
|
| 28 |
+
"""Load pre-trained classification model"""
|
| 29 |
+
try:
|
| 30 |
+
# Using zero-shot classification for now
|
| 31 |
+
# Later: Fine-tune BERT on bug data
|
| 32 |
+
self.classifier = pipeline(
|
| 33 |
+
"zero-shot-classification",
|
| 34 |
+
model="facebook/bart-large-mnli",
|
| 35 |
+
device=-1 # CPU
|
| 36 |
+
)
|
| 37 |
+
logger.info("β
Triage model loaded successfully")
|
| 38 |
+
return True
|
| 39 |
+
except Exception as e:
|
| 40 |
+
logger.error(f"β Model loading failed: {e}")
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
def classify_bug(self, title: str, description: str) -> Dict:
|
| 44 |
+
"""
|
| 45 |
+
Classify a bug based on title and description
|
| 46 |
+
|
| 47 |
+
Args:
|
| 48 |
+
title: Bug title
|
| 49 |
+
description: Bug description
|
| 50 |
+
|
| 51 |
+
Returns:
|
| 52 |
+
Dict with category, priority, severity, confidence
|
| 53 |
+
"""
|
| 54 |
+
if not self.classifier:
|
| 55 |
+
logger.error("Model not loaded!")
|
| 56 |
+
return None
|
| 57 |
+
|
| 58 |
+
# Combine title and description
|
| 59 |
+
bug_text = f"{title}. {description}"
|
| 60 |
+
|
| 61 |
+
# Classify category
|
| 62 |
+
category_result = self.classifier(
|
| 63 |
+
bug_text,
|
| 64 |
+
candidate_labels=self.categories
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
# Determine priority based on keywords
|
| 68 |
+
priority = self._determine_priority(bug_text)
|
| 69 |
+
|
| 70 |
+
# Determine severity
|
| 71 |
+
severity = self._determine_severity(bug_text, priority)
|
| 72 |
+
|
| 73 |
+
result = {
|
| 74 |
+
'category': category_result['labels'][0],
|
| 75 |
+
'category_confidence': category_result['scores'][0],
|
| 76 |
+
'priority': priority,
|
| 77 |
+
'severity': severity,
|
| 78 |
+
'reasoning': self._generate_reasoning(
|
| 79 |
+
category_result['labels'][0],
|
| 80 |
+
priority,
|
| 81 |
+
severity
|
| 82 |
+
)
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
logger.info(f"β
Classified bug as {result['category']} / {result['priority']}")
|
| 86 |
+
return result
|
| 87 |
+
|
| 88 |
+
def _determine_priority(self, text: str) -> str:
|
| 89 |
+
"""Determine priority based on keywords"""
|
| 90 |
+
text_lower = text.lower()
|
| 91 |
+
|
| 92 |
+
# Critical keywords -> P0
|
| 93 |
+
critical_keywords = ['crash', 'critical', 'security', 'data loss', 'payment']
|
| 94 |
+
if any(keyword in text_lower for keyword in critical_keywords):
|
| 95 |
+
return 'P0'
|
| 96 |
+
|
| 97 |
+
# High priority keywords -> P1
|
| 98 |
+
high_keywords = ['broken', 'not working', 'error', 'fail', 'timeout']
|
| 99 |
+
if any(keyword in text_lower for keyword in high_keywords):
|
| 100 |
+
return 'P1'
|
| 101 |
+
|
| 102 |
+
# Medium priority keywords -> P2
|
| 103 |
+
medium_keywords = ['slow', 'incorrect', 'missing', 'wrong']
|
| 104 |
+
if any(keyword in text_lower for keyword in medium_keywords):
|
| 105 |
+
return 'P2'
|
| 106 |
+
|
| 107 |
+
# Low priority keywords -> P3
|
| 108 |
+
low_keywords = ['cosmetic', 'color', 'spacing', 'typo']
|
| 109 |
+
if any(keyword in text_lower for keyword in low_keywords):
|
| 110 |
+
return 'P3'
|
| 111 |
+
|
| 112 |
+
# Default
|
| 113 |
+
return 'P2'
|
| 114 |
+
|
| 115 |
+
def _determine_severity(self, text: str, priority: str) -> str:
|
| 116 |
+
"""Map priority to severity"""
|
| 117 |
+
severity_map = {
|
| 118 |
+
'P0': 'Critical',
|
| 119 |
+
'P1': 'High',
|
| 120 |
+
'P2': 'Medium',
|
| 121 |
+
'P3': 'Low',
|
| 122 |
+
'P4': 'Low'
|
| 123 |
+
}
|
| 124 |
+
return severity_map.get(priority, 'Medium')
|
| 125 |
+
|
| 126 |
+
def _generate_reasoning(self, category: str, priority: str, severity: str) -> str:
|
| 127 |
+
"""Generate explanation for classification"""
|
| 128 |
+
return f"""
|
| 129 |
+
This bug has been classified as a {category} issue with {priority} priority
|
| 130 |
+
and {severity} severity based on the content analysis and keyword detection.
|
| 131 |
+
"""
|
| 132 |
+
|
| 133 |
+
def batch_classify(self, bugs_df: pd.DataFrame) -> pd.DataFrame:
|
| 134 |
+
"""Classify multiple bugs from a DataFrame"""
|
| 135 |
+
results = []
|
| 136 |
+
|
| 137 |
+
for idx, row in bugs_df.iterrows():
|
| 138 |
+
result = self.classify_bug(row['title'], row['description'])
|
| 139 |
+
results.append(result)
|
| 140 |
+
|
| 141 |
+
# Add results to dataframe
|
| 142 |
+
bugs_df['predicted_category'] = [r['category'] for r in results]
|
| 143 |
+
bugs_df['predicted_priority'] = [r['priority'] for r in results]
|
| 144 |
+
bugs_df['predicted_severity'] = [r['severity'] for r in results]
|
| 145 |
+
bugs_df['confidence'] = [r['category_confidence'] for r in results]
|
| 146 |
+
|
| 147 |
+
return bugs_df
|
| 148 |
+
|
| 149 |
+
# Example usage and testing
|
| 150 |
+
if __name__ == "__main__":
|
| 151 |
+
agent = TriageAgent()
|
| 152 |
+
|
| 153 |
+
if agent.load_model():
|
| 154 |
+
# Test single bug
|
| 155 |
+
result = agent.classify_bug(
|
| 156 |
+
title="Login button not responding",
|
| 157 |
+
description="Users cannot click the login button on mobile devices"
|
| 158 |
+
)
|
| 159 |
+
print("Classification Result:")
|
| 160 |
+
print(result)
|
| 161 |
+
|
| 162 |
+
# Test batch classification
|
| 163 |
+
bugs_df = pd.read_csv('../../data/sample/bugs_sample.csv')
|
| 164 |
+
classified_df = agent.batch_classify(bugs_df)
|
| 165 |
+
print("\nBatch Classification Results:")
|
| 166 |
+
print(classified_df[['bug_id', 'category', 'predicted_category', 'confidence']])
|
app.py
CHANGED
|
@@ -1,51 +1,81 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
|
|
|
| 3 |
from dotenv import load_dotenv
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
# Load environment variables
|
| 6 |
load_dotenv()
|
| 7 |
-
|
| 8 |
-
# Set HF_HOME to persist model caches
|
| 9 |
os.environ['HF_HOME'] = os.getenv('HF_HOME', '/data/.huggingface')
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
def welcome_message():
|
| 12 |
-
"""
|
| 13 |
return """
|
| 14 |
# π Intelligent Bug Triage System
|
| 15 |
|
| 16 |
-
|
| 17 |
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
###
|
| 21 |
-
|
| 22 |
-
- π₯ Intelligent developer assignment with workload balancing
|
| 23 |
-
- π‘ Solution recommendations using RAG
|
| 24 |
-
- π Real-time analytics and performance tracking
|
| 25 |
|
| 26 |
-
|
| 27 |
-
1. Set up Neo4j knowledge graph
|
| 28 |
-
2. Configure vector database (Qdrant)
|
| 29 |
-
3. Train classification models
|
| 30 |
-
4. Build multi-agent system
|
| 31 |
-
|
| 32 |
-
**Project Status**: Development Environment Ready β
|
| 33 |
"""
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
def test_space_storage():
|
| 36 |
-
"""Test
|
| 37 |
import shutil
|
| 38 |
disk_usage = shutil.disk_usage('/')
|
| 39 |
total_gb = disk_usage.total / (1024**3)
|
| 40 |
-
used_gb = disk_usage.used / (1024**3)
|
| 41 |
free_gb = disk_usage.free / (1024**3)
|
| 42 |
-
|
| 43 |
-
return f"""
|
| 44 |
-
**Space Storage Information:**
|
| 45 |
-
- Total: {total_gb:.2f} GB
|
| 46 |
-
- Used: {used_gb:.2f} GB
|
| 47 |
-
- Free: {free_gb:.2f} GB
|
| 48 |
-
"""
|
| 49 |
|
| 50 |
# Create Gradio Interface
|
| 51 |
with gr.Blocks(theme=gr.themes.Soft(), title="Bug Triage System") as demo:
|
|
@@ -53,41 +83,85 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Bug Triage System") as demo:
|
|
| 53 |
gr.Markdown("*AI-Powered Bug Management with Knowledge Graphs & Multi-Agent RAG*")
|
| 54 |
|
| 55 |
with gr.Tab("Home"):
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
gr.Markdown("""
|
| 66 |
-
## Project
|
| 67 |
|
| 68 |
-
###
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
### Technology Stack
|
| 76 |
-
- **
|
| 77 |
-
- **Knowledge Graph**: Neo4j
|
| 78 |
- **Vector DB**: Qdrant
|
| 79 |
- **Agent Framework**: LangChain
|
| 80 |
-
- **ML Models**: Transformers (BERT)
|
| 81 |
- **Frontend**: Gradio
|
|
|
|
| 82 |
|
| 83 |
-
### Development
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
""")
|
| 89 |
|
| 90 |
-
# Launch
|
| 91 |
if __name__ == "__main__":
|
| 92 |
port = int(os.environ.get('PORT', 7860))
|
| 93 |
demo.launch(
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import os
|
| 3 |
+
import sys
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
+
import pandas as pd
|
| 6 |
+
|
| 7 |
+
# Add project root to Python path
|
| 8 |
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
| 9 |
|
| 10 |
# Load environment variables
|
| 11 |
load_dotenv()
|
|
|
|
|
|
|
| 12 |
os.environ['HF_HOME'] = os.getenv('HF_HOME', '/data/.huggingface')
|
| 13 |
|
| 14 |
+
# Import agents (will handle import errors gracefully)
|
| 15 |
+
try:
|
| 16 |
+
from agents.triage.triage_agent import TriageAgent
|
| 17 |
+
TRIAGE_AVAILABLE = True
|
| 18 |
+
except ImportError as e:
|
| 19 |
+
print(f"Triage agent not available: {e}")
|
| 20 |
+
TRIAGE_AVAILABLE = False
|
| 21 |
+
|
| 22 |
def welcome_message():
|
| 23 |
+
"""Welcome message"""
|
| 24 |
return """
|
| 25 |
# π Intelligent Bug Triage System
|
| 26 |
|
| 27 |
+
## Phase 1: Foundation - Triage Agent Ready! π
|
| 28 |
|
| 29 |
+
### Current Features:
|
| 30 |
+
- β
**Automated Bug Classification** (UI, API, Database, Performance, Backend)
|
| 31 |
+
- β
**Priority Assignment** (P0-P4)
|
| 32 |
+
- β
**Severity Detection** (Critical, High, Medium, Low)
|
| 33 |
+
- β
**Confidence Scoring**
|
| 34 |
|
| 35 |
+
### Try It Now:
|
| 36 |
+
Navigate to the "Bug Triage" tab to classify your first bug!
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
+
**Status**: Triage Agent Online β
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
"""
|
| 40 |
|
| 41 |
+
def classify_single_bug(title, description):
|
| 42 |
+
"""Classify a single bug"""
|
| 43 |
+
if not TRIAGE_AVAILABLE:
|
| 44 |
+
return "β Triage agent not loaded. Please check dependencies."
|
| 45 |
+
|
| 46 |
+
if not title or not description:
|
| 47 |
+
return "β οΈ Please provide both title and description."
|
| 48 |
+
|
| 49 |
+
try:
|
| 50 |
+
agent = TriageAgent()
|
| 51 |
+
if not agent.load_model():
|
| 52 |
+
return "β Failed to load triage model."
|
| 53 |
+
|
| 54 |
+
result = agent.classify_bug(title, description)
|
| 55 |
+
|
| 56 |
+
output = f"""
|
| 57 |
+
## Classification Results π―
|
| 58 |
+
|
| 59 |
+
**Category:** {result['category']}
|
| 60 |
+
**Priority:** {result['priority']}
|
| 61 |
+
**Severity:** {result['severity']}
|
| 62 |
+
**Confidence:** {result['category_confidence']:.2%}
|
| 63 |
+
|
| 64 |
+
### Reasoning:
|
| 65 |
+
{result['reasoning']}
|
| 66 |
+
"""
|
| 67 |
+
return output
|
| 68 |
+
|
| 69 |
+
except Exception as e:
|
| 70 |
+
return f"β Classification failed: {str(e)}"
|
| 71 |
+
|
| 72 |
def test_space_storage():
|
| 73 |
+
"""Test storage"""
|
| 74 |
import shutil
|
| 75 |
disk_usage = shutil.disk_usage('/')
|
| 76 |
total_gb = disk_usage.total / (1024**3)
|
|
|
|
| 77 |
free_gb = disk_usage.free / (1024**3)
|
| 78 |
+
return f"**Storage:** {free_gb:.2f} GB free of {total_gb:.2f} GB total"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
# Create Gradio Interface
|
| 81 |
with gr.Blocks(theme=gr.themes.Soft(), title="Bug Triage System") as demo:
|
|
|
|
| 83 |
gr.Markdown("*AI-Powered Bug Management with Knowledge Graphs & Multi-Agent RAG*")
|
| 84 |
|
| 85 |
with gr.Tab("Home"):
|
| 86 |
+
gr.Markdown(welcome_message())
|
| 87 |
+
storage_output = gr.Markdown(test_space_storage())
|
| 88 |
+
|
| 89 |
+
with gr.Tab("Bug Triage π―"):
|
| 90 |
+
gr.Markdown("## Classify a New Bug")
|
| 91 |
+
gr.Markdown("Enter bug details below to get automated classification:")
|
| 92 |
|
| 93 |
+
with gr.Row():
|
| 94 |
+
with gr.Column():
|
| 95 |
+
bug_title = gr.Textbox(
|
| 96 |
+
label="Bug Title",
|
| 97 |
+
placeholder="e.g., Login button not responding",
|
| 98 |
+
lines=2
|
| 99 |
+
)
|
| 100 |
+
bug_description = gr.Textbox(
|
| 101 |
+
label="Bug Description",
|
| 102 |
+
placeholder="Detailed description of the issue...",
|
| 103 |
+
lines=5
|
| 104 |
+
)
|
| 105 |
+
classify_btn = gr.Button("π Classify Bug", variant="primary")
|
| 106 |
+
|
| 107 |
+
with gr.Column():
|
| 108 |
+
classification_output = gr.Markdown(label="Classification Results")
|
| 109 |
|
| 110 |
+
classify_btn.click(
|
| 111 |
+
fn=classify_single_bug,
|
| 112 |
+
inputs=[bug_title, bug_description],
|
| 113 |
+
outputs=classification_output
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
# Examples
|
| 117 |
+
gr.Examples(
|
| 118 |
+
examples=[
|
| 119 |
+
["Login button not responding", "Users cannot click the login button on mobile devices. Issue occurs on iOS Safari only."],
|
| 120 |
+
["API timeout on large queries", "Database queries taking over 30 seconds to complete when fetching user data."],
|
| 121 |
+
["Memory leak in background service", "Application consuming 8GB RAM after running for 24 hours continuously."],
|
| 122 |
+
],
|
| 123 |
+
inputs=[bug_title, bug_description]
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
with gr.Tab("Documentation π"):
|
| 127 |
gr.Markdown("""
|
| 128 |
+
## Project Architecture
|
| 129 |
|
| 130 |
+
### Multi-Agent System
|
| 131 |
+
1. **Triage Agent** β
(ACTIVE)
|
| 132 |
+
- Classifies bugs into categories
|
| 133 |
+
- Assigns priority levels
|
| 134 |
+
- Determines severity
|
| 135 |
+
|
| 136 |
+
2. **Assignment Agent** (Coming in Phase 2)
|
| 137 |
+
- Routes bugs to developers
|
| 138 |
+
- Balances workload
|
| 139 |
+
- Considers expertise
|
| 140 |
+
|
| 141 |
+
3. **Resolution Agent** (Coming in Phase 3)
|
| 142 |
+
- Provides solution recommendations
|
| 143 |
+
- Uses RAG for knowledge retrieval
|
| 144 |
+
|
| 145 |
+
4. **Analytics Agent** (Coming in Phase 4)
|
| 146 |
+
- Tracks performance metrics
|
| 147 |
+
- Generates insights
|
| 148 |
|
| 149 |
### Technology Stack
|
| 150 |
+
- **ML Models**: Transformers (BART, BERT)
|
| 151 |
+
- **Knowledge Graph**: Neo4j
|
| 152 |
- **Vector DB**: Qdrant
|
| 153 |
- **Agent Framework**: LangChain
|
|
|
|
| 154 |
- **Frontend**: Gradio
|
| 155 |
+
- **Backend**: FastAPI
|
| 156 |
|
| 157 |
+
### Development Progress
|
| 158 |
+
- β
Phase 1: Foundation (Week 1-2)
|
| 159 |
+
- π Phase 2: Multi-Agent (Week 3-4)
|
| 160 |
+
- β³ Phase 3: RAG System (Week 5-6)
|
| 161 |
+
- β³ Phase 4: Deployment (Week 7-8)
|
| 162 |
""")
|
| 163 |
|
| 164 |
+
# Launch
|
| 165 |
if __name__ == "__main__":
|
| 166 |
port = int(os.environ.get('PORT', 7860))
|
| 167 |
demo.launch(
|
data/sample/bugs_sample.csv
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
bug_id,title,description,category,priority,severity
|
| 2 |
+
BUG-001,Login button not responding,User cannot click login button on mobile devices,UI,P1,High
|
| 3 |
+
BUG-002,API timeout on large queries,Database queries taking >30 seconds,API,P0,Critical
|
| 4 |
+
BUG-003,Memory leak in background service,Application consuming 8GB RAM after 24hrs,Performance,P1,High
|
| 5 |
+
BUG-004,Incorrect tax calculation,Shopping cart shows wrong tax amount,Backend,P0,Critical
|
| 6 |
+
BUG-005,Dark mode colors incorrect,Text not visible in dark mode,UI,P3,Low
|
| 7 |
+
BUG-006,Search returns empty results,Search functionality broken for certain queries,Backend,P2,Medium
|
| 8 |
+
BUG-007,Image upload fails for PNG,PNG files cannot be uploaded to profile,UI,P2,Medium
|
| 9 |
+
BUG-008,Database connection pool exhausted,App crashes during high traffic,Database,P0,Critical
|
| 10 |
+
BUG-009,Email notifications not sent,Users not receiving password reset emails,Backend,P1,High
|
| 11 |
+
BUG-010,Pagination breaks on page 10,Cannot navigate beyond page 10 in results,UI,P3,Low
|
knowledge-graph/neo4j_client.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Neo4j Knowledge Graph Client"""
|
| 2 |
+
import os
|
| 3 |
+
from neo4j import GraphDatabase
|
| 4 |
+
from typing import List, Dict, Any
|
| 5 |
+
import logging
|
| 6 |
+
|
| 7 |
+
logging.basicConfig(level=logging.INFO)
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
class Neo4jClient:
|
| 11 |
+
"""Client for interacting with Neo4j Knowledge Graph"""
|
| 12 |
+
|
| 13 |
+
def __init__(self):
|
| 14 |
+
"""Initialize Neo4j connection"""
|
| 15 |
+
self.uri = os.getenv('NEO4J_URI', 'bolt://localhost:7687')
|
| 16 |
+
self.user = os.getenv('NEO4J_USER', 'neo4j')
|
| 17 |
+
self.password = os.getenv('NEO4J_PASSWORD', 'password')
|
| 18 |
+
self.driver = None
|
| 19 |
+
|
| 20 |
+
def connect(self):
|
| 21 |
+
"""Establish connection to Neo4j"""
|
| 22 |
+
try:
|
| 23 |
+
self.driver = GraphDatabase.driver(
|
| 24 |
+
self.uri,
|
| 25 |
+
auth=(self.user, self.password)
|
| 26 |
+
)
|
| 27 |
+
logger.info("β
Connected to Neo4j")
|
| 28 |
+
return True
|
| 29 |
+
except Exception as e:
|
| 30 |
+
logger.error(f"β Neo4j connection failed: {e}")
|
| 31 |
+
return False
|
| 32 |
+
|
| 33 |
+
def close(self):
|
| 34 |
+
"""Close Neo4j connection"""
|
| 35 |
+
if self.driver:
|
| 36 |
+
self.driver.close()
|
| 37 |
+
logger.info("Neo4j connection closed")
|
| 38 |
+
|
| 39 |
+
def execute_query(self, query: str, parameters: Dict = None):
|
| 40 |
+
"""Execute a Cypher query"""
|
| 41 |
+
if not self.driver:
|
| 42 |
+
logger.error("Not connected to Neo4j")
|
| 43 |
+
return None
|
| 44 |
+
|
| 45 |
+
with self.driver.session() as session:
|
| 46 |
+
result = session.run(query, parameters or {})
|
| 47 |
+
return [record.data() for record in result]
|
| 48 |
+
|
| 49 |
+
def create_bug(self, bug_data: Dict[str, Any]):
|
| 50 |
+
"""Create a new bug node"""
|
| 51 |
+
query = """
|
| 52 |
+
CREATE (b:Bug {
|
| 53 |
+
id: $id,
|
| 54 |
+
title: $title,
|
| 55 |
+
description: $description,
|
| 56 |
+
priority: $priority,
|
| 57 |
+
category: $category,
|
| 58 |
+
status: 'Open',
|
| 59 |
+
created_at: datetime()
|
| 60 |
+
})
|
| 61 |
+
RETURN b
|
| 62 |
+
"""
|
| 63 |
+
return self.execute_query(query, bug_data)
|
| 64 |
+
|
| 65 |
+
def get_developer_by_expertise(self, expertise: str):
|
| 66 |
+
"""Find developers with specific expertise"""
|
| 67 |
+
query = """
|
| 68 |
+
MATCH (d:Developer)
|
| 69 |
+
WHERE $expertise IN d.expertise
|
| 70 |
+
AND d.current_workload < d.max_capacity
|
| 71 |
+
RETURN d
|
| 72 |
+
ORDER BY d.current_workload ASC
|
| 73 |
+
LIMIT 5
|
| 74 |
+
"""
|
| 75 |
+
return self.execute_query(query, {'expertise': expertise})
|
| 76 |
+
|
| 77 |
+
# Example usage
|
| 78 |
+
if __name__ == "__main__":
|
| 79 |
+
client = Neo4jClient()
|
| 80 |
+
if client.connect():
|
| 81 |
+
print("Neo4j client ready!")
|
| 82 |
+
client.close()
|
knowledge-graph/schemas/bug_schema.cypher
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Bug Triage Knowledge Graph Schema
|
| 2 |
+
// Nodes: Bug, Developer, Component, Team, Resolution
|
| 3 |
+
|
| 4 |
+
// Create Constraints
|
| 5 |
+
CREATE CONSTRAINT bug_id IF NOT EXISTS FOR (b:Bug) REQUIRE b.id IS UNIQUE;
|
| 6 |
+
CREATE CONSTRAINT dev_id IF NOT EXISTS FOR (d:Developer) REQUIRE d.id IS UNIQUE;
|
| 7 |
+
CREATE CONSTRAINT component_id IF NOT EXISTS FOR (c:Component) REQUIRE c.name IS UNIQUE;
|
| 8 |
+
CREATE CONSTRAINT team_id IF NOT EXISTS FOR (t:Team) REQUIRE t.name IS UNIQUE;
|
| 9 |
+
|
| 10 |
+
// Create Indexes
|
| 11 |
+
CREATE INDEX bug_priority IF NOT EXISTS FOR (b:Bug) ON (b.priority);
|
| 12 |
+
CREATE INDEX bug_status IF NOT EXISTS FOR (b:Bug) ON (b.status);
|
| 13 |
+
CREATE INDEX bug_category IF NOT EXISTS FOR (b:Bug) ON (b.category);
|
| 14 |
+
CREATE INDEX dev_expertise IF NOT EXISTS FOR (d:Developer) ON (d.expertise);
|
| 15 |
+
|
| 16 |
+
// Sample Node Creation
|
| 17 |
+
CREATE (b:Bug {
|
| 18 |
+
id: 'BUG-001',
|
| 19 |
+
title: 'Sample Bug',
|
| 20 |
+
description: 'This is a sample bug for testing',
|
| 21 |
+
priority: 'P2',
|
| 22 |
+
severity: 'Medium',
|
| 23 |
+
category: 'UI',
|
| 24 |
+
status: 'Open',
|
| 25 |
+
created_at: datetime(),
|
| 26 |
+
reporter: 'user@example.com'
|
| 27 |
+
});
|
| 28 |
+
|
| 29 |
+
CREATE (d:Developer {
|
| 30 |
+
id: 'DEV-001',
|
| 31 |
+
name: 'John Doe',
|
| 32 |
+
email: 'john@example.com',
|
| 33 |
+
expertise: ['Python', 'FastAPI', 'React'],
|
| 34 |
+
current_workload: 3,
|
| 35 |
+
max_capacity: 5
|
| 36 |
+
});
|
| 37 |
+
|
| 38 |
+
CREATE (c:Component {
|
| 39 |
+
name: 'Authentication',
|
| 40 |
+
description: 'User authentication module',
|
| 41 |
+
language: 'Python',
|
| 42 |
+
repository: 'backend'
|
| 43 |
+
});
|
| 44 |
+
|
| 45 |
+
CREATE (t:Team {
|
| 46 |
+
name: 'Backend Team',
|
| 47 |
+
description: 'Handles backend services',
|
| 48 |
+
size: 5
|
| 49 |
+
});
|
| 50 |
+
|
| 51 |
+
// Sample Relationships
|
| 52 |
+
MATCH (d:Developer {id: 'DEV-001'}), (t:Team {name: 'Backend Team'})
|
| 53 |
+
CREATE (d)-[:BELONGS_TO]->(t);
|
| 54 |
+
|
| 55 |
+
MATCH (d:Developer {id: 'DEV-001'}), (c:Component {name: 'Authentication'})
|
| 56 |
+
CREATE (d)-[:EXPERT_IN {proficiency: 0.9}]->(c);
|
rag-system/vectorstore/qdrant_client.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Qdrant Vector Database Client for RAG"""
|
| 2 |
+
import os
|
| 3 |
+
from qdrant_client import QdrantClient
|
| 4 |
+
from qdrant_client.models import Distance, VectorParams, PointStruct
|
| 5 |
+
from sentence_transformers import SentenceTransformer
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
logging.basicConfig(level=logging.INFO)
|
| 9 |
+
logger = logging.getLogger(__name__)
|
| 10 |
+
|
| 11 |
+
class QdrantVectorStore:
|
| 12 |
+
"""Vector database for semantic search in bug resolution"""
|
| 13 |
+
|
| 14 |
+
def __init__(self, collection_name: str = "bug_solutions"):
|
| 15 |
+
"""Initialize Qdrant client"""
|
| 16 |
+
self.host = os.getenv('QDRANT_HOST', 'localhost')
|
| 17 |
+
self.port = int(os.getenv('QDRANT_PORT', 6333))
|
| 18 |
+
self.collection_name = collection_name
|
| 19 |
+
self.client = None
|
| 20 |
+
self.encoder = None
|
| 21 |
+
|
| 22 |
+
def connect(self):
|
| 23 |
+
"""Establish connection to Qdrant"""
|
| 24 |
+
try:
|
| 25 |
+
# For local development, use in-memory mode
|
| 26 |
+
self.client = QdrantClient(":memory:")
|
| 27 |
+
logger.info("β
Connected to Qdrant (in-memory mode)")
|
| 28 |
+
|
| 29 |
+
# Initialize sentence transformer for embeddings
|
| 30 |
+
self.encoder = SentenceTransformer('all-MiniLM-L6-v2')
|
| 31 |
+
logger.info("β
Loaded embedding model")
|
| 32 |
+
|
| 33 |
+
return True
|
| 34 |
+
except Exception as e:
|
| 35 |
+
logger.error(f"β Qdrant connection failed: {e}")
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
def create_collection(self, vector_size: int = 384):
|
| 39 |
+
"""Create a new collection for bug solutions"""
|
| 40 |
+
try:
|
| 41 |
+
self.client.create_collection(
|
| 42 |
+
collection_name=self.collection_name,
|
| 43 |
+
vectors_config=VectorParams(
|
| 44 |
+
size=vector_size,
|
| 45 |
+
distance=Distance.COSINE
|
| 46 |
+
)
|
| 47 |
+
)
|
| 48 |
+
logger.info(f"β
Created collection: {self.collection_name}")
|
| 49 |
+
return True
|
| 50 |
+
except Exception as e:
|
| 51 |
+
logger.error(f"β Collection creation failed: {e}")
|
| 52 |
+
return False
|
| 53 |
+
|
| 54 |
+
def add_solution(self, solution_id: str, text: str, metadata: dict):
|
| 55 |
+
"""Add a bug solution to vector store"""
|
| 56 |
+
try:
|
| 57 |
+
# Generate embedding
|
| 58 |
+
vector = self.encoder.encode(text).tolist()
|
| 59 |
+
|
| 60 |
+
# Create point
|
| 61 |
+
point = PointStruct(
|
| 62 |
+
id=solution_id,
|
| 63 |
+
vector=vector,
|
| 64 |
+
payload={
|
| 65 |
+
"text": text,
|
| 66 |
+
**metadata
|
| 67 |
+
}
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
self.client.upsert(
|
| 71 |
+
collection_name=self.collection_name,
|
| 72 |
+
points=[point]
|
| 73 |
+
)
|
| 74 |
+
logger.info(f"β
Added solution: {solution_id}")
|
| 75 |
+
return True
|
| 76 |
+
except Exception as e:
|
| 77 |
+
logger.error(f"β Failed to add solution: {e}")
|
| 78 |
+
return False
|
| 79 |
+
|
| 80 |
+
def search_similar(self, query: str, limit: int = 5):
|
| 81 |
+
"""Search for similar bug solutions"""
|
| 82 |
+
try:
|
| 83 |
+
# Generate query embedding
|
| 84 |
+
query_vector = self.encoder.encode(query).tolist()
|
| 85 |
+
|
| 86 |
+
# Search
|
| 87 |
+
results = self.client.search(
|
| 88 |
+
collection_name=self.collection_name,
|
| 89 |
+
query_vector=query_vector,
|
| 90 |
+
limit=limit
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
return results
|
| 94 |
+
except Exception as e:
|
| 95 |
+
logger.error(f"β Search failed: {e}")
|
| 96 |
+
return []
|
| 97 |
+
|
| 98 |
+
# Example usage
|
| 99 |
+
if __name__ == "__main__":
|
| 100 |
+
store = QdrantVectorStore()
|
| 101 |
+
if store.connect():
|
| 102 |
+
store.create_collection()
|
| 103 |
+
print("Qdrant vector store ready!")
|