Spaces:
Sleeping
Sleeping
File size: 7,447 Bytes
7e2f74d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | import json
import logging
from typing import List, Dict, Any, Tuple
from memory.embedding_service import EmbeddingService
from memory.vector_store import VectorStore
logger = logging.getLogger("knowledge_index")
class KnowledgeIndexBuilder:
def __init__(self, embedding_service: EmbeddingService, vector_store: VectorStore):
self.embedder = embedding_service
self.store = vector_store
def chunk_text(self, text: str, chunk_size: int = 1000, overlap: int = 100) -> List[str]:
"""Simple sliding window text splitter."""
if not text:
return []
chunks = []
start = 0
text_len = len(text)
while start < text_len:
end = min(start + chunk_size, text_len)
chunks.append(text[start:end])
start += chunk_size - overlap
# Prevent infinite loop if overlap >= chunk_size
if chunk_size - overlap <= 0:
break
return chunks
def build_index(
self,
repo_id: str,
profile: Dict[str, Any],
summary: Dict[str, Any],
graph: Dict[str, Any],
report: str,
flat_files: List[Dict[str, Any]]
):
"""Builds and indexes a repository's code, structure, and profile metadata into ChromaDB."""
logger.info(f"Starting index build for repository: {repo_id}")
# Clear existing collection if any
self.store.delete_collection(repo_id)
documents: List[str] = []
metadatas: List[Dict[str, Any]] = []
ids: List[str] = []
# Helper to generate unique IDs
def add_chunk(content: str, metadata: Dict[str, Any], prefix: str):
chunk_id = f"{prefix}_{len(documents)}"
documents.append(content)
metadatas.append(metadata)
ids.append(chunk_id)
# 1. Index the Repository Intelligence Report (Markdown)
report_chunks = self.chunk_text(report, chunk_size=1000, overlap=100)
for i, chunk in enumerate(report_chunks):
add_chunk(
content=chunk,
metadata={"category": "report", "chunk_index": i},
prefix="report"
)
# 2. Index the Summary details
elevator_pitch = summary.get("elevator_pitch", "")
if elevator_pitch:
add_chunk(
content=f"Project Elevator Pitch:\n{elevator_pitch}",
metadata={"category": "summary", "subcategory": "elevator_pitch"},
prefix="summary_pitch"
)
for feat in summary.get("core_features", []):
add_chunk(
content=f"Core Feature: {feat}",
metadata={"category": "summary", "subcategory": "core_feature"},
prefix="summary_feat"
)
for start_point in summary.get("developer_start_points", []):
add_chunk(
content=f"Developer Starting Point File: {start_point}",
metadata={"category": "summary", "subcategory": "developer_start_point"},
prefix="summary_start"
)
# 3. Index Profile Metadata (Architecture, APIs, Auth, Dependencies)
arch_pattern = profile.get("architecture_pattern", "")
if arch_pattern:
add_chunk(
content=f"Architecture Pattern: {arch_pattern}",
metadata={"category": "architecture", "pattern": arch_pattern},
prefix="profile_arch"
)
for auth_method in profile.get("authentication_methods", []):
add_chunk(
content=f"Authentication / Security Method: {auth_method}",
metadata={"category": "authentication", "method": auth_method},
prefix="profile_auth"
)
for endpoint in profile.get("api_endpoints", []):
add_chunk(
content=f"API Endpoint / Route: {endpoint}",
metadata={"category": "api", "endpoint": endpoint},
prefix="profile_api"
)
for dep in profile.get("dependencies", []):
add_chunk(
content=f"Package Dependency: {dep}",
metadata={"category": "dependency", "dependency": dep},
prefix="profile_dep"
)
# 4. Index Business Flows & Concepts from Graph
for flow in graph.get("business_flows", []):
flow_name = flow.get("flow_name", "")
flow_desc = flow.get("description", "")
flow_steps = ", ".join(flow.get("steps", []))
add_chunk(
content=f"Business Flow: {flow_name}\nDescription: {flow_desc}\nSteps: {flow_steps}",
metadata={"category": "business_flow", "flow_name": flow_name},
prefix="graph_flow"
)
for concept in graph.get("concepts", []):
concept_name = concept.get("name", "")
concept_desc = concept.get("description", "")
concept_files = ", ".join(concept.get("files", []))
add_chunk(
content=f"Core Concept: {concept_name}\nDescription: {concept_desc}\nFiles: {concept_files}",
metadata={"category": "concept", "concept_name": concept_name},
prefix="graph_concept"
)
# 5. Index Source Code Files Content
for f in flat_files:
file_path = f.get("path", "")
content = f.get("content", "")
file_size = f.get("size", 0)
# Skip massive files to avoid cluttering vector space
if file_size > 100 * 1024 or not content:
continue
file_chunks = self.chunk_text(content, chunk_size=1000, overlap=100)
for j, chunk in enumerate(file_chunks):
# Include path info inside the document text to maintain retrieval association
chunk_content = f"File: {file_path} (Chunk {j+1}/{len(file_chunks)})\n\n{chunk}"
add_chunk(
content=chunk_content,
metadata={"category": "file", "path": file_path, "chunk_index": j},
prefix="file_chunk"
)
if not documents:
logger.info("No documents found to index.")
return
# 6. Generate Embeddings in Batches of 50 to respect API rate limits and connection pooling
batch_size = 50
logger.info(f"Generating embeddings for {len(documents)} document chunks in batches of {batch_size}...")
all_embeddings: List[List[float]] = []
for start_idx in range(0, len(documents), batch_size):
end_idx = min(start_idx + batch_size, len(documents))
batch_docs = documents[start_idx:end_idx]
batch_embeddings = self.embedder.embed_texts(batch_docs)
all_embeddings.extend(batch_embeddings)
# 7. Write to Vector Store
self.store.add_documents(
repo_id=repo_id,
documents=documents,
metadatas=metadatas,
ids=ids,
embeddings=all_embeddings
)
logger.info(f"Vector database indexing complete. Indexed {len(documents)} chunks.")
|