Skip to content

Commit 8a6db20

Browse files
authored
fix: skip llm parsing for oversized project affiliation files (#4319)
Signed-off-by: Yeganathan S <63534555+skwowet@users.noreply.github.com>
1 parent fd93162 commit 8a6db20

2 files changed

Lines changed: 50 additions & 17 deletions

File tree

services/apps/git_integration/src/crowdgit/services/affiliation/affiliation_service.py

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class AffiliationService(BaseService):
5252

5353
MAX_CHUNK_SIZE = 5000
5454
MAX_CONCURRENT_CHUNKS = 3
55+
MAX_FILE_SIZE_BYTES = 1_000_000
5556
FILE_PICKER_PREVIEW_MAX_CHARS = 400
5657
FILE_PICKER_BATCH_SIZE = 20
5758

@@ -74,14 +75,19 @@ class AffiliationService(BaseService):
7475
)
7576

7677
@staticmethod
77-
async def read_text_file(file_path: str) -> str:
78-
async with aiofiles.open(file_path, "rb") as f:
79-
return safe_decode(await f.read())
80-
81-
@staticmethod
82-
def compute_file_hash(content: str) -> str:
83-
"""SHA-256 hex digest of UTF-8 file content (not a Git blob SHA)."""
84-
return hashlib.sha256(content.encode("utf-8")).hexdigest()
78+
async def compute_file_hash_from_path(
79+
file_path: str, *, retain_content: bool = False
80+
) -> tuple[bytes | None, str]:
81+
"""SHA-256 of raw file bytes. Set retain_content to decode and parse under the size limit."""
82+
hasher = hashlib.sha256()
83+
chunks: list[bytes] = []
84+
async with aiofiles.open(file_path, "rb") as affiliation_file:
85+
while file_chunk := await affiliation_file.read(1024 * 1024):
86+
hasher.update(file_chunk)
87+
if retain_content:
88+
chunks.append(file_chunk)
89+
file_bytes = b"".join(chunks) if retain_content else None
90+
return file_bytes, hasher.hexdigest()
8591

8692
@classmethod
8793
def is_text_file_path(cls, relative_path: str) -> bool:
@@ -955,15 +961,42 @@ async def process_affiliations(
955961
raise AffiliationFileNotFoundError(ai_cost=ai_cost)
956962

957963
file_path_on_disk = os.path.join(batch_info.repo_path, latest_file_path)
958-
content = await self.read_text_file(file_path_on_disk)
959-
file_hash = self.compute_file_hash(content)
960-
latest_file_hash = file_hash
961-
962-
affiliations, parse_cost = await self.resolve_snapshot(
963-
registry,
964-
content,
965-
file_hash,
966-
)
964+
file_size_bytes = await aiofiles.os.path.getsize(file_path_on_disk)
965+
# Too big for llm — mark unusable and move on.
966+
if file_size_bytes > self.MAX_FILE_SIZE_BYTES:
967+
# Steady state: already gave up on this file; getsize is enough.
968+
if (
969+
registry
970+
and registry.status == AffiliationRegistryStatus.UNUSABLE.value
971+
and registry.file_path == latest_file_path
972+
and registry.file_hash
973+
):
974+
file_hash = registry.file_hash
975+
latest_file_hash = file_hash
976+
affiliations = []
977+
parse_cost = 0.0
978+
else:
979+
# First hit: hash it so the registry can mark it as unusable.
980+
_, file_hash = await self.compute_file_hash_from_path(file_path_on_disk)
981+
latest_file_hash = file_hash
982+
raise AffiliationAnalysisError(
983+
retain_file_hash=True,
984+
error_message=(
985+
f"Affiliation file {latest_file_path!r} is too large for LLM parsing "
986+
f"({file_size_bytes} bytes > {self.MAX_FILE_SIZE_BYTES} bytes)"
987+
),
988+
)
989+
else:
990+
file_bytes, file_hash = await self.compute_file_hash_from_path(
991+
file_path_on_disk, retain_content=True
992+
)
993+
content = safe_decode(file_bytes)
994+
latest_file_hash = file_hash
995+
affiliations, parse_cost = await self.resolve_snapshot(
996+
registry,
997+
content,
998+
file_hash,
999+
)
9671000
ai_cost += parse_cost
9681001

9691002
if repository.parent_repo:

services/libs/test-kit/src/factories/member.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)