mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-17 15:57:59 +00:00
Fixed model capabilities and how model names are matched (#958)
This commit is contained in:
1 parent
e64c51bd57
commit
2834529753
16 files changed
+1164
-155
No files matched your search
@@ -6,13 +6,29 @@ public static partial class ProviderExtensions
|
||||
{
|
||||
private static List<Capability> GetModelCapabilitiesAlibaba(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
var modelName = NormalizeModelId(model.Id).AsSpan();
|
||||
|
||||
// Qwen models:
|
||||
if (modelName.StartsWith("qwen"))
|
||||
{
|
||||
// Check for omni models:
|
||||
// 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,
|
||||
@@ -20,10 +36,11 @@ public static partial class ProviderExtensions
|
||||
Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT,
|
||||
|
||||
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
// Check for Qwen 3.5:
|
||||
if(modelName.StartsWith("qwen3.5"))
|
||||
return
|
||||
@@ -47,6 +64,44 @@ public static partial class ProviderExtensions
|
||||
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"))
|
||||
{
|
||||
@@ -84,15 +139,28 @@ public static partial class ProviderExtensions
|
||||
];
|
||||
}
|
||||
|
||||
// Check for the 3.0 VL models:
|
||||
// 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"))
|
||||
@@ -106,15 +174,22 @@ public static partial class ProviderExtensions
|
||||
];
|
||||
}
|
||||
|
||||
// QwQ models:
|
||||
//
|
||||
// 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_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
|
||||
Capability.ALWAYS_REASONING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ public static partial class ProviderExtensions
|
||||
{
|
||||
private static List<Capability> GetModelCapabilitiesAnthropic(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
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"))
|
||||
|
||||
@@ -6,7 +6,7 @@ public static partial class ProviderExtensions
|
||||
{
|
||||
private static List<Capability> GetModelCapabilitiesDeepSeek(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
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)
|
||||
|
||||
@@ -32,7 +32,7 @@ public static partial class ProviderExtensions
|
||||
var separatorIndex = model.Id.IndexOf('/');
|
||||
var vendor = separatorIndex is -1 ? string.Empty : model.Id[..separatorIndex].ToLowerInvariant();
|
||||
var bareModel = separatorIndex is -1 ? model : model with { Id = model.Id[(separatorIndex + 1)..] };
|
||||
var bareModelName = bareModel.Id.ToLowerInvariant().AsSpan();
|
||||
var bareModelName = NormalizeModelId(bareModel.Id).AsSpan();
|
||||
|
||||
var capabilities = vendor switch
|
||||
{
|
||||
@@ -69,6 +69,10 @@ public static partial class ProviderExtensions
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
private static List<Capability> NormalizeForGateway(List<Capability> capabilities)
|
||||
{
|
||||
|
||||
@@ -6,14 +6,61 @@ public static partial class ProviderExtensions
|
||||
{
|
||||
private static List<Capability> GetModelCapabilitiesGoogle(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
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. The two rolling
|
||||
// aliases carry no version number and are listed separately:
|
||||
// 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")
|
||||
@@ -54,17 +101,6 @@ public static partial class ProviderExtensions
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Image generation:
|
||||
if(modelName.IndexOf("-2.0-flash-preview-image-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT,
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Realtime model:
|
||||
if(modelName.IndexOf("-2.0-flash-live-") is not -1)
|
||||
return
|
||||
@@ -78,17 +114,17 @@ public static partial class ProviderExtensions
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// The 2.0 flash models cannot call functions:
|
||||
if(modelName.IndexOf("-2.0-flash-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT,
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
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
|
||||
|
||||
@@ -64,7 +64,7 @@ public static partial class ProviderExtensions
|
||||
|
||||
private static List<Capability> GetModelCapabilitiesMistral(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
var modelName = NormalizeModelId(model.Id).AsSpan();
|
||||
|
||||
// Pixtral models are able to do process images:
|
||||
if (modelName.IndexOf("pixtral") is not -1)
|
||||
|
||||
@@ -6,7 +6,7 @@ public static partial class ProviderExtensions
|
||||
{
|
||||
private static List<Capability> GetModelCapabilitiesOpenAI(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
var modelName = NormalizeModelId(model.Id).AsSpan();
|
||||
|
||||
if (modelName is "gpt-4o-search-preview")
|
||||
return
|
||||
@@ -54,14 +54,6 @@ public static partial class ProviderExtensions
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if (modelName.StartsWith("chatgpt-4o-"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if (modelName.StartsWith("o3-mini"))
|
||||
return
|
||||
[
|
||||
@@ -116,8 +108,9 @@ public static partial class ProviderExtensions
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
|
||||
Capability.FUNCTION_CALLING, Capability.ALWAYS_REASONING,
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
@@ -132,12 +125,19 @@ public static partial class ProviderExtensions
|
||||
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.IMAGE_OUTPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING,
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API,
|
||||
@@ -147,8 +147,8 @@ public static partial class ProviderExtensions
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING, Capability.OPTIONAL_REASONING,
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.RESPONSES_API, Capability.CHAT_COMPLETION_API,
|
||||
@@ -180,7 +180,7 @@ public static partial class ProviderExtensions
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING, Capability.REASONING_BY_DEFAULT,
|
||||
Capability.WEB_SEARCH,
|
||||
@@ -197,7 +197,22 @@ public static partial class ProviderExtensions
|
||||
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,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -6,31 +6,34 @@ public static partial class ProviderExtensions
|
||||
{
|
||||
private static List<Capability> GetModelCapabilitiesPerplexity(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
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.IMAGE_OUTPUT,
|
||||
|
||||
|
||||
Capability.ALWAYS_REASONING,
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.MULTIPLE_IMAGE_INPUT,
|
||||
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.IMAGE_OUTPUT,
|
||||
|
||||
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
@@ -5,6 +5,70 @@ namespace AIStudio.Settings;
|
||||
|
||||
public static partial class ProviderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The longest model ID we normalize without going to the heap.
|
||||
/// </summary>
|
||||
private const int MAX_STACK_ALLOCATED_MODEL_ID_LENGTH = 256;
|
||||
|
||||
/// <summary>
|
||||
/// Brings a model ID into the form the capability rules are written in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <param name="modelId">The model ID as the provider reports it.</param>
|
||||
/// <returns>The model ID in lowercase, with every separator written as a single hyphen.</returns>
|
||||
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<char> 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]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the capabilities of the model used by the configured provider.
|
||||
/// </summary>
|
||||
|
||||
Reference in new issue
Block a user