Update src/main.py
Browse files- src/main.py +48 -105
src/main.py
CHANGED
|
@@ -246,25 +246,33 @@ class CodebaseIndexer:
|
|
| 246 |
if not self.config.PINECONE_API_KEY:
|
| 247 |
raise ValueError("Pinecone API key required")
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
pc.
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
)
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
|
|
|
|
|
|
|
|
|
| 268 |
|
| 269 |
return self._index
|
| 270 |
|
|
@@ -505,30 +513,21 @@ Provide JSON:
|
|
| 505 |
for c in code_context[:5]
|
| 506 |
])
|
| 507 |
|
| 508 |
-
prompt = f"""Create
|
| 509 |
|
| 510 |
-
|
| 511 |
|
| 512 |
-
|
| 513 |
{context_str}
|
| 514 |
|
| 515 |
-
Provide
|
| 516 |
{{
|
| 517 |
-
"architecture_notes": "
|
| 518 |
-
"implementation_steps": ["
|
| 519 |
-
"files_to_modify": [
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
"reason": "Why this file needs to be changed",
|
| 524 |
-
"details": "Specific changes needed (functions to add, classes to modify, etc.)"
|
| 525 |
-
}}
|
| 526 |
-
],
|
| 527 |
-
"patterns_to_follow": ["Pattern 1 from codebase", "Pattern 2 from codebase"],
|
| 528 |
-
"integration_points": ["Where this integrates with existing code"]
|
| 529 |
-
}}
|
| 530 |
-
|
| 531 |
-
Be specific about file paths, actions, and implementation details."""
|
| 532 |
|
| 533 |
response = self.client.chat.completions.create(
|
| 534 |
model=self.model,
|
|
@@ -569,56 +568,25 @@ class DeveloperLLM:
|
|
| 569 |
self._client = None
|
| 570 |
|
| 571 |
def generate_boilerplate(self, ticket_analysis: Dict, strategy: Dict, code_context: List[Dict]) -> Dict[str, str]:
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
f"// File: {c['metadata'].get('file_path', '?')}\n{c['content'][:600]}\n"
|
| 575 |
-
for c in code_context[:5]
|
| 576 |
-
])
|
| 577 |
-
|
| 578 |
-
files_to_modify = strategy.get('files_to_modify', [])
|
| 579 |
-
files_info = "\n".join([
|
| 580 |
-
f"- {f.get('path', 'unknown')}: {f.get('action', 'create')} - {f.get('reason', '')}"
|
| 581 |
-
for f in files_to_modify[:10]
|
| 582 |
-
]) if files_to_modify else "Create new files as needed"
|
| 583 |
|
| 584 |
-
|
| 585 |
-
patterns_str = "\n".join([f"- {p}" for p in patterns]) if patterns else "Follow existing codebase patterns"
|
| 586 |
-
|
| 587 |
-
prompt = f"""Generate complete, production-ready implementation code for this ticket.
|
| 588 |
-
|
| 589 |
-
Ticket Summary: {ticket_analysis.get('summary', '')}
|
| 590 |
-
Key Entities: {', '.join(ticket_analysis.get('key_entities', []))}
|
| 591 |
-
Implementation Steps:
|
| 592 |
-
{chr(10).join(f"{i+1}. {step}" for i, step in enumerate(strategy.get('implementation_steps', [])))}
|
| 593 |
-
|
| 594 |
-
Files to Create/Modify:
|
| 595 |
-
{files_info}
|
| 596 |
|
| 597 |
-
|
| 598 |
-
{
|
|
|
|
| 599 |
|
| 600 |
-
Existing
|
| 601 |
{context_str}
|
| 602 |
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
2. Follow the exact patterns, structure, and style from the existing codebase
|
| 606 |
-
3. Include all necessary imports, error handling, and type hints
|
| 607 |
-
4. Make the code production-ready and functional
|
| 608 |
-
5. For new files, include complete class/function implementations
|
| 609 |
-
6. For modifications, show the complete updated code sections
|
| 610 |
-
7. Use the same coding conventions, naming, and architecture as the existing code
|
| 611 |
-
|
| 612 |
-
Respond with JSON where keys are file paths and values are complete code:
|
| 613 |
-
{{"path/to/file.py": "complete working code here", "path/to/other.js": "complete working code here"}}
|
| 614 |
-
|
| 615 |
-
Generate actual implementation code, not TODO comments."""
|
| 616 |
|
| 617 |
response = self.client.chat.completions.create(
|
| 618 |
model=self.model,
|
| 619 |
messages=[{"role": "user", "content": prompt}],
|
| 620 |
-
temperature=0.
|
| 621 |
-
max_tokens=4000 # Allow for longer, more complete code generation
|
| 622 |
)
|
| 623 |
|
| 624 |
usage = response.usage
|
|
@@ -626,35 +594,10 @@ Generate actual implementation code, not TODO comments."""
|
|
| 626 |
|
| 627 |
content = response.choices[0].message.content
|
| 628 |
try:
|
| 629 |
-
# Clean up markdown code blocks
|
| 630 |
content = re.sub(r'^```json?\s*', '', content.strip())
|
| 631 |
content = re.sub(r'\s*```$', '', content)
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
# Post-process: Ensure code quality and completeness
|
| 635 |
-
processed_code = {}
|
| 636 |
-
for file_path, code in code_dict.items():
|
| 637 |
-
# Check if code is mostly TODOs (more than 50% TODO lines)
|
| 638 |
-
lines = code.split('\n')
|
| 639 |
-
todo_count = sum(1 for line in lines if re.search(r'TODO:', line, re.IGNORECASE))
|
| 640 |
-
total_lines = len([l for l in lines if l.strip()])
|
| 641 |
-
|
| 642 |
-
if total_lines > 0 and (todo_count / total_lines) > 0.5:
|
| 643 |
-
# Code is mostly TODOs - add a note but keep it
|
| 644 |
-
processed_code[file_path] = f"# Note: This code contains many TODOs. Please review and implement.\n\n{code}"
|
| 645 |
-
else:
|
| 646 |
-
# Code looks good, return as-is
|
| 647 |
-
processed_code[file_path] = code
|
| 648 |
-
|
| 649 |
-
return processed_code
|
| 650 |
-
except json.JSONDecodeError:
|
| 651 |
-
# If JSON parsing fails, try to extract code blocks
|
| 652 |
-
code_blocks = re.findall(r'```(?:\w+)?\n(.*?)```', content, re.DOTALL)
|
| 653 |
-
if code_blocks:
|
| 654 |
-
return {"generated_code.txt": code_blocks[0]}
|
| 655 |
-
return {"generated_code.txt": content}
|
| 656 |
-
except Exception as e:
|
| 657 |
-
print(f"Warning: Error processing generated code: {e}")
|
| 658 |
return {"generated_code.txt": content}
|
| 659 |
|
| 660 |
def explain_code_context(self, code_context: List[Dict], question: str) -> str:
|
|
|
|
| 246 |
if not self.config.PINECONE_API_KEY:
|
| 247 |
raise ValueError("Pinecone API key required")
|
| 248 |
|
| 249 |
+
try:
|
| 250 |
+
# Initialize Pinecone (v5+ syntax)
|
| 251 |
+
pc = Pinecone(api_key=self.config.PINECONE_API_KEY)
|
| 252 |
+
|
| 253 |
+
# Create index if not exists
|
| 254 |
+
existing_indexes = pc.list_indexes()
|
| 255 |
+
index_names = [idx.name for idx in existing_indexes] if hasattr(existing_indexes, '__iter__') else []
|
| 256 |
+
|
| 257 |
+
if self.config.PINECONE_INDEX_NAME not in index_names:
|
| 258 |
+
pc.create_index(
|
| 259 |
+
name=self.config.PINECONE_INDEX_NAME,
|
| 260 |
+
dimension=self.config.EMBEDDING_DIM,
|
| 261 |
+
metric="cosine",
|
| 262 |
+
spec=ServerlessSpec(
|
| 263 |
+
cloud=self.config.PINECONE_CLOUD,
|
| 264 |
+
region=self.config.PINECONE_REGION
|
| 265 |
+
)
|
| 266 |
)
|
| 267 |
+
# Wait for index to be ready
|
| 268 |
+
print(f"⏳ Waiting for index to be ready...")
|
| 269 |
+
time.sleep(10)
|
| 270 |
+
|
| 271 |
+
self._index = pc.Index(self.config.PINECONE_INDEX_NAME)
|
| 272 |
+
print(f"📂 Pinecone index ready: {self.config.PINECONE_INDEX_NAME}")
|
| 273 |
+
except Exception as e:
|
| 274 |
+
print(f"❌ Pinecone initialization error: {str(e)}")
|
| 275 |
+
raise ValueError(f"Failed to initialize Pinecone: {str(e)}")
|
| 276 |
|
| 277 |
return self._index
|
| 278 |
|
|
|
|
| 513 |
for c in code_context[:5]
|
| 514 |
])
|
| 515 |
|
| 516 |
+
prompt = f"""Create implementation strategy:
|
| 517 |
|
| 518 |
+
Analysis: {json.dumps(ticket_analysis)}
|
| 519 |
|
| 520 |
+
Code Context:
|
| 521 |
{context_str}
|
| 522 |
|
| 523 |
+
Provide JSON:
|
| 524 |
{{
|
| 525 |
+
"architecture_notes": "how it fits",
|
| 526 |
+
"implementation_steps": ["step1", "step2"],
|
| 527 |
+
"files_to_modify": [{{"path": "file", "action": "modify/create", "reason": "why"}}],
|
| 528 |
+
"patterns_to_follow": ["pattern1"],
|
| 529 |
+
"integration_points": ["point1"]
|
| 530 |
+
}}"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 531 |
|
| 532 |
response = self.client.chat.completions.create(
|
| 533 |
model=self.model,
|
|
|
|
| 568 |
self._client = None
|
| 569 |
|
| 570 |
def generate_boilerplate(self, ticket_analysis: Dict, strategy: Dict, code_context: List[Dict]) -> Dict[str, str]:
|
| 571 |
+
context_str = "\n".join([f"// {c['metadata'].get('file_path', '?')}\n{c['content'][:400]}"
|
| 572 |
+
for c in code_context[:3]])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 573 |
|
| 574 |
+
prompt = f"""Generate boilerplate code:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 575 |
|
| 576 |
+
Summary: {ticket_analysis.get('summary', '')}
|
| 577 |
+
Entities: {ticket_analysis.get('key_entities', [])}
|
| 578 |
+
Steps: {strategy.get('implementation_steps', [])}
|
| 579 |
|
| 580 |
+
Existing patterns:
|
| 581 |
{context_str}
|
| 582 |
|
| 583 |
+
Respond with JSON where keys are file paths:
|
| 584 |
+
{{"path/file.py": "# code with TODOs"}}"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
|
| 586 |
response = self.client.chat.completions.create(
|
| 587 |
model=self.model,
|
| 588 |
messages=[{"role": "user", "content": prompt}],
|
| 589 |
+
temperature=0.2
|
|
|
|
| 590 |
)
|
| 591 |
|
| 592 |
usage = response.usage
|
|
|
|
| 594 |
|
| 595 |
content = response.choices[0].message.content
|
| 596 |
try:
|
|
|
|
| 597 |
content = re.sub(r'^```json?\s*', '', content.strip())
|
| 598 |
content = re.sub(r'\s*```$', '', content)
|
| 599 |
+
return json.loads(content)
|
| 600 |
+
except:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 601 |
return {"generated_code.txt": content}
|
| 602 |
|
| 603 |
def explain_code_context(self, code_context: List[Dict], question: str) -> str:
|