fix(clustering): route embeddings through LiteLLM instead of Ollama directly
The old code called Ollama's /api/embeddings one task at a time, which caused silent fallback to project-based grouping when host.docker.internal:11434 was unreachable from the ml-serving container. - Switch to LiteLLM /embeddings (model alias "embedder") as primary path - Batch all task contents in one request instead of N serial calls - Fall back to Ollama /api/embed (updated to current API) when LITELLM_URL is absent - Update tests to mock _embed_batch instead of the removed _embed Fixes #123 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from ml.agents.clustering import cluster_tasks, Cluster, _greedy_cluster, _cosine
|
||||
from ml.agents.clustering import cluster_tasks, Cluster, _greedy_cluster, _cosine, _embed_batch
|
||||
|
||||
|
||||
# ── helpers ──────────────────────────────────────────────────────────────────
|
||||
@@ -89,8 +89,8 @@ class TestClusterTasks:
|
||||
result = cluster_tasks([])
|
||||
assert result == []
|
||||
|
||||
def test_fallback_when_ollama_unavailable(self):
|
||||
with patch("ml.agents.clustering._embed", return_value=None):
|
||||
def test_fallback_when_embed_unavailable(self):
|
||||
with patch("ml.agents.clustering._embed_batch", return_value=None):
|
||||
tasks = [_task("A", "p1"), _task("B", "p2"), _task("C", "p1")]
|
||||
clusters = cluster_tasks(tasks)
|
||||
assert len(clusters) == 2
|
||||
@@ -98,7 +98,7 @@ class TestClusterTasks:
|
||||
assert "p1" in labels and "p2" in labels
|
||||
|
||||
def test_fallback_groups_by_project(self):
|
||||
with patch("ml.agents.clustering._embed", return_value=None):
|
||||
with patch("ml.agents.clustering._embed_batch", return_value=None):
|
||||
tasks = [_task("A", "work")] * 3 + [_task("B", "home")] * 2
|
||||
clusters = cluster_tasks(tasks)
|
||||
by_label = {c.label: c.task_count for c in clusters}
|
||||
@@ -107,7 +107,7 @@ class TestClusterTasks:
|
||||
|
||||
def test_tasks_without_content_go_to_other(self):
|
||||
v = [1.0, 0.0]
|
||||
with patch("ml.agents.clustering._embed", return_value=v):
|
||||
with patch("ml.agents.clustering._embed_batch", return_value=[v]):
|
||||
tasks = [_task("Has content"), {"is_overdue": False}]
|
||||
clusters = cluster_tasks(tasks)
|
||||
labels = {c.label for c in clusters}
|
||||
@@ -116,8 +116,8 @@ class TestClusterTasks:
|
||||
def test_semantic_clustering_groups_similar(self):
|
||||
v_work = [1.0, 0.0, 0.0]
|
||||
v_home = [0.0, 1.0, 0.0]
|
||||
side_effects = [v_work, v_work, v_home, v_home]
|
||||
with patch("ml.agents.clustering._embed", side_effect=side_effects):
|
||||
batch_result = [v_work, v_work, v_home, v_home]
|
||||
with patch("ml.agents.clustering._embed_batch", return_value=batch_result):
|
||||
tasks = [
|
||||
_task("Write report"),
|
||||
_task("Review PR"),
|
||||
|
||||
Reference in New Issue
Block a user