mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
Unified Reranker Architecture: - Add BaseReranker ABC with factory pattern - Implement 4 backends: ONNX (default), API, LiteLLM, Legacy - Add .env configuration parsing for API credentials - Migrate from sentence-transformers to optimum+onnxruntime File Watcher Module: - Add real-time file system monitoring with watchdog - Implement IncrementalIndexer for single-file updates - Add WatcherManager with signal handling and graceful shutdown - Add 'codexlens watch' CLI command - Event filtering, debouncing, and deduplication - Thread-safe design with proper resource cleanup Tests: 16 watcher tests + 5 reranker test files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
"""Tests for LiteLLMReranker (LLM-based reranking)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
from dataclasses import dataclass
|
|
|
|
import pytest
|
|
|
|
from codexlens.semantic.reranker.litellm_reranker import LiteLLMReranker
|
|
|
|
|
|
def _install_dummy_ccw_litellm(
|
|
monkeypatch: pytest.MonkeyPatch, *, responses: list[str]
|
|
) -> None:
|
|
@dataclass(frozen=True, slots=True)
|
|
class ChatMessage:
|
|
role: str
|
|
content: str
|
|
|
|
class LiteLLMClient:
|
|
def __init__(self, model: str = "default", **kwargs) -> None:
|
|
self.model = model
|
|
self.kwargs = kwargs
|
|
self._responses = list(responses)
|
|
self.calls: list[list[ChatMessage]] = []
|
|
|
|
def chat(self, messages, **kwargs):
|
|
self.calls.append(list(messages))
|
|
content = self._responses.pop(0) if self._responses else ""
|
|
return types.SimpleNamespace(content=content)
|
|
|
|
dummy = types.ModuleType("ccw_litellm")
|
|
dummy.ChatMessage = ChatMessage
|
|
dummy.LiteLLMClient = LiteLLMClient
|
|
monkeypatch.setitem(sys.modules, "ccw_litellm", dummy)
|
|
|
|
|
|
def test_score_pairs_parses_numbers_and_normalizes_scales(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
_install_dummy_ccw_litellm(monkeypatch, responses=["0.73", "7", "80"])
|
|
|
|
reranker = LiteLLMReranker(model="dummy")
|
|
scores = reranker.score_pairs([("q", "d1"), ("q", "d2"), ("q", "d3")])
|
|
assert scores == pytest.approx([0.73, 0.7, 0.8])
|
|
|
|
|
|
def test_score_pairs_parses_json_score_field(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_install_dummy_ccw_litellm(monkeypatch, responses=['{"score": 0.42}'])
|
|
|
|
reranker = LiteLLMReranker(model="dummy")
|
|
scores = reranker.score_pairs([("q", "d")])
|
|
assert scores == pytest.approx([0.42])
|
|
|
|
|
|
def test_score_pairs_uses_default_score_on_parse_failure(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
_install_dummy_ccw_litellm(monkeypatch, responses=["N/A"])
|
|
|
|
reranker = LiteLLMReranker(model="dummy", default_score=0.123)
|
|
scores = reranker.score_pairs([("q", "d")])
|
|
assert scores == pytest.approx([0.123])
|
|
|
|
|
|
def test_rate_limiting_sleeps_between_requests(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_install_dummy_ccw_litellm(monkeypatch, responses=["0.1", "0.2"])
|
|
|
|
reranker = LiteLLMReranker(model="dummy", min_interval_seconds=1.0)
|
|
|
|
import codexlens.semantic.reranker.litellm_reranker as litellm_reranker_module
|
|
|
|
sleeps: list[float] = []
|
|
times = iter([100.0, 100.0, 100.1, 100.1])
|
|
|
|
monkeypatch.setattr(litellm_reranker_module.time, "monotonic", lambda: next(times))
|
|
monkeypatch.setattr(
|
|
litellm_reranker_module.time, "sleep", lambda seconds: sleeps.append(seconds)
|
|
)
|
|
|
|
_ = reranker.score_pairs([("q", "d1"), ("q", "d2")])
|
|
assert sleeps == pytest.approx([0.9])
|
|
|