tracegrpo / src /cpg /context_extractor.py
Dhruv1000's picture
Add files using upload-large-folder tool
5bfc214 verified
Raw History Blame Contribute Delete
3.36 kB
"""VULPO-style lightweight repository context extraction (spec §4.5).
For each target function, extracts (a) callee function bodies (1 hop),
(b) callee signatures only (2 hops), (c) relevant global/type declarations —
budgeted to <=4096 tokens, truncating callee bodies first, then dropping
2-hop signatures. Where repo access is unavailable, context is empty and
`has_context=False` (spec: "never fabricate context").
Token counting here is a simple whitespace-split approximation, not a real
tokenizer — good enough for budgeting (consistently conservative: real BPE
tokenizers produce >= this many tokens for code, so this under-counts and
stays safely under budget, never over).
"""
from __future__ import annotations
from dataclasses import dataclass, field
DEFAULT_TOKEN_BUDGET = 4096
def approx_token_count(text: str) -> int:
return len(text.split())
@dataclass
class RepoContext:
callee_bodies: dict[str, str] = field(default_factory=dict) # name -> source (1-hop)
callee_signatures: dict[str, str] = field(default_factory=dict) # name -> signature (2-hop)
global_decls: list[str] = field(default_factory=list)
has_context: bool = False
truncated: bool = False
def build_context(
callee_bodies: dict[str, str],
callee_signatures: dict[str, str],
global_decls: list[str],
token_budget: int = DEFAULT_TOKEN_BUDGET,
) -> RepoContext:
"""Assembles context under `token_budget`, truncating callee bodies first
(dropping lowest-priority ones entirely once budget is exhausted — not
mid-body, since a half-truncated function body is worse than none), then
dropping 2-hop signatures (spec §4.5's stated truncation order)."""
if not callee_bodies and not callee_signatures and not global_decls:
return RepoContext(has_context=False)
budget = token_budget
truncated = False
kept_decls = []
for decl in global_decls:
cost = approx_token_count(decl)
if cost <= budget:
kept_decls.append(decl)
budget -= cost
else:
truncated = True
kept_bodies: dict[str, str] = {}
for name, body in callee_bodies.items():
cost = approx_token_count(body)
if cost <= budget:
kept_bodies[name] = body
budget -= cost
else:
truncated = True # drop this callee body entirely, not mid-body
kept_sigs: dict[str, str] = {}
for name, sig in callee_signatures.items():
cost = approx_token_count(sig)
if cost <= budget:
kept_sigs[name] = sig
budget -= cost
else:
truncated = True
return RepoContext(
callee_bodies=kept_bodies,
callee_signatures=kept_sigs,
global_decls=kept_decls,
has_context=True,
truncated=truncated,
)
def render_context(ctx: RepoContext) -> str:
"""Flattens a RepoContext into the text block the prompt template embeds."""
if not ctx.has_context:
return ""
parts: list[str] = []
if ctx.global_decls:
parts.append("\n".join(ctx.global_decls))
for name, body in ctx.callee_bodies.items():
parts.append(f"// callee: {name}\n{body}")
for name, sig in ctx.callee_signatures.items():
parts.append(f"// callee signature: {name}\n{sig}")
return "\n\n".join(parts)