Updated model capabilities across all providers (#940)

This commit is contained in:
Thorsten Sommer 2026-08-29 16:07:00 +02:00 committed by GitHub
parent c9b7f224cc
commit edbb75dd9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 275 additions and 256 deletions

View File

@ -3318,6 +3318,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T2906887599"
-- No audit yet
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3138877447"] = "Noch keine Prüfung vorhanden"
-- Your organization requires this assistant to stay enabled
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3240350158"] = "Ihre Organisation verlangt, dass dieser Assistent aktiviert bleibt."
-- Confidence
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3243388657"] = "Gewissheit"
@ -3327,6 +3330,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3424652889"
-- Close
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3448155331"] = "Schließen"
-- Enabled by your organization, you may switch it off
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3528104897"] = "Von Ihrer Organisation aktiviert. Sie können diese Einstellung deaktivieren."
-- No stored audit details are available yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3647137899"] = "Es sind noch keine gespeicherten Audit-Details verfügbar."
@ -3342,6 +3348,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3916957031"
-- Audited at
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4103354206"] = "Geprüft am"
-- Required by your organization
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4148393979"] = "Von Ihrer Organisation vorgeschrieben"
-- Approved hash
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4170340306"] = "Genehmigter Hash"
@ -3354,6 +3363,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4289123040"
-- Audit hash
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T53507304"] = "Prüf-Hash"
-- Activation
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T561695293"] = "Aktivierung"
-- {0} Finding(s)
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T631393016"] = "{0} Fund(e)"
@ -9021,6 +9033,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3143506997"] = "Das Assistent-Plugin
-- An error occurred while sharing the plugin.
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3184210266"] = "Beim Teilen des Plugins ist ein Fehler aufgetreten."
-- Your organization requires this assistant to stay enabled
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3240350158"] = "Ihre Organisation verlangt, dass dieser Assistent aktiviert bleibt."
-- Your organization has disabled exporting plugins.
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3342440765"] = "Ihre Organisation hat das Exportieren von Plugins deaktiviert."

View File

@ -3318,6 +3318,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T2906887599"
-- No audit yet
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3138877447"] = "No audit yet"
-- Your organization requires this assistant to stay enabled
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3240350158"] = "Your organization requires this assistant to stay enabled"
-- Confidence
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3243388657"] = "Confidence"
@ -3327,6 +3330,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3424652889"
-- Close
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3448155331"] = "Close"
-- Enabled by your organization, you may switch it off
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3528104897"] = "Enabled by your organization, you may switch it off"
-- No stored audit details are available yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3647137899"] = "No stored audit details are available yet."
@ -3342,6 +3348,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T3916957031"
-- Audited at
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4103354206"] = "Audited at"
-- Required by your organization
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4148393979"] = "Required by your organization"
-- Approved hash
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4170340306"] = "Approved hash"
@ -3354,6 +3363,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T4289123040"
-- Audit hash
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T53507304"] = "Audit hash"
-- Activation
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T561695293"] = "Activation"
-- {0} Finding(s)
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T631393016"] = "{0} Finding(s)"
@ -9021,6 +9033,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3143506997"] = "The assistant plugin
-- An error occurred while sharing the plugin.
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3184210266"] = "An error occurred while sharing the plugin."
-- Your organization requires this assistant to stay enabled
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3240350158"] = "Your organization requires this assistant to stay enabled"
-- Your organization has disabled exporting plugins.
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3342440765"] = "Your organization has disabled exporting plugins."

View File

@ -47,6 +47,43 @@ public static partial class ProviderExtensions
Capability.CHAT_COMPLETION_API,
];
// Check for the Qwen 3.8 family:
if(modelName.StartsWith("qwen3.8"))
{
// Flash thinks by default, but thinking can be turned off:
if(modelName.StartsWith("qwen3.8-flash"))
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.VIDEO_INPUT,
Capability.TEXT_OUTPUT,
Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Unlike the open-weight checkpoint, the Max model keeps its vision
// capabilities when used through Alibaba Cloud:
if(modelName.StartsWith("qwen3.8-max"))
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.VIDEO_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// All other 3.8 models, such as the 27B one:
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
}
// Check for the 3.0 VL models:
if(modelName.IndexOf("-vl-") is not -1)
return

View File

@ -18,6 +18,26 @@ public static partial class ProviderExtensions
Capability.CHAT_COMPLETION_API,
];
// Claude Opus 5 and Sonnet 5 think adaptively unless thinking is turned off:
if(modelName.StartsWith("claude-opus-5") || modelName.StartsWith("claude-sonnet-5"))
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Claude Haiku 4.5 needs an explicit thinking budget to reason:
if(modelName.StartsWith("claude-haiku-4-5"))
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Claude 4.x models:
if(modelName.StartsWith("claude-opus-4") || modelName.StartsWith("claude-sonnet-4"))
return [
@ -48,9 +68,10 @@ public static partial class ProviderExtensions
Capability.CHAT_COMPLETION_API,
];
// Any other model is able to process text only:
// Any other model. Every current Claude model accepts images, so we assume the
// same for models we do not know yet:
return [
Capability.TEXT_INPUT,
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,

View File

@ -8,21 +8,31 @@ public static partial class ProviderExtensions
{
var modelName = model.Id.ToLowerInvariant().AsSpan();
// The reasoner alias points to the thinking mode of the current flash model:
if(modelName.IndexOf("reasoner") is not -1)
return
[
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING,
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
return
[
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
// The chat alias points to the non-thinking mode of the same model:
if(modelName.IndexOf("chat") is not -1)
return
[
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// DeepSeek publishes its models as open weights and offers them under the same
// names here. Instead of maintaining a second copy of those rules, we reuse the
// ones for open source models:
return GetModelCapabilitiesOpenSource(model);
}
}

View File

@ -10,14 +10,13 @@ public static partial class ProviderExtensions
if (modelName.IndexOf("gemini-") is not -1)
{
// Chat-compatible Gemini 3.x reasoning models:
if (modelName is "gemini-3.5-flash" ||
// Chat-compatible Gemini 3.x reasoning models. We match the entire 3.x line
// so that new releases are covered as well: they all reason, and the
// thinking level can only be lowered, never turned off. The two rolling
// aliases carry no version number and are listed separately:
if (modelName.IndexOf("gemini-3") is not -1 ||
modelName is "gemini-flash-latest" ||
modelName is "gemini-3.1-flash-lite" ||
modelName is "gemini-3-flash-preview" ||
modelName is "gemini-pro-latest" ||
modelName is "gemini-3.1-pro-preview" ||
modelName is "gemini-3.1-pro-preview-customtools")
modelName is "gemini-pro-latest")
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT,

View File

@ -182,7 +182,7 @@ public static partial class ProviderExtensions
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT,
Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING, Capability.REASONING_BY_DEFAULT,
Capability.FUNCTION_CALLING, Capability.REASONING_BY_DEFAULT,
Capability.WEB_SEARCH,
Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API,
];
@ -193,7 +193,7 @@ public static partial class ProviderExtensions
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING, Capability.REASONING_BY_DEFAULT,
Capability.FUNCTION_CALLING, Capability.REASONING_BY_DEFAULT,
Capability.WEB_SEARCH,
Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API,
];

View File

@ -6,244 +6,66 @@ public static partial class ProviderExtensions
{
private static List<Capability> GetModelCapabilitiesOpenRouter(Model model)
{
var modelName = model.Id.ToLowerInvariant().AsSpan();
//
// OpenRouter model IDs follow the pattern "vendor/model-name". Examples:
// - openai/gpt-5.6
// - anthropic/claude-opus-5
// - google/gemini-3.7-flash
// - qwen/qwen3.8-flash-next
//
// OpenRouter offers the models of all the other providers. Instead of keeping a
// second set of rules here, which would always lag behind, we hand the model
// over to the provider implementation which already knows it. The vendor prefix
// has to be removed first: some of those implementations match the beginning of
// the model name and would not recognize a prefixed ID.
//
var separatorIndex = model.Id.IndexOf('/');
var vendor = separatorIndex is -1 ? string.Empty : model.Id[..separatorIndex].ToLowerInvariant();
var bareModel = separatorIndex is -1 ? model : model with { Id = model.Id[(separatorIndex + 1)..] };
var bareModelName = bareModel.Id.ToLowerInvariant().AsSpan();
//
// OpenRouter model IDs follow the pattern: "provider/model-name"
// Examples:
// - openai/gpt-4o
// - anthropic/claude-3-5-sonnet
// - google/gemini-pro-1.5
// - meta-llama/llama-3.1-405b-instruct
//
// We need to detect capabilities based on both provider and model name.
//
//
// OpenAI models via OpenRouter:
//
if (modelName.IndexOf("openai/") is not -1)
var capabilities = vendor switch
{
// Reasoning models (o1, o3, o4 series)
if (modelName.IndexOf("/o1") is not -1 ||
modelName.IndexOf("/o3") is not -1 ||
modelName.IndexOf("/o4") is not -1)
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING,
Capability.CHAT_COMPLETION_API,
];
// The gpt-oss models are open weights. The OpenAI implementation does not
// know them, because they are not part of the OpenAI cloud offering:
"openai" when bareModelName.IndexOf("gpt-oss") is not -1 => GetModelCapabilitiesOpenSource(bareModel),
"openai" => GetModelCapabilitiesOpenAI(bareModel),
// GPT-4o and GPT-5 series with multimodal
if (modelName.IndexOf("/gpt-4o") is not -1 ||
modelName.IndexOf("/gpt-5") is not -1 ||
modelName.IndexOf("/chatgpt-4o") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
"anthropic" => GetModelCapabilitiesAnthropic(bareModel),
// Standard GPT-4
if (modelName.IndexOf("/gpt-4") is not -1)
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Gemma is open weights, Gemini is not:
"google" when bareModelName.IndexOf("gemma") is not -1 => GetModelCapabilitiesOpenSource(bareModel),
"google" => GetModelCapabilitiesGoogle(bareModel),
// GPT-3.5
if (modelName.IndexOf("/gpt-3.5") is not -1 ||
modelName.IndexOf("/gpt-3") is not -1)
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
}
"mistralai" => GetModelCapabilitiesMistral(bareModel),
"perplexity" => GetModelCapabilitiesPerplexity(bareModel),
//
// Anthropic models via OpenRouter:
//
if (modelName.IndexOf("anthropic/") is not -1)
{
// Claude 3.5 and newer with vision
if (modelName.IndexOf("/claude-3.5") is not -1 ||
modelName.IndexOf("/claude-3-5") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Everything else is open source: Qwen, Llama, GLM, Kimi, Muse, Hunyuan,
// Nemotron, Grok, and whatever OpenRouter adds next. DeepSeek belongs here
// as well: its own implementation covers the aliases of the DeepSeek
// platform, while OpenRouter uses the names of the open weights.
_ => GetModelCapabilitiesOpenSource(bareModel),
};
// Claude 3 Opus/Sonnet with vision
if (modelName.IndexOf("/claude-3-opus") is not -1 ||
modelName.IndexOf("/claude-3-sonnet") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Other Claude 3 models
if (modelName.IndexOf("/claude-3") is not -1)
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
}
//
// Google models via OpenRouter:
//
if (modelName.IndexOf("google/") is not -1)
{
// Gemini models with multimodal
if (modelName.IndexOf("/gemini") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
}
//
// xAI Grok models via OpenRouter:
//
if (modelName.IndexOf("x-ai/") is not -1 || modelName.IndexOf("/grok") is not -1)
{
if (modelName.IndexOf("-vision") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
}
//
// DeepSeek models via OpenRouter:
//
if (modelName.IndexOf("/deepseek") is not -1)
{
if (modelName.IndexOf("-r1") is not -1 || modelName.IndexOf(" r1") is not -1)
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING,
Capability.CHAT_COMPLETION_API,
];
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
}
//
// Mistral models via OpenRouter:
//
if (modelName.IndexOf("/mistral") is not -1 || modelName.IndexOf("/pixtral") is not -1)
{
if (modelName.IndexOf("/pixtral") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
}
//
// Meta Llama models via OpenRouter:
//
if (modelName.IndexOf("/llama") is not -1)
{
// Llama 4 with vision
if (modelName.IndexOf("/llama-4") is not -1 ||
modelName.IndexOf("/llama4") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Vision models
if (modelName.IndexOf("-vision") is not -1)
return [
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
// Llama 3.1+ with function calling
if (modelName.IndexOf("/llama-3.") is not -1 ||
modelName.IndexOf("/llama3.") is not -1)
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Default Llama
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
}
//
// Qwen models via OpenRouter:
//
if (modelName.IndexOf("/qwen") is not -1 || modelName.IndexOf("/qwq") is not -1)
{
if (modelName.IndexOf("/qwq") is not -1)
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING,
Capability.CHAT_COMPLETION_API,
];
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
}
//
// Default for unknown models:
// Assume basic text input/output with chat completion
//
return [
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.CHAT_COMPLETION_API,
];
return NormalizeForOpenRouter(capabilities);
}
}
/// <summary>
/// Adjusts the capabilities reported by another provider for use through OpenRouter.
/// </summary>
/// <param name="capabilities">The capabilities as reported by the provider implementation.</param>
/// <returns>The capabilities as they apply when using the model through OpenRouter.</returns>
/// <remarks>
/// OpenRouter serves every model through its OpenAI-compatible chat completion API.
/// The Responses API is not available there, no matter which API the original
/// provider offers.
/// </remarks>
private static List<Capability> NormalizeForOpenRouter(List<Capability> capabilities)
{
capabilities.Remove(Capability.RESPONSES_API);
if(!capabilities.Contains(Capability.CHAT_COMPLETION_API))
capabilities.Add(Capability.CHAT_COMPLETION_API);
return capabilities;
}
}

View File

@ -71,6 +71,21 @@ public static partial class ProviderExtensions
];
}
//
// Meta Muse models. They need their own block because their names do not
// contain "llama". Muse Glimmer always reasons: its chat template opens the
// thinking channel unconditionally, only the reasoning strength can be lowered.
//
if (modelName.IndexOf("muse-glimmer") is not -1)
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
//
// DeepSeek models:
//
@ -122,6 +137,19 @@ public static partial class ProviderExtensions
Capability.CHAT_COMPLETION_API,
];
// Check for the Qwen 3.8 Flash models. The open weights are published as
// Flash-Next, while Flash without the suffix is the production model. Both
// share the same capabilities, so one check covers them:
if(modelName.IndexOf("qwen3.8-flash") is not -1)
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.VIDEO_INPUT,
Capability.TEXT_OUTPUT,
Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Check for the multimodal Qwen 3.8 27B checkpoint:
if(modelName.IndexOf("qwen3.8-27b") is not -1)
return
@ -193,6 +221,23 @@ public static partial class ProviderExtensions
Capability.CHAT_COMPLETION_API,
];
//
// Tencent Hunyuan models. Hy3 answers directly by default: its reasoning_effort
// parameter defaults to no_think, low and high must be requested. We also match
// the short name because providers offer the model as tencent/hy3, so checking
// the start of the name is not enough.
//
if (modelName.IndexOf("hunyuan") is not -1 ||
modelName.IndexOf("hy3") is not -1)
return
[
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
//
// Mistral models:
//
@ -335,6 +380,18 @@ public static partial class ProviderExtensions
Capability.CHAT_COMPLETION_API,
];
// Grok 4 models take text, images, and video natively. Reasoning is always
// on, only the reasoning effort can be configured:
if(modelName.IndexOf("grok-4") is not -1)
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.VIDEO_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
if(modelName.StartsWith("grok-3-mini"))
return
[
@ -346,14 +403,25 @@ public static partial class ProviderExtensions
];
if(modelName.StartsWith("grok-3"))
return
return
[
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
// Any other Grok model. Without this, unknown Grok versions would fall
// through to the global default and would lose function calling:
return
[
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
}
//
@ -383,11 +451,41 @@ public static partial class ProviderExtensions
];
}
//
// NVIDIA Nemotron models. They are built for agentic workloads and are text
// only. Reasoning has to be requested through enable_thinking, so it is
// optional. The check also covers the quantized checkpoints such as
// NVIDIA-Nemotron-3.5-Lightning-30B-A3B-NVFP4.
//
if (modelName.IndexOf("nemotron") is not -1)
return
[
Capability.TEXT_INPUT,
Capability.TEXT_OUTPUT,
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
//
// Z AI / GLM models:
//
if (modelName.IndexOf("glm") is not -1)
{
// GLM 5.3 uses forced thinking: the reasoning effort can be lowered, but
// reasoning cannot be turned off. This check must stay in front of the
// vision check below, because quantized builds such as GLM-5.3-Flash-NVFP4
// contain a "v" and would be misread as a vision model:
if (modelName.IndexOf("glm-5.3") is not -1)
return
[
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
Capability.TEXT_OUTPUT,
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
Capability.CHAT_COMPLETION_API,
];
if (modelName.IndexOf("glm-5.2") is not -1)
return
[

View File

@ -3,6 +3,7 @@
- Added configurable direct-chat launchers for assistant plugins. Plugin authors and the Assistant Builder can now open a chat with a chosen workspace, provider, profile, chat template, and data sources, while unavailable or unauthorized selections are reported before a chat is created.
- Added provider logos throughout AI Studio, making models easier to recognize at a glance. Configuration plugins can now give managed LLM, transcription, and embedding providers their own project icon with the optional `IconPath` field.
- Added the option for IT departments to enable assistant plugins they rolled out. Approving an assistant only stated that it is safe, so everybody still had to switch it on themselves. An approval can now also enable the assistant, either as a default which you may switch off again, or in a way your IT department keeps in place. The plugin page and the security card of the assistant tell you which of the two applies.
- Added knowledge about the latest AI models. AI Studio now recognizes Qwen 3.8 Flash, GLM-5.3 Flash, Meta's Muse Glimmer, NVIDIA's Nemotron 3.5, Tencent's Hunyuan Hy3, Grok 4, Claude Opus 5 and Sonnet 5, and Gemini 3.6 and 3.7. It knows what each of them is capable of, so images, videos, tool usage, and reasoning are available right away instead of staying hidden.
- Improved the safety of plugin symbols: AI Studio now shows the symbol of a plugin in isolation, so nothing inside a symbol can reach the rest of the app.
- Improved how much memory AI Studio needs. Working with large documents used to grow the app to several gigabytes, and on macOS that memory was never handed back. AI Studio now stays at a fraction of that and returns memory to your system. This matters most on devices with little memory, such as a Raspberry Pi.
- Improved the preview for large documents. It now shows you the beginning of your document instead of loading all of it, so the dialog opens right away. Your complete document still goes to the AI.
@ -17,4 +18,5 @@
- Fixed the description of the global voice recording shortcut always appearing in English. On Linux, your desktop asks you to confirm such a shortcut and shows this description; it now appears in your language.
- Fixed the code editor being offered for assistant plugins your organization manages. Editing one would have withdrawn the approval of your IT department and demanded a fresh security audit.
- Fixed assistant plugins your organization deployed alongside a configuration not being recognized as centrally managed unless they declared it themselves.
- Fixed a misleading warning in the log when an organization deployed an archive that carries no configuration of its own.
- Fixed a misleading warning in the log when an organization deployed an archive that carries no configuration of its own.
- Fixed AI Studio underrating what many models can do. Newer Claude, Gemini, Grok, DeepSeek, and Qwen models were missing abilities they actually have, such as image input or tool usage. This was most noticeable with OpenRouter, where nearly every model was affected. AI Studio now derives these abilities from the same source for all providers, so a model offers the same capabilities no matter which way you reach it.