diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.Alibaba.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.Alibaba.cs deleted file mode 100644 index 6a26b0c8..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.Alibaba.cs +++ /dev/null @@ -1,220 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - private static List GetModelCapabilitiesAlibaba(Model model) - { - var modelName = NormalizeModelId(model.Id).AsSpan(); - - // Qwen models: - if (modelName.StartsWith("qwen")) - { - // Check for omni models. Alibaba lists the Qwen3 and Qwen3.5 Omni series among the - // models which call functions; the older qwen-omni ones are not on that list, which - // is what the version check separates here: - if (modelName.IndexOf("omni") is not -1) - { - if (modelName.StartsWith("qwen3")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.AUDIO_INPUT, Capability.SPEECH_INPUT, - Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.AUDIO_INPUT, Capability.SPEECH_INPUT, - Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT, - - Capability.CHAT_COMPLETION_API, - ]; - } - - // Check for Qwen 3.5: - if(modelName.StartsWith("qwen3.5")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Check for Qwen 3.6 family: - if(modelName.StartsWith("qwen3.6")) - return - [ - Capability.TEXT_INPUT, Capability.VIDEO_INPUT, - Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Check for the Qwen 3.7 family. Thinking is optional here and switched on by - // default, except for the two preview snapshots, which do nothing else: - if(modelName.StartsWith("qwen3.7")) - { - if(modelName.IndexOf("-preview") is not -1 || - modelName.IndexOf("-2026-05-17") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Vision arrived in the middle of the series. The rolling qwen3.7-max alias - // still answers as the text-only May snapshot, so only the June one may be - // told that it reads images and video: - if(modelName.IndexOf("-2026-06-08") 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, - ]; - - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - 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 VL models. Alibaba names the Qwen3-VL Plus and Flash series as - // function callers; the older qwen-vl models are absent from that list: - if(modelName.IndexOf("-vl-") is not -1) - { - if(modelName.StartsWith("qwen3")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.CHAT_COMPLETION_API, - ]; - } - - // Check for Qwen 3: - if(modelName.StartsWith("qwen3")) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // QwQ models. What Model Studio serves under this name is qwq-plus, a commercial - // thinking-only model built on Qwen2.5. It is not the same model as the open-weight - // QwQ-32B, which the rules for open source models cover; the two only share a family - // name. Neither of them appears in Alibaba's list of models which call functions, and - // the model card of the open weights does not mention tools at all, which is why this - // states no such ability. Anybody who knows better can turn it on in the expert settings. - // - if (modelName.StartsWith("qwq")) - { - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // QVQ models: - if (modelName.StartsWith("qvq")) - { - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // Default to text input and output: - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.Anthropic.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.Anthropic.cs deleted file mode 100644 index 95ad0d46..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.Anthropic.cs +++ /dev/null @@ -1,80 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - private static List GetModelCapabilitiesAnthropic(Model model) - { - var modelName = NormalizeModelId(model.Id).AsSpan(); - - // Claude Fable 5 and Mythos 5 always use adaptive thinking: - if(modelName.StartsWith("claude-fable-5") || modelName.StartsWith("claude-mythos-5")) - return [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - 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 [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Claude 3.7 is able to do reasoning: - if(modelName.StartsWith("claude-3-7")) - return [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // All other 3.x models are able to process text and images as input: - if(modelName.StartsWith("claude-3-")) - return [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // 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.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.DeepSeek.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.DeepSeek.cs deleted file mode 100644 index 2bc1681b..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.DeepSeek.cs +++ /dev/null @@ -1,38 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - private static List GetModelCapabilitiesDeepSeek(Model model) - { - var modelName = NormalizeModelId(model.Id).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.FUNCTION_CALLING, - 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); - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.Gateway.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.Gateway.cs deleted file mode 100644 index a09f9c45..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.Gateway.cs +++ /dev/null @@ -1,85 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - /// - /// Determines the capabilities of a model offered through a gateway. - /// - /// - /// A gateway serves the models of many other providers rather than models of its own. OpenRouter, - /// LiteLLM, and the Hugging Face router all work that way, and all three name their models the - /// same: "vendor/model-name". - /// - /// The model as the gateway names it. - /// The capabilities of the model when reached through a gateway. - private static List GetModelCapabilitiesGateway(Model model) - { - // - // 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 - // - // A gateway 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 = NormalizeModelId(bareModel.Id).AsSpan(); - - var capabilities = vendor switch - { - // 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), - - "anthropic" => GetModelCapabilitiesAnthropic(bareModel), - - // Gemma is open weights, Gemini is not: - "google" when bareModelName.IndexOf("gemma") is not -1 => GetModelCapabilitiesOpenSource(bareModel), - "google" => GetModelCapabilitiesGoogle(bareModel), - - "mistralai" => GetModelCapabilitiesMistral(bareModel), - "perplexity" => GetModelCapabilitiesPerplexity(bareModel), - - // Everything else is open source: Qwen, Llama, GLM, Kimi, Muse, Hunyuan, - // Nemotron, Grok, and whatever a gateway adds next. DeepSeek belongs here - // as well: its own implementation covers the aliases of the DeepSeek - // platform, while the gateways use the names of the open weights. - _ => GetModelCapabilitiesOpenSource(bareModel), - }; - - return NormalizeForGateway(capabilities); - } - - /// - /// Adjusts the capabilities reported by another provider for use through a gateway. - /// - /// The capabilities as reported by the provider implementation. - /// The capabilities as they apply when using the model through a gateway. - /// - /// A gateway 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. - /// - /// The same holds for a provider which resells a model under its plain name instead of - /// prefixing it with the vendor, such as GWDG. Those go through the open source rules, which - /// call this for the very same reason. - /// - private static List NormalizeForGateway(List capabilities) - { - capabilities.Remove(Capability.RESPONSES_API); - if(!capabilities.Contains(Capability.CHAT_COMPLETION_API)) - capabilities.Add(Capability.CHAT_COMPLETION_API); - - return capabilities; - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.Google.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.Google.cs deleted file mode 100644 index 1073b7ae..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.Google.cs +++ /dev/null @@ -1,162 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - private static List GetModelCapabilitiesGoogle(Model model) - { - var modelName = NormalizeModelId(model.Id).AsSpan(); - - if (modelName.IndexOf("gemini-") is not -1) - { - // - // Image generation models. They carry a version number like every other model and - // have to be asked about first, or gemini-3-pro-image would be read as a chat model - // of the 3.x line and be promised function calling. No image model of the family - // offers that; what they do offer, and the chat models do not, is writing images. - // - if (modelName.IndexOf("-image") is not -1) - { - // Of the image models, only the 3.1 Flash ones read video. They think about - // complex prompts, and, as with the 3.x chat models, thinking cannot be - // switched off: - if (modelName.IndexOf("gemini-3.1-flash") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - // Every other Gemini 3 image model thinks as well, it just does not read video: - if (modelName.IndexOf("gemini-3") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - - Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - // The older image models, such as the 2.5 Flash one, do not think: - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - - Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // 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. That holds for the - // Flash Lite models of this line too, which is what sets them apart from - // Gemini 2.5 Flash Lite below: there, thinking is off until it is asked for, - // while here the lowest level still thinks. 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-pro-latest") - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT, - Capability.SPEECH_INPUT, Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Gemini 2.5 Flash Lite supports thinking, but the default is off: - if (modelName.IndexOf("gemini-2.5-flash-lite") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT, - Capability.SPEECH_INPUT, Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Reasoning models: - if (modelName.IndexOf("gemini-2.5") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT, - Capability.SPEECH_INPUT, Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Realtime model: - if(modelName.IndexOf("-2.0-flash-live-") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.AUDIO_INPUT, Capability.SPEECH_INPUT, - Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // There used to be a branch here which withheld function calling from the 2.0 Flash - // models. It said the wrong thing about them, and it only ever caught the dated IDs - // because it asked for a trailing hyphen: the plain gemini-2.0-flash alias walked - // past it and got a different answer than gemini-2.0-flash-001, which is the same - // model. Both questions are moot now, because Google shut the 2.0 Flash chat models - // down on 1 June 2026. Anything still asking for one of those names gets the default - // below. The live model above keeps its branch: it belongs to a different API whose - // retirement Google announces separately. - // - - // The old 1.0 pro vision model: - if(modelName.IndexOf("pro-vision") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - - Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - // Default to all other Gemini models: - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT, - Capability.SPEECH_INPUT, Capability.VIDEO_INPUT, - - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // Default for all other models: - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.Mistral.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.Mistral.cs deleted file mode 100644 index 60b61f15..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.Mistral.cs +++ /dev/null @@ -1,191 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - // - // Mistral names its models after the month they were released: mistral-large-2512 is - // Mistral Large 3 from December 2025. The version number lives in the marketing name only, - // so matching on it misses nearly every model the API actually serves. The constants below - // read as YYMM and say from which release on a family gained a capability. - // - private const int MISTRAL_LARGE_VISION_SINCE = 2512; // Mistral Large 3 - private const int MISTRAL_LARGE_REASONING_SINCE = 2512; // Mistral Large 3 - private const int MISTRAL_MEDIUM_VISION_SINCE = 2505; // Mistral Medium 3 - private const int MISTRAL_MEDIUM_REASONING_SINCE = 2604; // Mistral Medium 3.5 - private const int MISTRAL_SMALL_VISION_SINCE = 2503; // Mistral Small 3.1 - private const int MISTRAL_SMALL_REASONING_SINCE = 2603; // Mistral Small 4 - private const int MINISTRAL_VISION_SINCE = 2512; // Ministral 3 - - /// - /// Used for families which have no reasoning at all. No release date can ever reach it. - /// - private const int MISTRAL_REASONING_NEVER = int.MaxValue; - - // - // Where the "latest" aliases point to. Mistral moves them on with every release, so they - // have to behave like the release they resolve to instead of carrying their own rules. - // - private const int MISTRAL_LARGE_LATEST = 2512; - private const int MISTRAL_MEDIUM_LATEST = 2604; - private const int MISTRAL_SMALL_LATEST = 2603; - private const int MINISTRAL_LATEST = 2512; - - /// - /// Mistral released its first date-named model in 2023. Anything below that is not a release - /// date but a parameter count or a context size which happens to have four digits. - /// - private const int MISTRAL_FIRST_RELEASE_YEAR = 23; - - // - // Mistral serves some models under their marketing version as well, and it writes the version - // separator both ways: mistral-medium-3.5 and mistral-medium-3-5 are the same model. Those - // names carry no release date, so we map them onto the release they stand for. The order - // matters: the more specific version has to come first, otherwise "3" would swallow "3.5". - // - private static readonly (string VersionName, int ReleaseDate)[] MISTRAL_VERSION_NAMES = - [ - ("mistral-large-3", 2512), - - ("mistral-medium-3.5", 2604), - ("mistral-medium-3-5", 2604), - ("mistral-medium-3.1", 2508), - ("mistral-medium-3-1", 2508), - ("mistral-medium-3", 2505), - - ("mistral-small-4", 2603), - ("mistral-small-3.2", 2506), - ("mistral-small-3-2", 2506), - ("mistral-small-3.1", 2503), - ("mistral-small-3-1", 2503), - ("mistral-small-3", 2501), - ]; - - private static List GetModelCapabilitiesMistral(Model model) - { - var modelName = NormalizeModelId(model.Id).AsSpan(); - - // Pixtral models are able to do process images: - 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, - ]; - - // Mistral saba: - if (modelName.IndexOf("mistral-saba-") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - // - // The four families Mistral versions by release date. Ministral has to be matched before - // the others, although its name does not contain "mistral" as a substring: keeping the - // families together makes the block easier to read. - // - if (modelName.IndexOf("ministral") is not -1) - return BuildMistralCapabilities(GetMistralReleaseDate(modelName, MINISTRAL_LATEST), MINISTRAL_VISION_SINCE, MISTRAL_REASONING_NEVER); - - if (modelName.IndexOf("mistral-large") is not -1) - return BuildMistralCapabilities(GetMistralReleaseDate(modelName, MISTRAL_LARGE_LATEST), MISTRAL_LARGE_VISION_SINCE, MISTRAL_LARGE_REASONING_SINCE); - - if (modelName.IndexOf("mistral-medium") is not -1) - return BuildMistralCapabilities(GetMistralReleaseDate(modelName, MISTRAL_MEDIUM_LATEST), MISTRAL_MEDIUM_VISION_SINCE, MISTRAL_MEDIUM_REASONING_SINCE); - - if (modelName.IndexOf("mistral-small") is not -1) - return BuildMistralCapabilities(GetMistralReleaseDate(modelName, MISTRAL_SMALL_LATEST), MISTRAL_SMALL_VISION_SINCE, MISTRAL_SMALL_REASONING_SINCE); - - // Default: - return GetModelCapabilitiesOpenSource(model); - } - - /// - /// Determines the release date a Mistral model belongs to. - /// - /// The lowercase model name to inspect. - /// The release the family's "latest" alias points to. - /// The release date as YYMM, or 0 when the name carries none. - private static int GetMistralReleaseDate(ReadOnlySpan modelName, int latestReleaseDate) - { - // The "latest" alias always points to the newest release of its family: - if (modelName.IndexOf("-latest") is not -1) - return latestReleaseDate; - - foreach (var (versionName, releaseDate) in MISTRAL_VERSION_NAMES) - if (modelName.IndexOf(versionName) is not -1) - return releaseDate; - - return ReadMistralReleaseDate(modelName); - } - - /// - /// Reads the four-digit release date out of a Mistral model name. - /// - /// - /// The block has to be exactly four digits long and has to read as a plausible year and month. - /// Without that, the size of a model would be mistaken for its release: ministral-14b-2512 - /// must resolve to 2512 and not to anything the "14b" part could be read as. - /// - /// The lowercase model name to inspect. - /// The release date as YYMM, or 0 when the name carries none. - private static int ReadMistralReleaseDate(ReadOnlySpan modelName) - { - for (var index = 0; index + 4 <= modelName.Length; index++) - { - // A digit next to the block means the block is longer than four digits: - if (index > 0 && char.IsAsciiDigit(modelName[index - 1])) - continue; - - if (index + 4 < modelName.Length && char.IsAsciiDigit(modelName[index + 4])) - continue; - - var candidate = modelName.Slice(index, 4); - if (!char.IsAsciiDigit(candidate[0]) || !char.IsAsciiDigit(candidate[1]) || - !char.IsAsciiDigit(candidate[2]) || !char.IsAsciiDigit(candidate[3])) - continue; - - var releaseDate = int.Parse(candidate); - var year = releaseDate / 100; - var month = releaseDate % 100; - if (year < MISTRAL_FIRST_RELEASE_YEAR || month is < 1 or > 12) - continue; - - return releaseDate; - } - - return 0; - } - - /// - /// Builds the capabilities of a Mistral model from its release date. - /// - /// - /// A model whose release date we cannot read gets neither image input nor reasoning. That is - /// the safe direction: offering an ability the model does not have would fail the request, - /// whereas a missing one can be added by hand through the capability overrides. - /// - /// The release date of the model as YYMM, or 0 when unknown. - /// The release from which this family accepts images. - /// The release from which this family can reason. - /// The capabilities of the model. - private static List BuildMistralCapabilities(int releaseDate, int visionSince, int reasoningSince) - { - List capabilities = [Capability.TEXT_INPUT, Capability.FUNCTION_CALLING, Capability.CHAT_COMPLETION_API, Capability.TEXT_OUTPUT]; - - if (releaseDate >= visionSince) - capabilities.Add(Capability.MULTIPLE_IMAGE_INPUT); - - if (releaseDate >= reasoningSince) - capabilities.Add(Capability.OPTIONAL_REASONING); - - return capabilities; - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.OpenAI.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.OpenAI.cs deleted file mode 100644 index 5f4b10e6..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.OpenAI.cs +++ /dev/null @@ -1,226 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - private static List GetModelCapabilitiesOpenAI(Model model) - { - var modelName = NormalizeModelId(model.Id).AsSpan(); - - if (modelName is "gpt-4o-search-preview") - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.WEB_SEARCH, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName is "gpt-4o-mini-search-preview") - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.WEB_SEARCH, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.StartsWith("o1-mini")) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - if(modelName is "gpt-3.5-turbo") - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - Capability.RESPONSES_API, - ]; - - if(modelName.StartsWith("gpt-3.5")) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.StartsWith("o3-mini")) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.RESPONSES_API, - ]; - - if (modelName.StartsWith("o4-mini") || modelName.StartsWith("o3")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, - ]; - - if (modelName.StartsWith("o1")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.RESPONSES_API, - ]; - - if(modelName.StartsWith("gpt-4-turbo")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.RESPONSES_API, - ]; - - if(modelName is "gpt-4" || modelName.StartsWith("gpt-4-")) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - Capability.RESPONSES_API, - ]; - - if(modelName.StartsWith("gpt-5-nano")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.ALWAYS_REASONING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, - ]; - - if(modelName is "gpt-5" || modelName.StartsWith("gpt-5-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.ALWAYS_REASONING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, - ]; - - // - // None of the GPT-5 models writes images itself. They can ask for one through the - // image generation tool, which is a tool call like any other and produces a picture - // from a separate model. That is a different thing from an output modality, and we - // must not report it as one: the chat would then offer to receive images which never - // arrive. - // - if(modelName is "gpt-5.1" || modelName.StartsWith("gpt-5.1-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API, - ]; - - if(modelName is "gpt-5.2" || modelName.StartsWith("gpt-5.2-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API, - ]; - - if(modelName is "gpt-5.3" || modelName.StartsWith("gpt-5.3-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API, - ]; - - if(modelName is "gpt-5.4" || modelName.StartsWith("gpt-5.4-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API, - ]; - - if(modelName is "gpt-5.5" || modelName.StartsWith("gpt-5.5-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.REASONING_BY_DEFAULT, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API, - ]; - - if(modelName is "gpt-5.6" || modelName.StartsWith("gpt-5.6-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.REASONING_BY_DEFAULT, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API, - ]; - - // - // GPT-6 Astra. Unlike the 5.5 and 5.6 models, it reasons on every request: the effort - // reaches from low to max, and there is no setting which switches thinking off. - // - if(modelName is "gpt-6-astra" || modelName.StartsWith("gpt-6-astra-")) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, Capability.ALWAYS_REASONING, - Capability.WEB_SEARCH, - Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.RESPONSES_API, - Capability.WEB_SEARCH, - ]; - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.OpenSource.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.OpenSource.cs deleted file mode 100644 index 441202b5..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.OpenSource.cs +++ /dev/null @@ -1,1332 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - private static List GetModelCapabilitiesOpenSource(Model model) - { - var modelName = NormalizeModelId(model.Id).AsSpan(); - - // - // Checking for names in the case of open source models is a hard task. - // Let's assume we want to check for the llama 3.1 405b model. - // - // Here is a not complete list of how providers name this model: - // - Fireworks: accounts/fireworks/models/llama-v3p1-405b-instruct - // - Hugging Face -> Nebius AI Studio: meta-llama/Meta-Llama-3.1-405B-Instruct - // - Groq: llama-3.1-405b-instruct - // - LM Studio: llama-3.1-405b-instruct - // - Helmholtz Blablador: 1 - Llama3 405 the best general model - // - GWDG: Llama 3.1 405B Instruct - // - Ollama: llama3.1:405b - // - // The name arrives here already normalized by NormalizeModelId: lowercase, with every - // separator written as a single hyphen. That is why the checks below no longer carry a - // variant with a space or a colon. What normalization cannot do is insert a separator - // where a provider left it out, or remove one where it added it, so a family which is - // written both as "llama3" and as "llama-3" still needs both spellings. - // - - // - // Some providers serve the models of the big vendors under their plain names, without the - // "vendor/model" prefix a gateway would put in front. GWDG is the case which brought this - // up: next to the open weights it hosts, it resells Claude and GPT models and names them - // the way their vendor does. A freely chosen LiteLLM alias and a self-hosted proxy can do - // the same. Without this, all of them would be judged by the rules for open weights, which - // know none of them, and would lose tool calling, vision, and reasoning alike. - // - // Only vendors whose rules do not lead back here may be asked. Mistral and DeepSeek fall - // back to this function themselves, so delegating to them would loop. - // - // Whatever comes back is normalized the way a gateway's answer is: a provider reselling a - // model serves it through its own OpenAI-compatible chat completion API, never through the - // Responses API of the vendor it bought the model from. - // - if (modelName.StartsWith("claude-") || modelName.IndexOf("-claude-") is not -1) - return NormalizeForGateway(GetModelCapabilitiesAnthropic(model)); - - if (modelName.StartsWith("gemini-") || modelName.IndexOf("-gemini-") is not -1) - return NormalizeForGateway(GetModelCapabilitiesGoogle(model)); - - if (IsOpenAICloudModelName(modelName)) - return NormalizeForGateway(GetModelCapabilitiesOpenAI(model)); - - // - // Base checkpoints, whatever family they come from. They were never instruction-tuned: - // they continue a text instead of answering, and they know neither a chat template nor - // tools. This is checked before any family, because otherwise each of them would have to - // repeat it, and because the default at the end of this function assumes tool calling. - // - // The name part has to be exactly "base", so that a model whose name merely contains the - // word, as in "based", is left alone. - // - if (modelName.EndsWith("-base") || modelName.IndexOf("-base-") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - // - // DeepSeek models. This block has to come before the Llama one: the R1 distills are Llama - // and Qwen checkpoints, and a name such as deepseek-r1-distill-llama-70b would otherwise - // be read as a plain Llama and lose its reasoning. - // - if (modelName.IndexOf("deepseek") is not -1) - { - // - // The distills are Llama and Qwen checkpoints fine-tuned on R1 answers. They reason, - // but they kept the chat template of the model they were built from, so none of the - // tool calling R1 itself was trained for survived. They are checked first because - // they carry "r1" in their name and would match the rule for it below: - // - if (modelName.IndexOf("distill") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - // The V4 generation, which also covers the point releases such as V4.1, and the - // experimental checkpoint which takes images: - if (modelName.IndexOf("deepseek-v4") is not -1) - { - if (modelName.IndexOf("vision") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - if(modelName.IndexOf("deepseek-r1") is not -1) - return [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // From V3.1 on, the model has a thinking mode which the request turns on; V3.2 added - // tool calling inside that mode. The gateways write these two either as "deepseek-v3.1" - // or as "deepseek-chat-v3.1", so the version alone is what we look for: - // - if (modelName.IndexOf("v3.1") is not -1 || - modelName.IndexOf("v3.2") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // The rest of the V3 line answers directly and calls functions: - if (modelName.IndexOf("v3") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Meta llama models: - // - if (modelName.IndexOf("llama") is not -1) - { - if (modelName.IndexOf("llama4") is not -1 || - modelName.IndexOf("llama-4") is not -1 || - modelName.IndexOf("llama-v4") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // The old vision models cannot do function calling: - if (modelName.IndexOf("vision") is not -1) - return [ - Capability.TEXT_INPUT, - Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - // - // All models >= 3.1 are able to do function calling: - // - if (modelName.IndexOf("llama3.") is not -1 || - modelName.IndexOf("llama-3.") is not -1 || - modelName.IndexOf("llama-v3p") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // All other llama models can only do text input and output: - return [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // 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, - ]; - - // - // Qwen models: - // - if (modelName.IndexOf("qwen") is not -1 || modelName.IndexOf("qwq") is not -1) - { - // - // QwQ has no tool calling. That is worth stating, because Alibaba serves a model of - // the same family name: qwq-plus is a commercial thinking-only model of theirs, while - // QwQ-32B here is the open-weight one built on Qwen2.5. They are two different models, - // and neither the model card of the open weights nor Alibaba's list of models which - // call functions mentions either of them. - // - if (modelName.IndexOf("qwq") is not -1) - return [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - // Check for the open-weight Qwen 3.8 checkpoint: - if(modelName.IndexOf("qwen3.8-2.4t-a95b") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - 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. Blablador writes this one in two - // further ways, which no normalization can turn into the canonical name: it separates - // the family from the version ("Qwen 3.8-27B with DFlash on haicluster"), and its short - // alias drops the dot ("alias-qwen38-27b"). - // - if(modelName.IndexOf("qwen3.8-27b") is not -1 || - modelName.IndexOf("qwen-3.8-27b") is not -1 || - modelName.IndexOf("qwen38-27b") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // Any other Qwen 3.8 checkpoint. The three checks above all need a size or a variant - // in the name, which the rolling tags do not carry: Ollama serves the 27B checkpoint - // as "qwen3.8:latest". Without this, such a name would fall through to the generic - // Qwen rule and lose everything the family can do. The 27B checkpoint is what the - // rolling tag points to, so it decides what this tier promises. - // - if(modelName.IndexOf("qwen3.8") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Check for Qwen 3.5: - if(modelName.IndexOf("qwen3.5") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Check for Qwen 3.6 family: - if(modelName.IndexOf("qwen3.6") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if(modelName.IndexOf("-vl-") is not -1) - return [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // Every other Qwen. The whole line calls functions, from Qwen 2.5 on, and the Coder - // checkpoints are built for exactly that. Reasoning is not promised here: the older - // generations have none, and which of the newer ones think by default differs per - // checkpoint, so the rules above name them one by one. - // - return [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Moonshot AI / Kimi models: - // - if (modelName.IndexOf("kimi-k3") 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.IndexOf("kimi-k2.7-code") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // The vision checkpoint reasons, but it is the one Kimi model no vendor lists among those - // which call functions, so it does not get that ability here: - // - if (modelName.IndexOf("kimi-vl") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // The rest of the Kimi line. Moonshot builds these for agentic work, and the K2 model card - // says so plainly: pass the tools with the request and the model decides on its own when - // to call them. The thinking variants say what they are in their name; the others answer - // directly. All of them take text only. - // - if (modelName.IndexOf("kimi") is not -1 || modelName.IndexOf("moonshot") is not -1) - { - if (modelName.IndexOf("thinking") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - 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, - ]; - - // - // Ministral models. They need their own block because their names do not contain - // "mistral" as a substring, so the block below never sees them. Only Ministral 3 accepts - // images, the 2024 models are text only, which is why the release date decides here too: - // - if (modelName.IndexOf("ministral") is not -1) - return BuildMistralCapabilities(GetMistralReleaseDate(modelName, MINISTRAL_LATEST), MINISTRAL_VISION_SINCE, MISTRAL_REASONING_NEVER); - - // - // Mistral models: - // - if (modelName.IndexOf("mistral") is not -1 || - modelName.IndexOf("magistral") is not -1 || - modelName.IndexOf("voxtral") 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, - ]; - - - // Mistral medium 3.5: - if (modelName.IndexOf("mistral-medium-3.5") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - - if (modelName.IndexOf("mistral-3") is not -1 || - modelName.IndexOf("mistral-large-3") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("mistral-small-3") is not -1 || - modelName.IndexOf("mistral-small-4") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("mistral-small-") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("voxtral-") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.SPEECH_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Magistral models: - if (modelName.IndexOf("magistral-") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("3.1") is not -1 || - modelName.IndexOf("3.2") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Default: - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Grok models: - // - if (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, - ]; - - // One member of the 4.20 line answers without thinking, and it says so in its - // name. It has to be asked about before the general Grok 4 rule, which would - // otherwise claim the opposite of what the name states: - if(modelName.IndexOf("-non-reasoning") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // Grok 4 models take text and images. Reasoning is always on, only the - // reasoning effort can be configured. Video is not among their modalities: - // xAI serves audio, image, and video through models and APIs of their own, - // and the model pages of the 4.x line say "text, image" and nothing else: - if(modelName.IndexOf("grok-4") 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.StartsWith("grok-3-mini")) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if(modelName.StartsWith("grok-3")) - 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, - ]; - } - - // - // The open-weight models of OpenAI. Everything else named after an OpenAI model, the - // gpt-3.5 aliases included, was handed to their rules at the top of this function, which - // is why only gpt-oss is left here. - // - if (modelName.IndexOf("gpt-oss") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.WEB_SEARCH, - Capability.CHAT_COMPLETION_API, - ]; - - // - // NVIDIA Nemotron models. They are built for agentic workloads and are text - // only. The check also covers the quantized checkpoints such as - // NVIDIA-Nemotron-3.5-Lightning-30B-A3B-NVFP4. - // - if (modelName.IndexOf("nemotron") is not -1) - { - // The third generation thinks unless the request says otherwise, through - // enable_thinking=False: - if (modelName.IndexOf("nemotron-3") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // The earlier ones have to be asked to think: - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Google Gemma models. Gemma is the open-weights family, while Gemini is not, which is why - // Gemma is handled here and Gemini in the Google implementation. - // - if (modelName.IndexOf("gemma") is not -1) - { - // - // Every checkpoint of the Gemma 4 generation is multimodal; there is no text-only - // variant. Audio input is limited to the E2B, E4B, and 12B checkpoints. Video is not - // a modality of any of them: the model card lists text, image, and audio, and mentions - // video only as a sequence of frames somebody else has to cut it into. The models can - // think, but only when the request asks them to, by putting a think token at the start - // of the system prompt. - // - // Gemma 4 is also the first generation with tool calling of its own, with tool tokens - // in its chat template. The generations below have none. - // - if (modelName.IndexOf("gemma-4") is not -1 || - modelName.IndexOf("gemma4") is not -1) - { - if (modelName.IndexOf("e2b") is not -1 || - modelName.IndexOf("e4b") is not -1 || - modelName.IndexOf("12b") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.AUDIO_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Gemma 3 accepts images from the 4B checkpoint upwards; the 1B one is text-only, and - // the 3n checkpoints take audio on top. This generation does not reason. - // - // It does not call functions either. What Google documents for Gemma 3 is writing the - // tool descriptions into the prompt by hand, which is a different thing from what an - // OpenAI-compatible tools field does: the chat template has neither a tool role nor - // tool tokens, and Ollama refuses a request carrying tools for these models. Native - // tool calling starts with Gemma 4 above. - // - // The check for the small checkpoint looks for "-1b" rather than "1b", so that a name - // such as gemma-3-31b does not match it. - // - if (modelName.IndexOf("gemma-3") is not -1 || - modelName.IndexOf("gemma3") is not -1) - { - if (modelName.IndexOf("-1b") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("gemma-3n") is not -1 || - modelName.IndexOf("gemma3n") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.AUDIO_INPUT, - Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // The earlier generations take text only and were not built for tool usage: - // - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Z AI / GLM models: - // - if (modelName.IndexOf("glm") is not -1) - { - // - // Both version checks below accept a hyphen as the version separator as well: - // Mistral serves these models as glm-5-2 and zai-glm-5-2, while everybody else - // writes the version with a dot. - // - - // 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 || - 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 || - modelName.IndexOf("glm-5-2") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if(IsGlmVisionModelName(modelName)) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("glm-4-") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.OPTIONAL_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // MiniMax models. The M line is built for agentic work and thinks between its tool calls, - // which MiniMax calls interleaved thinking: the reasoning is part of the answer rather - // than something the request switches on. The older Text-01 answers directly. - // - if (modelName.IndexOf("minimax") is not -1) - { - if (modelName.IndexOf("minimax-m") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // IBM Granite models. The instruct line calls functions using the OpenAI function - // definition schema. From 4.2 on they think unless the request says otherwise; 3.2 and 3.3 - // have a thinking toggle which starts off, and the generations between them do not reason - // at all. For the vision checkpoints, tool calling is not documented. - // - if (modelName.IndexOf("granite") 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, - ]; - - if (modelName.IndexOf("granite-4.2") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("granite-3.2") is not -1 || - modelName.IndexOf("granite-3.3") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Cohere Command models. Most of the line calls functions, in one step and in several. - // Command A Vision is the exception Cohere states outright: tool use is not supported - // with it. - // - if (modelName.IndexOf("command-a") is not -1 || - modelName.IndexOf("command-r") is not -1) - { - if (modelName.IndexOf("command-a-vision") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - // Command A+ sees and thinks unless the request disables thinking: - if (modelName.IndexOf("command-a-plus") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("command-a-reasoning") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // The Aya models come from Cohere as well, but they were not trained with tool use in - // mind, which their documentation says in as many words: - // - if (modelName.IndexOf("aya-expanse") is not -1 || - modelName.IndexOf("aya-vision") is not -1) - { - if (modelName.IndexOf("aya-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.CHAT_COMPLETION_API, - ]; - } - - // - // AI2 OLMo models. The instruct checkpoints of the third generation carry a functions - // section in their chat template, and their default system prompt calls the model a - // function-calling assistant. The Think variants reason on top of that. OLMo 2 has no - // tool template. - // - if (modelName.IndexOf("olmo") is not -1) - { - if (modelName.IndexOf("olmo-3") is not -1 || modelName.IndexOf("olmo3") is not -1) - { - if (modelName.IndexOf("think") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // ByteDance Seed-OSS. Trained for agentic work, and it thinks with a budget the request - // can cap; the thinking itself cannot be turned off. - // - if (modelName.IndexOf("seed-oss") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // TII Falcon. The third generation was post-trained on function calls and reports its - // tool-calling benchmark in its own model card. Falcon-H1 documents no tool template, - // except for the small checkpoint built for nothing else. - // - if (modelName.IndexOf("falcon") is not -1) - { - if (modelName.IndexOf("falcon-h1") is not -1 && - modelName.IndexOf("tool-calling") is -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // InclusionAI Ling and Ring. Both call functions natively; Ring is the thinking line of - // the two, Ling the one which answers directly. Their names are only accepted where a - // name part begins, because "ling" also sits inside unrelated models such as Starling. - // - if (modelName.IndexOf("inclusionai") is not -1 || - modelName.StartsWith("ling-") || modelName.IndexOf("-ling-") is not -1 || - modelName.StartsWith("ring-") || modelName.IndexOf("-ring-") is not -1) - { - if (modelName.StartsWith("ring-") || modelName.IndexOf("-ring-") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.REASONING_BY_DEFAULT, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Baidu ERNIE. The thinking checkpoints call functions. The vision ones run in a thinking - // and a non-thinking mode, and tool calling is not documented for them. - // - if (modelName.IndexOf("ernie") is not -1) - { - if (modelName.IndexOf("-vl") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("thinking") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // Hugging Face SmolLM. The third generation ships a chat template with tool support and a - // thinking mode the request switches on. The earlier ones have neither. - // - if (modelName.IndexOf("smollm") is not -1) - { - if (modelName.IndexOf("smollm3") is not -1 || modelName.IndexOf("smollm-3") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // ServiceNow Apriel. The Thinker models see and always reason, because their default chat - // template opens the thinking channel. Tool tokens arrived with 1.6; 1.5 has none. - // - if (modelName.IndexOf("apriel") is not -1) - { - if (modelName.IndexOf("apriel-1.5") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // InternLM, and the InternVL family next to it. InternLM has a role of its own for tool - // answers in its chat template and a deep-thinking mode the request asks for. What is - // documented for InternVL is that it takes images. - // - if (modelName.IndexOf("internvl") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - if (modelName.IndexOf("internlm") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // Swiss AI Apertus 1.5. It calls functions in the OpenAI format, takes images and audio, - // and thinks when asked to. Note that its tool calling does not work while it thinks -- - // a combination these capabilities cannot express, so both are stated side by side. - // - if (modelName.IndexOf("apertus-v1.5") is not -1 || - modelName.IndexOf("apertus-1.5") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.AUDIO_INPUT, - Capability.TEXT_OUTPUT, - - Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // Microsoft Phi. The mini and multimodal checkpoints of the fourth generation call - // functions with tool tokens of their own. The 14B model has no tool role in its chat - // template at all, and neither do the reasoning checkpoints, which always think. - // - if (modelName.IndexOf("phi-4") is not -1 || modelName.IndexOf("phi4") is not -1) - { - if (modelName.IndexOf("multimodal") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.AUDIO_INPUT, - Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - // - // The reasoning checkpoints have to be checked before the mini one, because - // Phi-4-mini-reasoning is both and would otherwise be read as a mini model which - // does not think: - // - if (modelName.IndexOf("reasoning") is not -1) - { - if (modelName.IndexOf("vision") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.CHAT_COMPLETION_API, - ]; - } - - if (modelName.IndexOf("mini") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - } - - // - // The models we know do not call functions. They have to be named one by one, because the - // default below assumes that an unknown model does. None of these documents a tool - // template: the European and Spanish public models, the discontinued Occiglot, and the Yi - // line, whose open weights speak plain ChatML while only the closed Yi-Large-FC calls - // functions. Salamandra is the one with a variant built for it, which keeps its ability. - // - // The family names are only accepted where a name part begins, so that "yi" does not - // match every model which happens to contain those two letters. - // - if (modelName.IndexOf("teuken") is not -1 || - modelName.IndexOf("eurollm") is not -1 || - modelName.IndexOf("occiglot") is not -1 || - (modelName.IndexOf("salamandra") is not -1 && modelName.IndexOf("-tools") is -1) || - modelName.StartsWith("yi-") || modelName.IndexOf("-yi-") is not -1) - return - [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.CHAT_COMPLETION_API, - ]; - - // - // Default. A model we do not recognize is assumed to call functions, because by now that - // is what an instruction-tuned model does: every family added here in the last while - // could do it, and the ones which cannot are the exception listed above. Guessing the - // other way around was the safer choice while the ability was only shown as an icon, but - // it stopped being safe once it decides whether tools are offered at all -- a model which - // can use them would silently never be asked to. - // - // Anybody hitting the rare case where this guess is wrong turns tool calling off for that - // provider in the expert settings, and an organization can do the same for everybody. - // - return [ - Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, - Capability.FUNCTION_CALLING, - Capability.CHAT_COMPLETION_API, - ]; - } - - /// - /// Checks whether a GLM model is one of the vision models. - /// - /// - /// Z AI marks these by appending a "v" to the version number: glm-4v, glm-4.1v, glm-4.5v. - /// Looking for a bare "v" anywhere in the name, which is what this used to do, calls every - /// quantized build a vision model, because "nvfp4" carries one too, and so does the name of - /// more than one inference provider. - /// - /// The normalized model name. - /// True, when the version number is followed by a "v". - private static bool IsGlmVisionModelName(ReadOnlySpan modelName) - { - for (var index = 1; index < modelName.Length; index++) - if (modelName[index] is 'v' && char.IsAsciiDigit(modelName[index - 1])) - return true; - - return false; - } - - /// - /// Checks whether a model is named after one of the models OpenAI serves through its API. - /// - /// The normalized model name. - /// True, when the name belongs to an OpenAI cloud model. - private static bool IsOpenAICloudModelName(ReadOnlySpan modelName) - { - // - // The o-series carries no vendor word at all, which is why it counts only at the very - // front of the name. Looking for it anywhere would claim open weights which end on the - // same two characters, such as Marco-o1. - // - if (modelName.StartsWith("o1") || modelName.StartsWith("o3") || modelName.StartsWith("o4")) - return true; - - if (IsVersionedGptName(modelName)) - return true; - - // - // Providers which answer with a descriptive name carry the model in the middle of it, as - // in "01 - GPT-5.5 - great overall performance": - // - var separatorIndex = modelName.IndexOf("-gpt-"); - return separatorIndex is not -1 && IsVersionedGptName(modelName[(separatorIndex + 1)..]); - } - - /// - /// Checks whether a name starts with "gpt-" followed by a version. - /// - /// - /// The digit is what separates the models OpenAI serves from the open weights which borrow - /// the name: gpt-oss, gpt-neox, and gpt-j are none of theirs. - /// - /// The normalized model name, or a part of it. - /// True, when the name starts with a versioned GPT name. - private static bool IsVersionedGptName(ReadOnlySpan modelName) => - modelName.StartsWith("gpt-") && modelName.Length > 4 && char.IsAsciiDigit(modelName[4]); -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.Perplexity.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.Perplexity.cs deleted file mode 100644 index 4665d240..00000000 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.Perplexity.cs +++ /dev/null @@ -1,41 +0,0 @@ -using AIStudio.Provider; - -namespace AIStudio.Settings; - -public static partial class ProviderExtensions -{ - private static List GetModelCapabilitiesPerplexity(Model model) - { - var modelName = NormalizeModelId(model.Id).AsSpan(); - - // - // No Sonar model writes images. What looked like it does is the option to have the - // answer come with images: those are pictures the search found on the pages it read, - // handed back as links, not something the model drew. - // - if(modelName.IndexOf("reasoning") is not -1 || - modelName.IndexOf("deep-research") is not -1) - return - [ - Capability.TEXT_INPUT, - Capability.MULTIPLE_IMAGE_INPUT, - - Capability.TEXT_OUTPUT, - - Capability.ALWAYS_REASONING, - Capability.WEB_SEARCH, - Capability.CHAT_COMPLETION_API, - ]; - - return - [ - Capability.TEXT_INPUT, - Capability.MULTIPLE_IMAGE_INPUT, - - Capability.TEXT_OUTPUT, - - Capability.WEB_SEARCH, - Capability.CHAT_COMPLETION_API, - ]; - } -} \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ProviderExtensions.cs b/app/MindWork AI Studio/Settings/ProviderExtensions.cs index 1e926291..2d1d2c19 100644 --- a/app/MindWork AI Studio/Settings/ProviderExtensions.cs +++ b/app/MindWork AI Studio/Settings/ProviderExtensions.cs @@ -1,76 +1,11 @@ -using AIStudio.Models; +using AIStudio.Models; using AIStudio.Models.Registry; using AIStudio.Provider; -using AIStudio.Provider.HuggingFace; namespace AIStudio.Settings; public static partial class ProviderExtensions { - /// - /// The longest model ID we normalize without going to the heap. - /// - private const int MAX_STACK_ALLOCATED_MODEL_ID_LENGTH = 256; - - /// - /// Brings a model ID into the form the capability rules are written in. - /// - /// - /// Every provider names the same model differently, and the difference is rarely in the words: - /// it is in what sits between them. Ollama separates the variant with a colon - /// ("qwen3.8:27b-mlx"), Blablador answers with a whole sentence ("10 - Muse Glimmer 30b - the - /// newest META model"), Fireworks puts a path in front - /// ("accounts/fireworks/models/llama-v3p1-405b-instruct"), and the hubs use hyphens. Without - /// this, every rule would have to spell out each of those writings, which is what the Llama - /// block used to do with four variants of one check. - /// - /// The dots stay. They carry the version boundary: llama3 and llama3.1 are different models, - /// and only the latter calls functions. Dropping them would merge the two. - /// - /// The patterns in the rules are written in this normalized form already, which is why they - /// use lowercase and hyphens throughout. - /// - /// The model ID as the provider reports it. - /// The model ID in lowercase, with every separator written as a single hyphen. - private static string NormalizeModelId(string modelId) - { - if (string.IsNullOrWhiteSpace(modelId)) - return string.Empty; - - // - // Normalizing never makes a name longer, so the original length is always enough room. - // Model IDs are short, which is why the buffer lives on the stack: the longest ones we - // know of are the descriptive names Blablador answers with, at around 75 characters. A - // provider reporting something longer still gets a correct answer, just from the heap. - // - Span normalized = modelId.Length <= MAX_STACK_ALLOCATED_MODEL_ID_LENGTH - ? stackalloc char[modelId.Length] - : new char[modelId.Length]; - - var length = 0; - foreach (var character in modelId) - { - if (char.IsAsciiLetterOrDigit(character) || character is '.') - { - normalized[length++] = char.ToLowerInvariant(character); - continue; - } - - // Anything else separates two parts of the name. A leading separator, and a repeated - // one, say nothing and would only get in the way of the patterns: - if (length is 0 || normalized[length - 1] is '-') - continue; - - normalized[length++] = '-'; - } - - // A trailing separator carries no meaning either: - if (length > 0 && normalized[length - 1] is '-') - length--; - - return new string(normalized[..length]); - } - /// /// Everything the app knows about the model this provider instance is configured with. /// @@ -118,17 +53,6 @@ public static partial class ProviderExtensions : stated; } - /// - /// Get the capabilities of the model used by the configured provider. - /// - /// The configured provider. - /// The capabilities of the configured model. - public static List GetModelCapabilities(this Provider provider) - { - var automaticCapabilities = provider.UsedLLMProvider.GetModelCapabilities(provider.Model); - return provider.CapabilityOverrides?.ApplyTo(automaticCapabilities) ?? automaticCapabilities; - } - /// /// Get whether the model used by the configured provider accepts images as input. /// @@ -141,56 +65,4 @@ public static partial class ProviderExtensions /// The configured provider. /// true when the model accepts image input. public static bool SupportsImageInput(this Provider provider) => provider.GetModelProfile().HasAny(Capability.SINGLE_IMAGE_INPUT | Capability.MULTIPLE_IMAGE_INPUT); - - /// - /// Get the capabilities of a model for a specific provider. - /// - /// The LLM provider. - /// The model to get the capabilities for. - /// >The capabilities of the model. - public static List GetModelCapabilities(this LLMProviders provider, Model model) - { - if (string.IsNullOrWhiteSpace(model.Id)) - return []; - - return provider switch - { - LLMProviders.OPEN_AI => GetModelCapabilitiesOpenAI(model), - LLMProviders.MISTRAL => GetModelCapabilitiesMistral(model), - LLMProviders.ANTHROPIC => GetModelCapabilitiesAnthropic(model), - LLMProviders.GOOGLE => GetModelCapabilitiesGoogle(model), - LLMProviders.X => GetModelCapabilitiesOpenSource(model), - LLMProviders.DEEP_SEEK => GetModelCapabilitiesDeepSeek(model), - LLMProviders.ALIBABA_CLOUD => GetModelCapabilitiesAlibaba(model), - LLMProviders.PERPLEXITY => GetModelCapabilitiesPerplexity(model), - LLMProviders.OPEN_ROUTER => GetModelCapabilitiesGateway(model), - LLMProviders.HETZNER or LLMProviders.IONOS => GetModelCapabilitiesOpenSource(model), - - // - // LiteLLM is a gateway just like OpenRouter, and it names its models the same way: - // "vendor/model", e.g. "anthropic/claude-opus-5" or "azure/gpt-5.6". So we let the - // gateway detection handle it, which resolves the vendor prefix and asks the - // provider who really knows the model. Everything it cannot place is treated as - // an open source model, which is the right fallback for a freely named alias: - // - LLMProviders.LITE_LLM => GetModelCapabilitiesGateway(model), - - LLMProviders.GROQ or LLMProviders.FIREWORKS => GetModelCapabilitiesOpenSource(model), - - // - // Hugging Face names its models the way the hub does, "org/model", which is the same - // shape the other gateways use. So we let the gateway detection resolve the organization - // and ask the provider implementation which really knows the model. The routing suffix - // has to go first: it says which inference provider answers, not what the model is. - // - LLMProviders.HUGGINGFACE => GetModelCapabilitiesGateway(model.WithoutRoutingSuffix()), - - LLMProviders.HELMHOLTZ => GetModelCapabilitiesOpenSource(model), - LLMProviders.GWDG => GetModelCapabilitiesOpenSource(model), - - LLMProviders.SELF_HOSTED => GetModelCapabilitiesOpenSource(model), - - _ => [] - }; - } } \ No newline at end of file diff --git a/app/Tests/Models/CapabilityCharacterizationTests.cs b/app/Tests/Models/CapabilityCharacterizationTests.cs index e9d2c839..88954841 100644 --- a/app/Tests/Models/CapabilityCharacterizationTests.cs +++ b/app/Tests/Models/CapabilityCharacterizationTests.cs @@ -50,31 +50,6 @@ public sealed class CapabilityCharacterizationTests Assert.Fail($"The capabilities of {DescribeDifference(recorded, current)}{Environment.NewLine}{Environment.NewLine}The full result was written to {CapabilitySnapshot.ACTUAL_FILE_PATH}."); } - /// - /// Checks that the models the audit found wrong answers for are still answered the wrong way. - /// - /// - /// This test is written the other way round from every other one here, and it is meant to fail - /// the day the rebuild takes over: that is the signal that the entry has done its job. When it - /// does, swap the two assertions below for a single one comparing against AnswerWanted, and - /// drop AnswerToday from the entry. - /// - [Test] - public void TheAnswersTheAuditFoundWrongAreStillTheOldOnes() - { - Assert.Multiple(() => - { - foreach (var change in ExpectedChanges.ENTRIES) - { - var entry = new CorpusEntry(change.Provider, change.ModelId, CorpusOrigin.NAMED_BY_NO_RULE); - var current = CapabilitySnapshot.Describe(CapabilitySnapshot.AskTheCurrentRules(entry)); - - Assert.That(current, Is.EqualTo(CapabilitySnapshot.Describe(change.AnswerToday)), $"The wrong answer for {change.Provider} {change.ModelId} has changed. If that was on purpose, this entry is done: compare against AnswerWanted from now on."); - Assert.That(current, Is.Not.EqualTo(CapabilitySnapshot.Describe(change.AnswerWanted)), $"{change.Provider} {change.ModelId} already answers the way the rebuild is meant to. Move the entry into the snapshot."); - } - }); - } - /// /// The corpus entries the snapshot covers, which is all of them except the known-wrong ones. /// @@ -121,11 +96,12 @@ public sealed class CapabilityCharacterizationTests /// Splits a snapshot into what each line says about which model. /// /// - /// The provider and the model ID make up everything before the last separator, which is the one - /// place a split is safe: a model ID may contain anything, the capability list may not. + /// The provider and the model ID make up everything before the last two separators, which are + /// the one place a split is safe: a model ID may contain anything, the capability list and the + /// kind may not. /// /// The snapshot text. - /// The capability text of every line, keyed by provider and model. + /// What every line says about a model, keyed by provider and model. private static Dictionary ModelLinesOf(string snapshot) { var lines = new Dictionary(StringComparer.Ordinal); @@ -134,7 +110,11 @@ public sealed class CapabilityCharacterizationTests if (line.Length is 0 || line.StartsWith('#')) continue; - var separatorIndex = line.LastIndexOf(" | ", StringComparison.Ordinal); + var kindSeparatorIndex = line.LastIndexOf(" | ", StringComparison.Ordinal); + if (kindSeparatorIndex is -1) + continue; + + var separatorIndex = line.LastIndexOf(" | ", kindSeparatorIndex - 1, StringComparison.Ordinal); if (separatorIndex is -1) continue; diff --git a/app/Tests/Models/Corpus/CapabilitySnapshot.cs b/app/Tests/Models/Corpus/CapabilitySnapshot.cs index 92d08c52..22b7fb3d 100644 --- a/app/Tests/Models/Corpus/CapabilitySnapshot.cs +++ b/app/Tests/Models/Corpus/CapabilitySnapshot.cs @@ -28,16 +28,17 @@ public static class CapabilitySnapshot private const string HEADER = """ - # The capabilities the current rules answer with, for every model of the corpus. + # What the rules answer, for every model of the corpus. # # Generated. Do not edit by hand: run the SnapshotWriter test to write it anew, then read # the diff. Every line of it is a statement about a model which somebody has to agree with. # - # Columns are provider, model ID as the provider reports it, and the capabilities sorted by - # name. The ID stands here unchanged, so a line may well carry leading or trailing spaces. + # Columns are provider, model ID as the provider reports it, the capabilities sorted by + # name, and what the model is made for. The ID stands here unchanged, so a line may well + # carry leading or trailing spaces. # - # Models whose current answer the audit showed to be wrong are not in here. They live in - # ExpectedChanges.cs, together with the answer the rebuild has to arrive at. + # Models the audit found a wrong answer for are not in here. They live in + # ExpectedChanges.cs, together with the answer they have to arrive at. # """; @@ -78,17 +79,26 @@ public static class CapabilitySnapshot var lines = entries .OrderBy(entry => entry.Provider.ToString(), StringComparer.Ordinal) .ThenBy(entry => entry.ModelId, StringComparer.Ordinal) - .Select(entry => $"{entry.Provider} | {entry.ModelId} | {Describe(AskTheCurrentRules(entry))}"); + .Select(Line); return new StringBuilder(HEADER).AppendJoin('\n', lines).ToString(); } /// - /// Asks the current capability rules about one corpus entry. + /// Writes one corpus entry as a snapshot line. /// - /// The entry to ask about. - /// The capabilities the current rules answer with. - public static List AskTheCurrentRules(CorpusEntry entry) => entry.Provider.GetModelCapabilities(new Model(entry.ModelId, null)); + /// + /// The kind stands next to the capabilities rather than among them: the two answer different + /// questions, and a model changing from a chat model into an embedding one is a different kind + /// of news than a model gaining image input. + /// + /// The entry to write. + /// The line. + private static string Line(CorpusEntry entry) + { + var profile = entry.Provider.GetModelProfile(new Model(entry.ModelId, null)); + return $"{entry.Provider} | {entry.ModelId} | {Describe(RebuiltRules.AsCapabilities(profile))} | {profile.Kind}"; + } /// /// Writes capabilities the way a snapshot line does. diff --git a/app/Tests/Models/Corpus/CapabilitySnapshot.txt b/app/Tests/Models/Corpus/CapabilitySnapshot.txt index e7c69543..a2608796 100644 --- a/app/Tests/Models/Corpus/CapabilitySnapshot.txt +++ b/app/Tests/Models/Corpus/CapabilitySnapshot.txt @@ -1,270 +1,271 @@ -# The capabilities the current rules answer with, for every model of the corpus. +# What the rules answer, for every model of the corpus. # # Generated. Do not edit by hand: run the SnapshotWriter test to write it anew, then read # the diff. Every line of it is a statement about a model which somebody has to agree with. # -# Columns are provider, model ID as the provider reports it, and the capabilities sorted by -# name. The ID stands here unchanged, so a line may well carry leading or trailing spaces. +# Columns are provider, model ID as the provider reports it, the capabilities sorted by +# name, and what the model is made for. The ID stands here unchanged, so a line may well +# carry leading or trailing spaces. # -# Models whose current answer the audit showed to be wrong are not in here. They live in -# ExpectedChanges.cs, together with the answer the rebuild has to arrive at. +# Models the audit found a wrong answer for are not in here. They live in +# ExpectedChanges.cs, together with the answer they have to arrive at. # -ALIBABA_CLOUD | qvq-max | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen-max-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen-mt-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen-plus-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen-turbo-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen-vl-max | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen2.5-14b-instruct-1m | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen2.5-72b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen2.5-omni-7b | AUDIO_INPUT, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, SPEECH_OUTPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -ALIBABA_CLOUD | qwen2.5-vl-72b-instruct | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3-235b-a22b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3-omni-flash | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, SPEECH_OUTPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -ALIBABA_CLOUD | qwen3-vl-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3.5-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3.6-max | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -ALIBABA_CLOUD | qwen3.7-max | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3.7-max-2026-05-17 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3.7-max-2026-06-08 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -ALIBABA_CLOUD | qwen3.7-max-preview | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3.8-27b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwen3.8-flash | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -ALIBABA_CLOUD | qwen3.8-max | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -ALIBABA_CLOUD | qwq-32b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -ALIBABA_CLOUD | qwq-plus | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-3-5-haiku-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-3-5-sonnet-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-3-7-sonnet-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-3-opus-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-7-sonnet | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-fable-5-1 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-haiku-4-5-20251001 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-mythos-5 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-opus-4-0 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-opus-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-sonnet-4-0 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -ANTHROPIC | claude-sonnet-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -DEEP_SEEK | deepseek-chat | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -DEEP_SEEK | deepseek-reasoner | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -DEEP_SEEK | deepseek-v3.2-exp | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -DEEP_SEEK | deepseek-v4 | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -DEEP_SEEK | deepseek-v4-vision | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -FIREWORKS | accounts/fireworks/models/deepseek-v3 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -FIREWORKS | accounts/fireworks/models/llama-v3p1-405b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -FIREWORKS | accounts/fireworks/models/qwen3-235b-a22b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -GOOGLE | gemini-1.0-pro-vision | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -GOOGLE | gemini-2.0-flash | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-2.0-flash-live-001 | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, SPEECH_INPUT, SPEECH_OUTPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-2.5-flash | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-2.5-flash-image | CHAT_COMPLETION_API, IMAGE_OUTPUT, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -GOOGLE | gemini-2.5-flash-lite | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-2.5-pro | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-3-flash | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-3-pro | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-3-pro-image | ALWAYS_REASONING, CHAT_COMPLETION_API, IMAGE_OUTPUT, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -GOOGLE | gemini-3.1-flash-image | ALWAYS_REASONING, CHAT_COMPLETION_API, IMAGE_OUTPUT, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-flash-latest | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GOOGLE | gemini-pro-latest | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -GROQ | llama-3.3-70b-versatile | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -GROQ | moonshotai/kimi-k2-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -GROQ | openai/gpt-oss-120b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -GROQ | qwen/qwen3-32b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -GWDG | claude-sonnet-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -GWDG | deepseek-r1 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -GWDG | gemma-3-27b-it | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -GWDG | gpt-5.5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -GWDG | internvl2.5-8b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -GWDG | meta-llama-3.1-8b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -GWDG | qwen3-235b-a22b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -HELMHOLTZ | 1 - Llama3 405 the best general model | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -HELMHOLTZ | 10 - Muse Glimmer 30b - the newest META model | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -HELMHOLTZ | Qwen 3.8-27B with DFlash on haicluster | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -HELMHOLTZ | alias-qwen38-27b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -HETZNER | gpt-oss-120b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -HETZNER | qwen3-coder-30b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | HuggingFaceTB/SmolLM3-3B | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | Qwen/Qwen3.8-27B | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | deepseek-ai/DeepSeek-R1-Distill-Qwen-32B | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | google/gemma-4-31B-it:novita | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | meta-llama/Llama-4-Scout-17B-16E-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | meta-llama/Meta-Llama-3.1-405B-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | mistralai/Magistral-Small-2509 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -HUGGINGFACE | openai/gpt-oss-120b:fireworks-ai | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -IONOS | meta-llama/Llama-3.3-70B-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -IONOS | mistralai/Mistral-Small-24B-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -LITE_LLM | anthropic/claude-sonnet-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -LITE_LLM | azure/gpt-5.6 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -LITE_LLM | the-fast-one | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | codestral-2508 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | magistral-medium-2506 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | ministral-14b-2512 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | ministral-3b-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | ministral-8b-2410 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-large-2411 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-large-2512 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-large-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-medium-2505 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-medium-2508 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-medium-2604 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-medium-3-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-medium-3.5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-medium-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-saba-2502 | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-small-2501 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-small-2503 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-small-2603 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | mistral-small-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | open-mistral-nemo | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | pixtral-12b-2409 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | pixtral-large-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -MISTRAL | voxtral-small-2507 | CHAT_COMPLETION_API, FUNCTION_CALLING, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT -NONE | gpt-5.6 | (nothing) -OPEN_AI | | (nothing) -OPEN_AI | gpt-3.5-turbo | RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | gpt-3.5-turbo-16k | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | gpt-4 | RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | gpt-4-0613 | RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | gpt-4-turbo | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | gpt-4o | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-4o-audio-preview | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-4o-mini | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-4o-mini-search-preview | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-4o-search-preview | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5 | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5-mini | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5-nano | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5.1 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5.1-codex | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5.2 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5.3 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5.4 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5.5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-5.6 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-6-astra | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | gpt-6-astra-mini | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | o1 | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | o1-mini | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | o1-pro | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | o3 | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | o3-mini | ALWAYS_REASONING, FUNCTION_CALLING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_AI | o3-pro | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_AI | o4-mini | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_ROUTER | anthropic/claude-opus-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | deepseek/deepseek-chat-v3.1 | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | deepseek/deepseek-r1-distill-llama-70b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | google/gemini-3.7-flash | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -OPEN_ROUTER | google/gemma-4-31b-it | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | meta-llama/llama-4-maverick | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | minimax/minimax-m2 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | mistralai/mistral-large-3 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | moonshotai/kimi-k2-thinking | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | nvidia/nemotron-3-49b | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -OPEN_ROUTER | openai/gpt-5.6 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_ROUTER | openai/gpt-oss-120b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_ROUTER | perplexity/sonar-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -OPEN_ROUTER | qwen/qwen3.8-flash-next | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -OPEN_ROUTER | z-ai/glm-5.3 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -PERPLEXITY | sonar | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -PERPLEXITY | sonar-deep-research | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -PERPLEXITY | sonar-pro | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -PERPLEXITY | sonar-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -PERPLEXITY | sonar-reasoning-pro | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -SELF_HOSTED | | (nothing) -SELF_HOSTED | --- | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | 01-ai/yi-large | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | a-model-nobody-has-heard-of | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | apertus-1.5-8b | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | apriel-1.5-15b-thinker | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | apriel-1.6-15b-thinker | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | aya-expanse:8b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | aya-vision:8b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | command-a-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | command-a-reasoning | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | command-a-vision | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | command-a:111b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | command-r7b:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | deepseek-r1-distill-llama-70b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | deepseek-r1:32b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | deepseek-v2.5 | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | deepseek-v3.1:671b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | ernie-4.5-21b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | ernie-4.5-vl-28b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | ernie-x1.1-thinking | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | eurollm-9b-instruct | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | falcon-h1-1.5b-tool-calling | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | falcon-h1:7b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | falcon3:10b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | gemma2:9b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | gemma3:1b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | gemma3:27b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | gemma3n:e4b | AUDIO_INPUT, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | gemma4:31b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | gemma4:e2b | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | glm-4-9b-chat | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | glm-4.5v | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | glm-4.6:latest | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | glm-5-2 | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | glm-5.3-flash-nvfp4 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | gpt-oss:20b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH -SELF_HOSTED | granite3.2-vision:2b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | hunyuan:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | inclusionai/ling-mini-2.0 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | internlm3:8b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | internvl3-8b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | kimi-k2.7-code | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | kimi-k2:1t | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | kimi-k3:latest | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT -SELF_HOSTED | kimi-vl:16b | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | ling-1t | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | llama-3.1-405b-base | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | llama2:13b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | llama3.2-vision:11b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | llama3.2:3b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | magistral:24b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | minimax-m2:latest | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | minimax-text-01 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | ministral-8b-instruct-2410 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | mistral-nemo:12b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | mistral-small3.2:24b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | muse-glimmer-30b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | nemotron-3-49b | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | nvidia-nemotron-3.5-lightning-30b-a3b-nvfp4 | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | occiglot-7b-eu5 | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | olmo-3-32b-think | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | olmo2:13b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | olmo3:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | phi-4-mini-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | phi-4-multimodal-instruct | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | phi-4-reasoning-vision | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | phi3:14b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | phi4-mini:latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | phi4:14b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen2.5-vl-7b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen3-coder:30b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen3.5:32b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen3.6:32b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen3.8-2.4t-a95b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen3.8:27b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen3.8:27b-mlx | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwen3.8:latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | qwq:32b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | ring-1t | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | salamandra-7b-instruct | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | salamandra-7b-instruct-tools | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | seed-oss:36b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | smollm2:1.7b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | smollm3:3b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | starling-lm:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | tencent/hy3 | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | teuken-7b-instruct | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | voxtral-mini-3b | CHAT_COMPLETION_API, FUNCTION_CALLING, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT -SELF_HOSTED | yi-1.5:9b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT -X | grok-2-vision-1212 | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -X | grok-3 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -X | grok-3-mini | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT -X | grok-4 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -X | grok-4-fast-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -X | grok-4.20 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -X | grok-4.20-non-reasoning | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT -X | grok-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT \ No newline at end of file +ALIBABA_CLOUD | qvq-max | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen-max-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen-mt-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen-plus-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen-turbo-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen-vl-max | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen2.5-14b-instruct-1m | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen2.5-72b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen2.5-omni-7b | AUDIO_INPUT, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, SPEECH_OUTPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +ALIBABA_CLOUD | qwen2.5-vl-72b-instruct | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3-235b-a22b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3-omni-flash | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, SPEECH_OUTPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +ALIBABA_CLOUD | qwen3-vl-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3.5-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3.6-max | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +ALIBABA_CLOUD | qwen3.7-max | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3.7-max-2026-05-17 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3.7-max-2026-06-08 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +ALIBABA_CLOUD | qwen3.7-max-preview | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3.8-27b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwen3.8-flash | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +ALIBABA_CLOUD | qwen3.8-max | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +ALIBABA_CLOUD | qwq-32b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +ALIBABA_CLOUD | qwq-plus | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-3-5-haiku-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-3-5-sonnet-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-3-7-sonnet-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-3-opus-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-7-sonnet | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-fable-5-1 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-haiku-4-5-20251001 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-mythos-5 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-opus-4-0 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-opus-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-sonnet-4-0 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +ANTHROPIC | claude-sonnet-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +DEEP_SEEK | deepseek-chat | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +DEEP_SEEK | deepseek-reasoner | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +DEEP_SEEK | deepseek-v3.2-exp | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +DEEP_SEEK | deepseek-v4 | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +DEEP_SEEK | deepseek-v4-vision | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +FIREWORKS | accounts/fireworks/models/deepseek-v3 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +FIREWORKS | accounts/fireworks/models/llama-v3p1-405b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +FIREWORKS | accounts/fireworks/models/qwen3-235b-a22b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +GOOGLE | gemini-1.0-pro-vision | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +GOOGLE | gemini-2.0-flash | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-2.0-flash-live-001 | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, SPEECH_INPUT, SPEECH_OUTPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-2.5-flash | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-2.5-flash-image | CHAT_COMPLETION_API, IMAGE_OUTPUT, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | IMAGE_GENERATION +GOOGLE | gemini-2.5-flash-lite | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-2.5-pro | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-3-flash | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-3-pro | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-3-pro-image | ALWAYS_REASONING, CHAT_COMPLETION_API, IMAGE_OUTPUT, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | IMAGE_GENERATION +GOOGLE | gemini-3.1-flash-image | ALWAYS_REASONING, CHAT_COMPLETION_API, IMAGE_OUTPUT, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | IMAGE_GENERATION +GOOGLE | gemini-flash-latest | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GOOGLE | gemini-pro-latest | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +GROQ | llama-3.3-70b-versatile | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +GROQ | moonshotai/kimi-k2-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +GROQ | openai/gpt-oss-120b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +GROQ | qwen/qwen3-32b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +GWDG | claude-sonnet-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +GWDG | deepseek-r1 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +GWDG | gemma-3-27b-it | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +GWDG | gpt-5.5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +GWDG | internvl2.5-8b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +GWDG | meta-llama-3.1-8b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +GWDG | qwen3-235b-a22b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +HELMHOLTZ | 1 - Llama3 405 the best general model | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +HELMHOLTZ | 10 - Muse Glimmer 30b - the newest META model | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +HELMHOLTZ | Qwen 3.8-27B with DFlash on haicluster | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +HELMHOLTZ | alias-qwen38-27b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +HETZNER | gpt-oss-120b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +HETZNER | qwen3-coder-30b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | HuggingFaceTB/SmolLM3-3B | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | Qwen/Qwen3.8-27B | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | deepseek-ai/DeepSeek-R1-Distill-Qwen-32B | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | google/gemma-4-31B-it:novita | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | meta-llama/Llama-4-Scout-17B-16E-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | meta-llama/Meta-Llama-3.1-405B-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | mistralai/Magistral-Small-2509 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +HUGGINGFACE | openai/gpt-oss-120b:fireworks-ai | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +IONOS | meta-llama/Llama-3.3-70B-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +IONOS | mistralai/Mistral-Small-24B-Instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +LITE_LLM | anthropic/claude-sonnet-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +LITE_LLM | azure/gpt-5.6 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +LITE_LLM | the-fast-one | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | codestral-2508 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | magistral-medium-2506 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | ministral-14b-2512 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | ministral-3b-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | ministral-8b-2410 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-large-2411 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-large-2512 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-large-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-medium-2505 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-medium-2508 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-medium-2604 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-medium-3-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-medium-3.5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-medium-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-saba-2502 | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-small-2501 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-small-2503 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-small-2603 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | mistral-small-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | open-mistral-nemo | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | pixtral-12b-2409 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | pixtral-large-latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +MISTRAL | voxtral-small-2507 | CHAT_COMPLETION_API, FUNCTION_CALLING, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT | TRANSCRIPTION +NONE | gpt-5.6 | (nothing) | CHAT +OPEN_AI | | (nothing) | CHAT +OPEN_AI | gpt-3.5-turbo | RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | gpt-3.5-turbo-16k | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | gpt-4 | RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | gpt-4-0613 | RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | gpt-4-turbo | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | gpt-4o | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-4o-audio-preview | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | SPEECH_SYNTHESIS +OPEN_AI | gpt-4o-mini | FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-4o-mini-search-preview | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-4o-search-preview | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5 | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5-mini | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5-nano | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5.1 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5.1-codex | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5.2 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5.3 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5.4 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5.5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-5.6 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-6-astra | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | gpt-6-astra-mini | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | o1 | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | o1-mini | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | o1-pro | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | o3 | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | o3-mini | ALWAYS_REASONING, FUNCTION_CALLING, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_AI | o3-pro | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_AI | o4-mini | ALWAYS_REASONING, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, RESPONSES_API, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_ROUTER | anthropic/claude-opus-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | deepseek/deepseek-chat-v3.1 | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | deepseek/deepseek-r1-distill-llama-70b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | google/gemini-3.7-flash | ALWAYS_REASONING, AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +OPEN_ROUTER | google/gemma-4-31b-it | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | meta-llama/llama-4-maverick | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | minimax/minimax-m2 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | mistralai/mistral-large-3 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | moonshotai/kimi-k2-thinking | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | nvidia/nemotron-3-49b | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +OPEN_ROUTER | openai/gpt-5.6 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_ROUTER | openai/gpt-oss-120b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_ROUTER | perplexity/sonar-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +OPEN_ROUTER | qwen/qwen3.8-flash-next | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +OPEN_ROUTER | z-ai/glm-5.3 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +PERPLEXITY | sonar | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +PERPLEXITY | sonar-deep-research | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +PERPLEXITY | sonar-pro | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +PERPLEXITY | sonar-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +PERPLEXITY | sonar-reasoning-pro | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +SELF_HOSTED | | (nothing) | CHAT +SELF_HOSTED | --- | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | 01-ai/yi-large | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | a-model-nobody-has-heard-of | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | apertus-1.5-8b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | apriel-1.5-15b-thinker | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | apriel-1.6-15b-thinker | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | aya-expanse:8b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | aya-vision:8b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | command-a-plus | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | command-a-reasoning | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | command-a-vision | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | command-a:111b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | command-r7b:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | deepseek-r1-distill-llama-70b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | deepseek-r1:32b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | deepseek-v2.5 | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | deepseek-v3.1:671b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | ernie-4.5-21b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | ernie-4.5-vl-28b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | ernie-x1.1-thinking | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | eurollm-9b-instruct | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | falcon-h1-1.5b-tool-calling | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | falcon-h1:7b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | falcon3:10b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | gemma2:9b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | gemma3:1b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | gemma3:27b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | gemma3n:e4b | AUDIO_INPUT, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | gemma4:31b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | gemma4:e2b | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | glm-4-9b-chat | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | glm-4.5v | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | glm-4.6:latest | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | glm-5-2 | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | glm-5.3-flash-nvfp4 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | gpt-oss:20b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT +SELF_HOSTED | granite3.2-vision:2b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | hunyuan:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | inclusionai/ling-mini-2.0 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | internlm3:8b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | internvl3-8b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | kimi-k2.7-code | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | kimi-k2:1t | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | kimi-k3:latest | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, VIDEO_INPUT | CHAT +SELF_HOSTED | kimi-vl:16b | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | ling-1t | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | llama-3.1-405b-base | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | llama2:13b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | llama3.2-vision:11b | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | llama3.2:3b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | magistral:24b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | minimax-m2:latest | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | minimax-text-01 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | ministral-8b-instruct-2410 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | mistral-nemo:12b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | mistral-small3.2:24b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | muse-glimmer-30b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | nemotron-3-49b | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | nvidia-nemotron-3.5-lightning-30b-a3b-nvfp4 | CHAT_COMPLETION_API, FUNCTION_CALLING, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | occiglot-7b-eu5 | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | olmo-3-32b-think | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | olmo2:13b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | olmo3:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | phi-4-mini-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | phi-4-multimodal-instruct | AUDIO_INPUT, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | phi-4-reasoning-vision | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | phi3:14b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | phi4-mini:latest | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | phi4:14b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen2.5-vl-7b-instruct | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen3-coder:30b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen3.5:32b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen3.6:32b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen3.8-2.4t-a95b | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen3.8:27b | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen3.8:27b-mlx | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwen3.8:latest | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | qwq:32b | ALWAYS_REASONING, CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | ring-1t | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | salamandra-7b-instruct | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | salamandra-7b-instruct-tools | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | seed-oss:36b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | smollm2:1.7b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | smollm3:3b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | starling-lm:7b | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | tencent/hy3 | CHAT_COMPLETION_API, FUNCTION_CALLING, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | teuken-7b-instruct | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +SELF_HOSTED | voxtral-mini-3b | CHAT_COMPLETION_API, FUNCTION_CALLING, SPEECH_INPUT, TEXT_INPUT, TEXT_OUTPUT | TRANSCRIPTION +SELF_HOSTED | yi-1.5:9b | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-2-vision-1212 | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-3 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-3-mini | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-4 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-4-fast-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-4.20 | ALWAYS_REASONING, CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-4.20-non-reasoning | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT | CHAT +X | grok-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT \ No newline at end of file diff --git a/app/Tests/Models/Corpus/ExpectedChange.cs b/app/Tests/Models/Corpus/ExpectedChange.cs index 1b36db49..0c4cf958 100644 --- a/app/Tests/Models/Corpus/ExpectedChange.cs +++ b/app/Tests/Models/Corpus/ExpectedChange.cs @@ -13,7 +13,7 @@ namespace AIStudio.Tests.Models.Corpus; /// /// The provider the model is reached through. /// The model ID, exactly as it appears in the corpus. -/// What the current rules answer. Written down so a change here is seen. +/// What the rules being replaced answered. History now: the code that produced it is gone, so nothing checks this any more. It stays because an entry saying only what is right leaves the reader wondering what was wrong. /// What the rebuilt rules have to answer. /// Why the current answer is wrong, in one sentence. /// Where that can be checked. diff --git a/app/Tests/Models/PortingDifferenceTests.cs b/app/Tests/Models/PortingDifferenceTests.cs index f41fe9fe..bb071954 100644 --- a/app/Tests/Models/PortingDifferenceTests.cs +++ b/app/Tests/Models/PortingDifferenceTests.cs @@ -5,42 +5,23 @@ using AIStudio.Tests.Models.Corpus; namespace AIStudio.Tests.Models; /// -/// Holds the rebuilt rules against the old ones, over the whole corpus. +/// Holds the rules to the answers somebody decided on, over the whole corpus. /// /// -/// This is the test the whole rebuild is being carried by. Every model the new rules answer for has -/// to be answered exactly the way the old ones answer it -- except where the audit found the old -/// answer wrong, and there it has to be answered the way ExpectedChanges says instead. Anything -/// else is either a porting mistake or a decision somebody has to make on purpose and record. +/// While the old rules still stood, this was the test the rebuild was carried by: every model was +/// asked of both and the two had to agree, except where the audit had found the old answer wrong. +/// That comparison is over -- the old rules are gone, and the snapshot took over the job of noticing +/// when an answer changes. /// -/// While the porting was under way, this was scoped by two growing lists: first the providers whose -/// models had rules, then the vendors, because open weights arrive through gateways which serve -/// everybody. Both are gone now that every family exists. What is left is simpler and says more: -/// whatever a rule answers is compared, and whatever no rule answers has to stand in -/// LeftToTheDefault with a reason. +/// What remains is the part no snapshot can do, because it is about intent rather than about +/// answers. The models the audit found wrong have to end up where the audit said. Every model +/// reaching the global assumption has to be one somebody let reach it, and every model somebody +/// listed there has to still be reaching it. And no name may be claimed by two rules with the same +/// right. /// [TestFixture] public sealed class PortingDifferenceTests { - [Test] - public void EveryModelTheRulesAnswerForGetsExactlyTheAnswerItGetsToday() - { - var compared = ModelCorpus.ENTRIES.Where(IsAnswered).Where(entry => !IsKnownToBeWrong(entry)).ToList(); - - Assert.Multiple(() => - { - Assert.That(compared, Is.Not.Empty, "Nothing was compared at all, which would make this test green for the wrong reason."); - - foreach (var entry in compared) - { - var today = CapabilitySnapshot.Describe(CapabilitySnapshot.AskTheCurrentRules(entry)); - var rebuilt = CapabilitySnapshot.Describe(RebuiltRules.Ask(entry)); - - Assert.That(rebuilt, Is.EqualTo(today), $"{entry.Provider} \"{entry.ModelId}\" is answered differently by the rebuilt rules."); - } - }); - } - [Test] public void EveryModelTheAuditFoundWrongIsNowAnsweredTheWayItShouldBe() { diff --git a/app/Tests/Models/TestHarnessTests.cs b/app/Tests/Models/TestHarnessTests.cs index 0ed75de7..2414c027 100644 --- a/app/Tests/Models/TestHarnessTests.cs +++ b/app/Tests/Models/TestHarnessTests.cs @@ -32,8 +32,8 @@ public sealed class TestHarnessTests [Test] public void TheCapabilityApiOfTheAppIsReachable() { - var capabilities = LLMProviders.OPEN_AI.GetModelCapabilities(new Model("gpt-5.1", null)); + var profile = LLMProviders.OPEN_AI.GetModelProfile(new Model("gpt-5.1", null)); - Assert.That(capabilities, Does.Contain(Capability.FUNCTION_CALLING)); + Assert.That(profile.Has(Capability.FUNCTION_CALLING), Is.True); } } \ No newline at end of file diff --git a/app/Tests/Settings/ModelProfileChainTests.cs b/app/Tests/Settings/ModelProfileChainTests.cs index df365c59..908fadc8 100644 --- a/app/Tests/Settings/ModelProfileChainTests.cs +++ b/app/Tests/Settings/ModelProfileChainTests.cs @@ -35,29 +35,6 @@ public sealed class ModelProfileChainTests }); } - [Test] - public void NothingLeftToTheDefaultGainsACapabilityItDoesNotHaveToday() - { - // - // The direction which matters. These models lose things on the way over -- the thinking of - // a family nobody wrote down, the image input of another -- and every loss was decided and - // written next to the entry. What may never happen is the other direction: the switch-over - // handing a model an ability the old rules denied it, which nobody decided and nobody would - // see until a request comes back as an error. - // - Assert.Multiple(() => - { - foreach (var left in LeftToTheDefault.ENTRIES) - { - var entry = new CorpusEntry(left.Provider, left.ModelId, CorpusOrigin.NAMED_BY_NO_RULE); - var today = CapabilitySnapshot.AskTheCurrentRules(entry); - var gained = RebuiltRules.AsCapabilities(left.Provider.GetModelProfile(new Model(left.ModelId, null))).Except(today).ToList(); - - Assert.That(gained, Is.Empty, $"{left.Provider} \"{left.ModelId}\" would gain {string.Join(", ", gained)}, which nobody decided."); - } - }); - } - [Test] public void AModelNoRuleKnowsReadsAndWritesTextAndCallsFunctions() {