mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-17 15:38:00 +00:00
Add one model host per provider and the unwrapping walk
This commit is contained in:
24 files changed
+1329
No files matched your search
@@ -0,0 +1,21 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Alibaba Cloud Model Studio.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Worth knowing about this one: several names mean a different model here than they do anywhere
|
||||
/// else. "qwq" is the commercial qwq-plus on Model Studio and the open weights everywhere else.
|
||||
/// That is not settled here but in the rules, which can bind themselves to a provider -- this host
|
||||
/// exists so that they have a provider to bind to.
|
||||
/// </remarks>
|
||||
public sealed class HostAlibabaCloud : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.ALIBABA_CLOUD;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://www.alibabacloud.com/help/en/model-studio/compatibility-of-openai-with-dashscope", new DateOnly(2026, 9, 11), "Models are named plainly, and the app reaches them through the OpenAI-compatible endpoint.");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Anthropic's own cloud.
|
||||
/// </summary>
|
||||
public sealed class HostAnthropic : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.ANTHROPIC;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.anthropic.com/en/api/messages", new DateOnly(2026, 9, 11), "Models are named plainly, and there is one API to reach them through.");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// DeepSeek's own platform.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It names its models by what they are for rather than by which checkpoint answers: "deepseek-chat"
|
||||
/// and "deepseek-reasoner" both point at whatever is current. Those are aliases, not wrappings, so
|
||||
/// there is nothing to take off -- the rules answer for the alias itself.
|
||||
/// </remarks>
|
||||
public sealed class HostDeepSeek : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.DEEP_SEEK;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://api-docs.deepseek.com/", new DateOnly(2026, 9, 11), "Models are named plainly, and the app reaches them through the OpenAI-compatible endpoint.");
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Fireworks AI, which puts a whole account path in front of every model.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// "accounts/fireworks/models/llama-v3p1-405b-instruct" is three segments of path and then the
|
||||
/// model. Nothing here counts them: the same taking-off-one-segment the gateways use is asked
|
||||
/// again until there is no path left. None of the three segments names a vendor we know, so none
|
||||
/// of them claims to.
|
||||
/// </remarks>
|
||||
public sealed class HostFireworks : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.FIREWORKS;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://fireworks.ai/models?show=Serverless", new DateOnly(2026, 9, 11), "Models are named \"accounts/<account>/models/<model>\", served through the OpenAI-compatible chat completion API.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor) => HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// The GWDG's academic cloud, which resells commercial models next to the open weights it runs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is the host the transport rule was written for. It offers Claude and GPT under the very
|
||||
/// names their vendors use -- "claude-sonnet-5", "gpt-5.5" -- so the rules recognize them and
|
||||
/// answer with everything those models can do at their vendor. Everything except the API: a request
|
||||
/// goes to Göttingen, not to San Francisco, and the Responses API is not served there.
|
||||
///
|
||||
/// The old code arrived at the same answer by having the open weights rules notice a Claude name
|
||||
/// and call the Anthropic rules, then correct the result. Here the recognizing and the correcting
|
||||
/// are two different things in two different places, which is why neither has to know about the
|
||||
/// other.
|
||||
/// </remarks>
|
||||
public sealed class HostGWDG : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.GWDG;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.hpc.gwdg.de/services/saia/index.html", new DateOnly(2026, 9, 11), "Open weights and resold commercial models alike are named plainly, and all of them are served through the OpenAI-compatible chat completion API.");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Google's own cloud.
|
||||
/// </summary>
|
||||
public sealed class HostGoogle : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.GOOGLE;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://ai.google.dev/gemini-api/docs/openai", new DateOnly(2026, 9, 11), "Models are named plainly, and the app reaches them through the OpenAI-compatible endpoint.");
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Groq, which serves open weights and writes some of their names the way the hub does.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Both spellings appear side by side in its catalog: "llama-3.3-70b-versatile" carries no
|
||||
/// organization, "moonshotai/kimi-k2-instruct" and "openai/gpt-oss-120b" do. Taking one off when
|
||||
/// there is one settles both without a rule per spelling.
|
||||
/// </remarks>
|
||||
public sealed class HostGroq : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.GROQ;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://console.groq.com/docs/api-reference", new DateOnly(2026, 9, 11), "Models are named either plainly or as the hub names them, and served through the OpenAI-compatible chat completion API.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor) => HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Helmholtz Blablador, which answers with the line a person would read in a menu.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// "1 - Llama3 405 the best general model" is a whole sentence, and the number in front is where
|
||||
/// the entry sits in the list -- it moves whenever the operator adds a model. Taking it off is the
|
||||
/// one thing this host does; the prose after the model name stays because there is no telling
|
||||
/// where the name ends and the recommendation begins.
|
||||
/// </remarks>
|
||||
public sealed class HostHelmholtz : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.HELMHOLTZ;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://sdlaml.pages.jsc.fz-juelich.de/ai/guides/blablador_api_access/", new DateOnly(2026, 9, 11), "Models are named as menu entries, \"<position> - <description>\", and served through the OpenAI-compatible chat completion API.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor)
|
||||
{
|
||||
declaredVendor = null;
|
||||
return HostNaming.TryStripMenuPosition(id, out inner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Hetzner's inference offering, which serves open weights under their plain names.
|
||||
/// </summary>
|
||||
public sealed class HostHetzner : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.HETZNER;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://experiments.hetzner.com/docs/inference", new DateOnly(2026, 9, 11), "Open weights named plainly, served through the OpenAI-compatible chat completion API.");
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// The Hugging Face router, whose names carry two wrappings rather than one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// "google/gemma-4-31B-it:novita" says three things at once: who published the weights, which model
|
||||
/// it is, and which inference provider should answer. The suffix goes first, because it is the
|
||||
/// outermost and because it says nothing about the model -- a request routed to Novita and one
|
||||
/// routed to Together AI reach the same weights.
|
||||
///
|
||||
/// This is the case the whole walk was written for. A host which took both off at once would work
|
||||
/// here and nowhere else; taking one off at a time is what also covers the account path Fireworks
|
||||
/// puts in front, without either host knowing about the other.
|
||||
/// </remarks>
|
||||
public sealed class HostHuggingFace : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.HUGGINGFACE;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://huggingface.co/docs/inference-providers/index", new DateOnly(2026, 9, 11), "Models are named as the hub names them, \"organization/model\", optionally followed by a colon and the inference provider to route to.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor)
|
||||
{
|
||||
declaredVendor = null;
|
||||
if (HostNaming.TryStripRoutingSuffix(id, out inner))
|
||||
return true;
|
||||
|
||||
return HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// The IONOS AI Model Hub, which keeps the hub spelling of the models it serves.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Its catalog reads like the hub's: "meta-llama/Llama-3.3-70B-Instruct",
|
||||
/// "mistralai/Mistral-Small-24B-Instruct". So the organization comes off, and with it comes the
|
||||
/// vendor -- stated rather than guessed from the name.
|
||||
/// </remarks>
|
||||
public sealed class HostIONOS : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.IONOS;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.ionos.com/cloud/ai/ai-model-hub", new DateOnly(2026, 9, 11), "Open weights named as the hub names them, served through the OpenAI-compatible chat completion API.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor) => HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// A LiteLLM proxy, which somebody operates themselves and names as they please.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Aliases here are whatever the operator wrote in their configuration. Many of them keep the
|
||||
/// "vendor/model" shape, some name the cloud instead of the vendor ("azure/gpt-5.6"), and some are
|
||||
/// a word ("the-fast-one"). Taking off a prefix costs nothing in the last case and helps in the
|
||||
/// first two, and a prefix nobody recognizes states no vendor -- so a name the operator invented
|
||||
/// is left for the rules to make what they can of.
|
||||
///
|
||||
/// This is also the host where a person is most likely to correct us by hand, which is what the
|
||||
/// expert settings are for: an alias only its operator can decipher is not something rules will
|
||||
/// ever get right.
|
||||
/// </remarks>
|
||||
public sealed class HostLiteLLM : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.LITE_LLM;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.litellm.ai/docs/proxy/user_keys", new DateOnly(2026, 9, 11), "Models are whatever the operator named them, served through the OpenAI-compatible chat completion API.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor) => HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Mistral's own platform, which by now also serves models Mistral did not build.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It names those under their plain names rather than prefixing them, so there is nothing to
|
||||
/// unwrap here. Which model it is remains a question for the rules; what this host settles is that
|
||||
/// whatever answers, it answers through Mistral's own API.
|
||||
/// </remarks>
|
||||
public sealed class HostMistral : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.MISTRAL;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.mistral.ai/api/", new DateOnly(2026, 9, 11), "Models are named plainly, its own and the open weights it hosts alike.");
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// OpenAI's own cloud, the one place where the Responses API is actually spoken.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is the single host that does not put its models on the ordinary chat completion API,
|
||||
/// because it is the single place the app sends a Responses API request from. Everywhere else a
|
||||
/// GPT model is reached -- a gateway, a reseller, somebody's own proxy -- it is reached through the
|
||||
/// ordinary API, and the host there says so.
|
||||
/// </remarks>
|
||||
public sealed class HostOpenAI : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.OPEN_AI;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://platform.openai.com/docs/api-reference/responses", new DateOnly(2026, 9, 11), "Models are named plainly, and both the Responses API and the chat completion API are served here.");
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Nothing is taken away: whichever of the two APIs a model states, it can be reached through
|
||||
/// it here.
|
||||
/// </remarks>
|
||||
public override ModelProfile ApplyTransport(in ModelProfile profile) => profile;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// OpenRouter, which serves other people's models and says whose they are.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The vendor prefix is the reason the old rules delegated between vendors in circles: a name such
|
||||
/// as "anthropic/claude-opus-5" had to be handed to whoever knew Claude, and the same for every
|
||||
/// other vendor. Here the prefix is simply taken off, and the vendor stated, and one set of rules
|
||||
/// answers the bare name -- no matter which provider it arrived from.
|
||||
/// </remarks>
|
||||
public sealed class HostOpenRouter : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.OPEN_ROUTER;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://openrouter.ai/docs/api-reference/overview", new DateOnly(2026, 9, 11), "Models are named \"vendor/model\", and every one of them is served through the OpenAI-compatible chat completion API.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor) => HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Perplexity's own API.
|
||||
/// </summary>
|
||||
public sealed class HostPerplexity : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.PERPLEXITY;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.perplexity.ai/api-reference/chat-completions-post", new DateOnly(2026, 9, 11), "Models are named plainly, and there is one API to reach them through.");
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using AIStudio.Models.Matching;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// Somebody's own engine: Ollama, LM Studio, vLLM, llama.cpp, or a proxy in front of them.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// vLLM serves whatever it was pointed at, and what it was pointed at is usually a hub repository:
|
||||
/// "meta-llama/Llama-3.3-70B-Instruct", "01-ai/yi-large". So the organization comes off here too.
|
||||
///
|
||||
/// The colon does not. Ollama writes the variant after it -- "qwen3.8:27b-mlx" -- and taking that
|
||||
/// off would leave a name which no longer says which build of the model is running. Only the host
|
||||
/// which actually has a router treats a colon as routing.
|
||||
///
|
||||
/// Whatever the engine can do beyond this, only the engine knows: how large a context window the
|
||||
/// operator configured, how many images it accepts. Those come from the model list of the running
|
||||
/// installation, not from a rule written here.
|
||||
/// </remarks>
|
||||
public sealed class HostSelfHosted : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.SELF_HOSTED;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html", new DateOnly(2026, 9, 11), "Models are named as the operator loaded them, often as a hub repository, and served through the OpenAI-compatible chat completion API.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor) => HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Models.Hosting.Hosts;
|
||||
|
||||
/// <summary>
|
||||
/// xAI's own API, where Grok comes from.
|
||||
/// </summary>
|
||||
public sealed class HostX : ModelHost
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override LLMProviders Provider => LLMProviders.X;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ModelSource Source => new("https://docs.x.ai/docs/api-reference", new DateOnly(2026, 9, 11), "Models are named plainly, and the app reaches them through the OpenAI-compatible endpoint.");
|
||||
}
|
||||
Reference in new issue
Block a user