codeatlas-enterprise / backend /utils /context_builder.py
Shivam311's picture
feat: CodeAtlas Enterprise - IBM Bob Engineering Intelligence Platform
3a7842d
Raw
History Blame Contribute Delete
5.55 kB
"""
Context Builder: assembles compact, high-signal repository context for IBM Bob.
"""
from __future__ import annotations
import re
MAX_CONTEXT_CHARS = 6000
def build_architecture_context(repo_data: dict) -> str:
structure = repo_data["structure"]
technologies = repo_data["technologies"]
file_contents = repo_data.get("file_contents", {})
important_files = _get_important_files(file_contents)
ctx = f"""REPOSITORY: {repo_data['github_url']}
TOTAL FILES: {structure['total_files']} | TOTAL LINES: {structure['total_lines']}
LANGUAGES: {[l['name'] for l in technologies.get('languages', [])[:5]]}
FRAMEWORKS: {technologies.get('frameworks', [])}
DATABASES: {technologies.get('databases', [])}
DEVOPS: {technologies.get('devops', [])}
API STYLE: {technologies.get('apis', [])}
FILE TREE (key files):
{_format_file_tree(_rank_files(structure['files'])[:100])}
KEY FILE CONTENTS:
{important_files}"""
return ctx[:MAX_CONTEXT_CHARS]
def build_qa_context(repo_data: dict, question: str) -> str:
file_contents = repo_data.get("file_contents", {})
technologies = repo_data["technologies"]
relevant_files = _find_relevant_files(file_contents, question)
ctx = f"""TECH STACK: {technologies.get('frameworks', [])} | {technologies.get('databases', [])}
LANGUAGES: {[l['name'] for l in technologies.get('languages', [])[:3]]}
RELEVANT CODE CONTEXT:
{relevant_files}"""
return ctx[:MAX_CONTEXT_CHARS]
def build_impact_context(repo_data: dict, target_file: str) -> str:
file_contents = repo_data.get("file_contents", {})
structure = repo_data["structure"]
target_content = file_contents.get(target_file, "File content not available")
importers = _find_importers(file_contents, target_file)
ctx = f"""REPOSITORY STATS: {structure['total_files']} files
TARGET FILE: {target_file}
TARGET FILE CONTENT:
```
{target_content[:2500]}
```
FILES THAT IMPORT/USE THIS:
{importers[:2200]}
ALL FILES IN REPO:
{_format_file_tree(_rank_files(structure['files'])[:80])}"""
return ctx[:MAX_CONTEXT_CHARS]
def build_docs_context(repo_data: dict) -> str:
"""Build a compact docs context so live Bob generation returns quickly."""
structure = repo_data["structure"]
technologies = repo_data["technologies"]
deps = repo_data.get("dependencies", {})
ranked_files = _rank_files(structure["files"])
ctx = f"""REPOSITORY: {repo_data['github_url']}
TOTAL FILES: {structure['total_files']} | TOTAL LINES: {structure['total_lines']}
LANGUAGES: {[l['name'] for l in technologies.get('languages', [])[:5]]}
FRAMEWORKS: {technologies.get('frameworks', [])}
DATABASES: {technologies.get('databases', [])}
DEVOPS: {technologies.get('devops', [])}
API STYLE: {technologies.get('apis', [])}
IMPORTANT FILES:
{_format_file_tree(ranked_files[:55])}
DEPENDENCIES:
NPM: {deps.get('npm', [])[:20]}
PIP: {deps.get('pip', [])[:20]}
GO: {deps.get('go', [])[:12]}
CARGO: {deps.get('cargo', [])[:12]}"""
return ctx[:3500]
def _rank_files(files: list[dict]) -> list[dict]:
important_tokens = ("main", "app", "server", "router", "route", "model", "schema", "auth", "security", "config", "package", "requirements")
def score(file_info: dict) -> tuple[int, int]:
path = file_info.get("path", "").lower()
token_score = sum(5 for token in important_tokens if token in path)
depth_score = max(0, 4 - path.count("/"))
return (token_score + depth_score, -len(path))
return sorted(files, key=score, reverse=True)
def _get_important_files(file_contents: dict) -> str:
priority_patterns = [
"main.py",
"app.py",
"index.js",
"server.js",
"app.js",
"main.ts",
"main.jsx",
"index.ts",
"package.json",
"requirements.txt",
"App.jsx",
"App.tsx",
"routes.py",
"models.py",
"schema.prisma",
"pyproject.toml",
]
result = ""
for name in priority_patterns:
for path, content in file_contents.items():
if path.endswith(name) and len(result) < 3600:
result += f"\n--- {path} ---\n{content[:700]}\n"
return result
def _format_file_tree(files: list[dict]) -> str:
return "\n".join(file_info.get("path", "") for file_info in files)
def _find_relevant_files(file_contents: dict, question: str) -> str:
keywords = [token for token in re.split(r"\W+", question.lower()) if len(token) >= 3]
scored = []
for path, content in file_contents.items():
combined = f"{path}\n{content}".lower()
score = sum(1 for keyword in keywords if keyword in combined)
if score:
scored.append((score, path, content))
scored.sort(key=lambda item: (item[0], -len(item[1])), reverse=True)
result = ""
for _, path, content in scored[:5]:
result += f"\n--- {path} ---\n{content[:800]}\n"
return result or _get_important_files(file_contents)
def _find_importers(file_contents: dict, target_file: str) -> str:
filename = target_file.split("/")[-1]
stem = filename.rsplit(".", 1)[0]
module_guess = target_file.rsplit(".", 1)[0].replace("/", ".")
result = ""
for path, content in file_contents.items():
if path == target_file:
continue
if target_file in content or filename in content or stem in content or module_guess in content:
result += f"\n--- {path} (references target) ---\n{content[:450]}\n"
return result or "No direct textual importers found in scanned content."