Skip to content

Commit f0f339c

Browse files
authored
Merge pull request #291 from DevanshuNEU/feat/context-assembler-ope-172
feat: context assembly -- per-task context packaging via MCP (OPE-172)
2 parents 487c61d + 24323cb commit f0f339c

8 files changed

Lines changed: 459 additions & 0 deletions

File tree

backend/dependencies.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from services.dependency_analyzer import DependencyAnalyzer
1212
from services.style_analyzer import StyleAnalyzer
1313
from services.dna_extractor import DNAExtractor
14+
from services.context_assembler import ContextAssembler
1415
from services.rate_limiter import RateLimiter, APIKeyManager
1516
from services.supabase_service import get_supabase_service
1617
from services.input_validator import CostController
@@ -25,6 +26,7 @@
2526
dependency_analyzer = DependencyAnalyzer()
2627
style_analyzer = StyleAnalyzer()
2728
dna_extractor = DNAExtractor()
29+
context_assembler = ContextAssembler()
2830

2931
# Rate limiting and API key management
3032
rate_limiter = RateLimiter(redis_client=cache.redis if cache.redis else None)

backend/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from routes.github import router as github_router
3636
from routes.feedback import router as feedback_router
3737
from routes.admin import router as admin_router
38+
from routes.context import router as context_router
3839
from routes.ws_playground import websocket_playground_index
3940
from routes.ws_repos import websocket_repo_indexing
4041

@@ -106,6 +107,7 @@ async def dispatch(self, request: Request, call_next):
106107
app.include_router(github_router, prefix=API_PREFIX)
107108
app.include_router(feedback_router, prefix=API_PREFIX)
108109
app.include_router(admin_router, prefix=API_PREFIX)
110+
app.include_router(context_router, prefix=API_PREFIX)
109111

110112
# WebSocket endpoints (versioned)
111113
app.add_api_websocket_route(f"{API_PREFIX}/ws/index/{{repo_id}}", websocket_index)

backend/routes/context.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""Context assembly endpoint.
2+
3+
Provides per-task context packaging via POST /api/v1/context/assemble.
4+
Uses semantic search + dependency graph + project rules to build a
5+
minimal, precise context package for AI coding assistants.
6+
"""
7+
import time
8+
from typing import Any
9+
10+
from fastapi import APIRouter, Depends, HTTPException
11+
from pydantic import BaseModel, Field
12+
13+
from dependencies import verify_repo_access
14+
from middleware.auth import AuthContext, require_auth
15+
from services.observability import (
16+
add_breadcrumb,
17+
capture_exception,
18+
logger,
19+
metrics,
20+
set_operation_context,
21+
)
22+
23+
router = APIRouter(tags=["context"])
24+
25+
26+
class AssembleRequest(BaseModel):
27+
task: str = Field(..., min_length=3, max_length=1000)
28+
repo_id: str
29+
token_budget: int = Field(default=1500, ge=100, le=10000)
30+
31+
32+
@router.post("/context/assemble")
33+
async def assemble_context(
34+
request: AssembleRequest,
35+
auth: AuthContext = Depends(require_auth),
36+
) -> dict[str, Any]:
37+
"""Assemble task-specific context from semantic search + deps + rules.
38+
39+
Returns a markdown context package sized to fit within token_budget,
40+
containing only the files, dependencies, and project rules relevant
41+
to the given task description.
42+
"""
43+
set_operation_context(
44+
"context_assemble",
45+
user_id=auth.user_id,
46+
repo_id=request.repo_id,
47+
)
48+
add_breadcrumb("Context assembly requested", category="context", repo_id=request.repo_id)
49+
50+
verify_repo_access(request.repo_id, auth.user_id)
51+
52+
from dependencies import context_assembler
53+
54+
start = time.time()
55+
try:
56+
result = await context_assembler.assemble(
57+
task=request.task,
58+
repo_id=request.repo_id,
59+
user_id=auth.user_id,
60+
token_budget=request.token_budget,
61+
)
62+
63+
elapsed = time.time() - start
64+
logger.info(
65+
"Context assembled",
66+
repo_id=request.repo_id,
67+
files=result["files_found"],
68+
tokens=result["tokens_used"],
69+
budget=request.token_budget,
70+
duration_ms=round(elapsed * 1000),
71+
)
72+
metrics.timing("context_assemble_ms", elapsed * 1000)
73+
74+
return result
75+
76+
except HTTPException:
77+
raise
78+
except Exception as exc:
79+
capture_exception(exc, operation="context_assemble", repo_id=request.repo_id)
80+
logger.error("Context assembly failed: %s", exc)
81+
raise HTTPException(status_code=500, detail="Context assembly failed")

0 commit comments

Comments
 (0)