Stop guessing a model's kind from its name at Google, Mistral, and xAI

This commit is contained in:
Thorsten Sommer 2026-09-19 17:20:17 +02:00
parent 125e555447
commit dc060ef5bc
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
3 changed files with 30 additions and 20 deletions

View File

@ -172,11 +172,15 @@ public class ProviderGoogle() : BaseProvider(LLMProviders.GOOGLE, new Uri("https
// Asking what a model is made for, rather than only ruling out the embedding ones. // Asking what a model is made for, rather than only ruling out the embedding ones.
// Google names everything after the chat model it grew out of, so the catalog is // Google names everything after the chat model it grew out of, so the catalog is
// full of names which look like something to talk to and are not: the image models, // full of names which look like something to talk to and are not: the image models,
// and the computer use model whose API refuses a request without its tool. // the computer use model whose API refuses a request without its tool, and the live
// line which wants a connection held open in both directions.
// //
..result.Models.Where(model => // The question used to be asked of names beginning with "gemini" alone, and that
model.Id.StartsWith("gemini-", StringComparison.OrdinalIgnoreCase) && // cost the two Gemma models Google serves on this very route. What the prefix kept
model.IsChatModel(this.Provider)) // out besides them -- Lyria, Imagen, Veo, the research and coding agents, AQA --
// is kept out by a rule now, where the reason is written down.
//
..result.Models.Where(model => model.IsChatModel(this.Provider))
.Select(this.WithDisplayNameFallback) .Select(this.WithDisplayNameFallback)
] ]
}; };

View File

@ -93,12 +93,13 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, new U
{ {
Models = Models =
[ [
// Codestral is a fill-in-the-middle model, which we cannot use for chats. That is //
// specific to Mistral's catalog, which is why it is not part of the shared model // Codestral is a fill-in-the-middle model, which we cannot use for chats. Its own
// kind detection: // family says so now, bound to this provider, so the word "code" no longer has to
..modelResponse.Models.Where(n => // be tested for here -- and testing for it never reached mistral-code-fim-latest,
!n.Id.StartsWith("code", StringComparison.OrdinalIgnoreCase) && // which does the same job under a name that begins differently.
n.IsChatModel(this.Provider)) //
..modelResponse.Models.Where(n => n.IsChatModel(this.Provider))
] ]
}; };
} }

View File

@ -76,7 +76,7 @@ public sealed class ProviderX() : BaseProvider(LLMProviders.X, new Uri("https://
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ModelLoadResult> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default) public override async Task<ModelLoadResult> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{ {
var result = await this.LoadModels(SecretStoreType.LLM_PROVIDER, ["grok-"], apiKeyProvisional, token); var result = await this.LoadModels(SecretStoreType.LLM_PROVIDER, apiKeyProvisional, token);
return result with return result with
{ {
// //
@ -108,19 +108,24 @@ public sealed class ProviderX() : BaseProvider(LLMProviders.X, new Uri("https://
#endregion #endregion
private Task<ModelLoadResult> LoadModels(SecretStoreType storeType, string[] prefixes, string? apiKeyProvisional, CancellationToken token) /// <summary>
/// Reads the xAI catalog, whole.
/// </summary>
/// <remarks>
/// Every name in it begins with "grok", which is why the prefix this used to filter by never
/// took anything away -- and why it said nothing either. What it did carry was Grok 2, appended
/// to every answer whether xAI still served it or not. It does not: the catalog has moved on to
/// Grok 4, and an entry nobody can talk to is worse than one missing from the list.
///
/// What the catalog does hold besides the chat models is five names which draw or film. The
/// caller asks the registry about those.
/// </remarks>
private Task<ModelLoadResult> LoadModels(SecretStoreType storeType, string? apiKeyProvisional, CancellationToken token)
{ {
return this.LoadModelsResponse<ModelsResponse>( return this.LoadModelsResponse<ModelsResponse>(
storeType, storeType,
"models", "models",
modelResponse => modelResponse.Data.Where(model => prefixes.Any(prefix => model.Id.StartsWith(prefix, StringComparison.InvariantCulture))) modelResponse => modelResponse.Data,
.Concat([
new Model
{
Id = "grok-2-latest",
DisplayName = "Grok 2.0 (latest)",
}
]),
apiKeyProvisional, token: token); apiKeyProvisional, token: token);
} }
} }