mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-15 04:32:23 +02:00
159 lines
4.9 KiB
Python
159 lines
4.9 KiB
Python
"""Native semantic skill ranking with lexical and capability-safe fallback."""
|
|
|
|
import pytest
|
|
|
|
from services.memory.skills import SkillsManager
|
|
from src import skill_index, tool_index
|
|
|
|
|
|
class _IntentLane:
|
|
name = "fastembed"
|
|
fingerprint = "intent-lane-v1"
|
|
|
|
def __init__(self):
|
|
self.document_encode_calls = 0
|
|
|
|
def encode(self, texts):
|
|
vectors = []
|
|
if len(texts) > 1:
|
|
self.document_encode_calls += 1
|
|
for text in texts:
|
|
lowered = text.lower()
|
|
if "orchestrate attendees" in lowered or "coordinate calendars" in lowered:
|
|
vectors.append([1.0, 0.0])
|
|
else:
|
|
vectors.append([0.0, 1.0])
|
|
return vectors
|
|
|
|
|
|
class _ReadyIndex:
|
|
def __init__(self, lane):
|
|
self.embedding_lanes = (lane,)
|
|
|
|
|
|
def _skill(name, description, *, requires=None, fallback_for=None):
|
|
return {
|
|
"name": name,
|
|
"description": description,
|
|
"when_to_use": "",
|
|
"tags": [],
|
|
"procedure": [],
|
|
"requires_toolsets": list(requires or []),
|
|
"fallback_for_toolsets": list(fallback_for or []),
|
|
"status": "published",
|
|
"confidence": 0.8,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def semantic_lane(monkeypatch):
|
|
lane = _IntentLane()
|
|
index = _ReadyIndex(lane)
|
|
skill_index.reset_skill_index_cache()
|
|
monkeypatch.setattr(tool_index, "get_ready_tool_index", lambda: index)
|
|
yield lane
|
|
skill_index.reset_skill_index_cache()
|
|
|
|
|
|
def test_semantic_ranking_recovers_paraphrase_with_no_lexical_overlap(tmp_path, semantic_lane):
|
|
manager = SkillsManager(str(tmp_path))
|
|
skills = [
|
|
_skill("meeting-coordination", "coordinate calendars and confirm a viable time"),
|
|
_skill("video-inspection", "extract representative frames from a recording"),
|
|
]
|
|
|
|
results = manager.get_relevant_skills(
|
|
"orchestrate attendees without double booking anyone",
|
|
skills=skills,
|
|
threshold=0.25,
|
|
)
|
|
|
|
assert [skill["name"] for skill in results] == ["meeting-coordination"]
|
|
|
|
|
|
def test_semantic_corpus_vectors_are_cached(tmp_path, semantic_lane):
|
|
manager = SkillsManager(str(tmp_path))
|
|
skills = [
|
|
_skill("meeting-coordination", "coordinate calendars and confirm a viable time"),
|
|
_skill("video-inspection", "extract representative frames from a recording"),
|
|
]
|
|
|
|
manager.get_relevant_skills("orchestrate attendees for a launch", skills=skills)
|
|
manager.get_relevant_skills("orchestrate attendees for an offsite", skills=skills)
|
|
|
|
assert semantic_lane.document_encode_calls == 1
|
|
|
|
|
|
def test_semantic_match_cannot_bypass_required_tool_capability(tmp_path, semantic_lane):
|
|
manager = SkillsManager(str(tmp_path))
|
|
skills = [
|
|
_skill(
|
|
"meeting-coordination",
|
|
"coordinate calendars and confirm a viable time",
|
|
requires=["manage_calendar"],
|
|
),
|
|
]
|
|
|
|
unavailable = manager.get_relevant_skills(
|
|
"orchestrate attendees without double booking anyone",
|
|
skills=skills,
|
|
available_toolsets={"web_search"},
|
|
)
|
|
available = manager.get_relevant_skills(
|
|
"orchestrate attendees without double booking anyone",
|
|
skills=skills,
|
|
available_toolsets={"manage_calendar"},
|
|
)
|
|
|
|
assert unavailable == []
|
|
assert [skill["name"] for skill in available] == ["meeting-coordination"]
|
|
|
|
|
|
def test_fallback_skill_is_hidden_when_native_tool_is_available(tmp_path, semantic_lane):
|
|
manager = SkillsManager(str(tmp_path))
|
|
skills = [
|
|
_skill(
|
|
"manual-meeting-fallback",
|
|
"coordinate calendars and confirm a viable time",
|
|
fallback_for=["manage_calendar"],
|
|
),
|
|
]
|
|
|
|
assert manager.get_relevant_skills(
|
|
"orchestrate attendees without double booking anyone",
|
|
skills=skills,
|
|
available_toolsets={"manage_calendar"},
|
|
) == []
|
|
|
|
|
|
def test_semantic_ranking_can_be_disabled_without_breaking_lexical_fallback(
|
|
tmp_path, semantic_lane, monkeypatch
|
|
):
|
|
manager = SkillsManager(str(tmp_path))
|
|
skills = [
|
|
_skill("meeting-coordination", "coordinate calendars and confirm a viable time"),
|
|
]
|
|
monkeypatch.setenv("ODYSSEUS_SKILL_SEMANTIC_RETRIEVAL", "0")
|
|
|
|
semantic_only = manager.get_relevant_skills(
|
|
"orchestrate attendees without double booking anyone",
|
|
skills=skills,
|
|
)
|
|
lexical = manager.get_relevant_skills(
|
|
"coordinate calendars and confirm a viable time",
|
|
skills=skills,
|
|
)
|
|
|
|
assert semantic_only == []
|
|
assert [skill["name"] for skill in lexical] == ["meeting-coordination"]
|
|
|
|
|
|
def test_no_ready_embedding_index_keeps_existing_lexical_behavior(tmp_path, monkeypatch):
|
|
manager = SkillsManager(str(tmp_path))
|
|
skills = [_skill("git-helper", "version control procedure")]
|
|
monkeypatch.setattr(tool_index, "get_ready_tool_index", lambda: None)
|
|
|
|
results = manager.get_relevant_skills("version control procedure", skills=skills)
|
|
|
|
assert [skill["name"] for skill in results] == ["git-helper"]
|