fix(tools): score_project_match strips 'project' filler + uses title for SequenceMatcher

CI surfaced three issues:
- 'famous supply project' didn't substring-match 'Famous-Supply Work topics'
  because the trailing filler word 'project' blocked the substring tier.
  Strip {project, projects} from the query before the substring check.
- SequenceMatcher fallback against `combined` (title + description +
  summary) diluted ratios to ~0.5 for plausible matches. Use title
  directly; the 0.70 tier already handles description/summary mentions.
- Test patches used patch.object on a consumer module where
  list_projects is imported locally — patch the source module instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 20:30:37 -04:00
parent 6de855e226
commit a551f52682
2 changed files with 25 additions and 8 deletions
+4 -2
View File
@@ -20,7 +20,9 @@ async def test_search_projects_tool_returns_success_true():
fake_projects = [_project(5, "Famous-Supply Work topics", "AT&T fiber circuit")]
with patch.object(projects_tool, "list_projects", new=AsyncMock(return_value=fake_projects)):
# `list_projects` is imported locally inside search_projects_tool, so we
# patch the source module rather than the consumer.
with patch("fabledassistant.services.projects.list_projects", new=AsyncMock(return_value=fake_projects)):
result = await projects_tool.search_projects_tool(
user_id=1, arguments={"query": "famous supply"},
)
@@ -101,7 +103,7 @@ async def test_search_projects_tool_ranks_substring_match_above_others():
_project(13, "ImageRepo", auto_summary="self-hosted Flask app"),
]
with patch.object(projects_tool, "list_projects", new=AsyncMock(return_value=fake_projects)):
with patch("fabledassistant.services.projects.list_projects", new=AsyncMock(return_value=fake_projects)):
result = await projects_tool.search_projects_tool(
user_id=1, arguments={"query": "famous supply project"},
)