fix(dispatch): fail explicitly when no enabled model qualifies
`pickModelForAgent` used to fall back to the CEO's pair when the
auto-strategy scoring returned no winner, and then silently route
the run on that pair. Three distinct problems rendered invisible by
that fallback: a missing capability row for a deployed model, a
mis-inferred task profile, a misconfigured model pool. Cost
attribution became meaningless — the agent ran on the CEO's model,
not its own tier — and operators had no signal to fix the root
cause.
The picker now returns a `ModelPickResult` tagged union. Success
yields the dispatch-ready assignment; failure yields a
`NO_QUALIFYING_CANDIDATE` reason with a human-readable message
naming the agent, the required domain, and the required tier.
Heartbeat maps the failure to a new `model_pick_failed` skip
reason, logs an activity row so it shows up in the company audit
trail, and defers. Task-runner returns `fail("MODEL_PICK_FAILED",
...)` so the UI surfaces the message directly. `getCeoAssignment`
drops out of `PickModelDeps` entirely — no caller needs a silent
CEO path any more, so the dep surface shrinks and the tests do
too. The test suite gains a regression assertion for the explicit
failure case.
This commit is contained in:
1 parent
76c2f085b6
commit
cdf783c0b8
4 files changed
+111
-70
No files matched your search
@@ -47,18 +47,16 @@ export function createTaskRunner(
|
||||
if (agent.companyId !== task.companyId) {
|
||||
return fail("AGENT_COMPANY_MISMATCH", "The assigned agent does not belong to the task company.");
|
||||
}
|
||||
const assignment = pickModelForAgent(agent, {
|
||||
const pick = pickModelForAgent(agent, {
|
||||
getConnector: (id) => db.getConnector(id),
|
||||
getDisabledModels: (id) => db.getDisabledModels(agent.companyId, id),
|
||||
getCeoAssignment: (cid) => {
|
||||
const ceo = db.getCeoForCompany(cid);
|
||||
if (!ceo) return null;
|
||||
const full = db.getAgent(ceo.id);
|
||||
return { connectorId: full.connectorId, modelId: full.model ?? null };
|
||||
},
|
||||
listModelCapabilities: () => db.modelCapabilities.listModelCapabilities(),
|
||||
readConnectors: () => db.connectorsCrud.readConnectors(),
|
||||
});
|
||||
if (!pick.ok) {
|
||||
return fail("MODEL_PICK_FAILED", pick.message);
|
||||
}
|
||||
const assignment = pick.assignment;
|
||||
const connector = db.getConnector(assignment.connectorId);
|
||||
const effectiveAgentForEligibility: AgentRecord = { ...agent, model: assignment.modelId };
|
||||
const eligibilityFailure = getRunEligibilityFailure(effectiveAgentForEligibility, connector, task.projectId ?? null);
|
||||
|
||||
Reference in new issue
Block a user