From c34d7350006b433751c9cd04f5ae90e8d8147bc3 Mon Sep 17 00:00:00 2001 From: Devon Kelley Date: Thu, 21 May 2026 07:50:06 -0700 Subject: [PATCH] fix: pass candidate_model_ids to decide() so router respects developer-registered paths Co-Authored-By: Claude Opus 4.7 (1M context) --- kalibr/intelligence.py | 23 +++++++++++++++++++---- kalibr/router.py | 7 ++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/kalibr/intelligence.py b/kalibr/intelligence.py index 596e812..644feaa 100644 --- a/kalibr/intelligence.py +++ b/kalibr/intelligence.py @@ -36,7 +36,7 @@ import threading import time import uuid as _uuid -from typing import Any, Optional +from typing import Any, List, Optional import httpx @@ -428,6 +428,7 @@ def decide( goal: str, task_risk_level: str = "low", pipeline_id: str | None = None, + candidate_model_ids: Optional[List[str]] = None, ) -> dict[str, Any]: """Get routing decision for a goal. @@ -468,6 +469,8 @@ def decide( } if pipeline_id is not None: payload["pipeline_id"] = pipeline_id + if candidate_model_ids is not None: + payload["candidate_model_ids"] = candidate_model_ids response = self._request( "POST", @@ -813,6 +816,7 @@ def decide( task_risk_level: str = "low", tenant_id: str | None = None, pipeline_id: str | None = None, + candidate_model_ids: Optional[List[str]] = None, ) -> dict[str, Any]: """Get routing decision for a goal. @@ -845,7 +849,12 @@ def decide( else: effective_tenant = _get_intelligence_client().tenant_id - cache_key = (goal, task_risk_level, effective_tenant) + cache_key = ( + goal, + task_risk_level, + effective_tenant, + tuple(candidate_model_ids) if candidate_model_ids is not None else None, + ) with _decide_cache_lock: cached = _decide_cache.get(cache_key) @@ -859,10 +868,16 @@ def decide( if tenant_id: with KalibrIntelligence(tenant_id=tenant_id) as client: - decision = client.decide(goal, task_risk_level, pipeline_id=pipeline_id) + decision = client.decide( + goal, task_risk_level, + pipeline_id=pipeline_id, + candidate_model_ids=candidate_model_ids, + ) else: decision = _get_intelligence_client().decide( - goal, task_risk_level, pipeline_id=pipeline_id, + goal, task_risk_level, + pipeline_id=pipeline_id, + candidate_model_ids=candidate_model_ids, ) with _decide_cache_lock: diff --git a/kalibr/router.py b/kalibr/router.py index 2c16e8c..77d53dc 100644 --- a/kalibr/router.py +++ b/kalibr/router.py @@ -750,7 +750,12 @@ def completion( self._last_decision = {"model_id": model_id, "forced": True} else: try: - decision = decide(goal=self.goal, pipeline_id=pipeline_id) + candidate_models = [p["model"] for p in self._paths if p.get("model")] + decision = decide( + goal=self.goal, + pipeline_id=pipeline_id, + candidate_model_ids=candidate_models, + ) model_id = decision.get("model_id") or self._paths[0]["model"] tool_id = decision.get("tool_id") params = decision.get("params") or {}