113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
# SPDX-FileCopyrightText: 2026 Art Sea LLC
|
|
#
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from band_aider.context.class_meta import ClassMeta
|
|
from band_aider.judgement.file_salience_check import (
|
|
FastFileSalienceCheck,
|
|
FileSalienceCheck,
|
|
FileSalienceCheckFactory,
|
|
FastFileSalienceCheckFactory,
|
|
LegacyFileSalienceCheck,
|
|
)
|
|
from band_aider.judgement.prompt_formatter import MarkdownPromptFormatterBuilder
|
|
from tests.support.completion_clients import StaticOpenAICompatibleClient
|
|
from tests.support.memory_repo_exec import MemoryRepoExec
|
|
|
|
|
|
class _StationaryFastFileSalienceCheck(FastFileSalienceCheck):
|
|
"""Exercise fast selection with fixed semantic scores and no model lookup."""
|
|
|
|
def _score(self, task: str, candidate: ClassMeta) -> float:
|
|
"""Score auth work above cache work for the fixed test task."""
|
|
|
|
del task
|
|
return 1.0 if candidate.location == "src/auth.py" else 0.0
|
|
|
|
|
|
class _UnavailableModelFileSalienceCheck(FastFileSalienceCheck):
|
|
"""Exercise the real legacy fallback after a deterministic local-model miss."""
|
|
|
|
def _score(self, task: str, candidate: ClassMeta) -> float:
|
|
"""Report the stationary unavailable-model condition without filesystem access."""
|
|
|
|
del task, candidate
|
|
raise FileNotFoundError("test model is intentionally unavailable")
|
|
|
|
|
|
def _meta(location: str, summary: str, *, relevance=None, symbols=None) -> ClassMeta:
|
|
return ClassMeta(
|
|
location=location,
|
|
summary=summary,
|
|
relevance=list(relevance or []),
|
|
relevant_symbols=dict(symbols or {}),
|
|
cdc=[(datetime(2026, 7, 14), "dev", summary)],
|
|
)
|
|
|
|
|
|
def test_file_salience_aliases_fast_implementation() -> None:
|
|
assert FileSalienceCheck is FastFileSalienceCheck
|
|
assert FileSalienceCheckFactory is FastFileSalienceCheckFactory
|
|
|
|
|
|
def test_fast_file_salience_prefers_file_most_similar_to_current_cot_task() -> None:
|
|
check = _StationaryFastFileSalienceCheck(
|
|
changes=[],
|
|
prompt="global goal",
|
|
repo_root=Path("/repo"),
|
|
repo_exec=MemoryRepoExec(),
|
|
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
|
|
)
|
|
|
|
first = _meta(
|
|
"src/auth.py",
|
|
"auth login flow",
|
|
relevance=["fix login auth failure"],
|
|
symbols={"login_user": "handles auth"},
|
|
)
|
|
second = _meta(
|
|
"src/cache.py",
|
|
"cache invalidation",
|
|
relevance=["cache cleanup"],
|
|
symbols={"flush_cache": "handles cache"},
|
|
)
|
|
|
|
assert check("fix login auth bug", first, second) is False
|
|
|
|
|
|
def test_fast_file_salience_falls_back_to_legacy_when_model2vec_unavailable() -> None:
|
|
check = _UnavailableModelFileSalienceCheck(
|
|
changes=[],
|
|
prompt="global goal",
|
|
repo_root=Path("/repo"),
|
|
repo_exec=MemoryRepoExec(),
|
|
client=StaticOpenAICompatibleClient('{"prefer_second": true, "reason": "The second file owns the task."}'),
|
|
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
|
|
)
|
|
|
|
first = _meta("src/a.py", "auth")
|
|
second = _meta("src/b.py", "cache")
|
|
|
|
assert check("task", first, second) is True
|
|
|
|
|
|
def test_legacy_file_salience_reads_repo_relative_file() -> None:
|
|
adapter = MemoryRepoExec()
|
|
repo_root = Path("/testbed")
|
|
path = repo_root / "band_aider" / "ai_client" / "foo.py"
|
|
adapter.write_text(path, "def foo():\n return 1\n")
|
|
check = LegacyFileSalienceCheck(
|
|
changes=[],
|
|
prompt="goal",
|
|
client=StaticOpenAICompatibleClient('{"prefer_second": false, "reason": "The first file owns the task."}'),
|
|
repo_root=repo_root,
|
|
repo_exec=adapter,
|
|
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
|
|
)
|
|
|
|
assert "def foo" in check._read_file("band_aider/ai_client/foo.py")
|