crossProviderSwitchRefusal function
Why a cross-provider pick is refused UP FRONT (G1, E1/E3) — null when the whole-triple switch may proceed.
targetResolvablefalse → E1: the entry's provider disappeared between listing and picking; the refusal names the provider and the reconfigure command, and no partial triple is applied.- Legacy wiring (no roles resolver) with
targetKeyResolvedfalse → E3: the switch would bake an empty key and the request would leave credential-less; the refusal names the TARGET provider's key slot. Roles mode is exempt: the chain resolves keys per entry at request time and skips with the correct-slot hint, so a switch is allowed.
Implementation
ModelCoherenceRefusal? crossProviderSwitchRefusal({
required String providerName,
required bool targetResolvable,
required bool rolesMode,
required bool targetKeyResolved,
required String? targetKeySlot,
}) {
if (!targetResolvable) {
return ModelCoherenceRefusal(
problem:
'provider "$providerName" is not configured '
'(removed since the list was built?)',
fields: const ['provider'],
repair: '/provider add — or pick a configured provider with /provider',
);
}
if (!rolesMode && !targetKeyResolved) {
return ModelCoherenceRefusal(
problem: 'no key resolved for provider "$providerName"',
fields: const ['key'],
repair:
'/key set ${targetKeySlot ?? '<ENDPOINT_KEY_NAME>'} <value> and '
'retry /model',
);
}
return null;
}