mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-13 20:33:49 +00:00
Added Requesty as a provider (#1006)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
1 parent
f912af321f
commit
2bd3d41421
44 files changed
+518
-65
No files matched your search
@@ -51,6 +51,7 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
|||||||
- [Hetzner](https://experiments.hetzner.com) (experimental inference API running open-source models in the EU)
|
- [Hetzner](https://experiments.hetzner.com) (experimental inference API running open-source models in the EU)
|
||||||
- [IONOS](https://cloud.ionos.com/managed/ai-model-hub) (AI Model Hub running open-source models in Germany)
|
- [IONOS](https://cloud.ionos.com/managed/ai-model-hub) (AI Model Hub running open-source models in Germany)
|
||||||
- [LiteLLM](https://www.litellm.ai/) (an AI gateway you run yourself, in front of models from many providers)
|
- [LiteLLM](https://www.litellm.ai/) (an AI gateway you run yourself, in front of models from many providers)
|
||||||
|
- [Requesty](https://www.requesty.ai/) (a hosted AI gateway from the UK, in front of models from many providers)
|
||||||
- [Hugging Face](https://huggingface.co/) using their [inference providers](https://huggingface.co/docs/inference-providers/index) such as Cerebras, Nebius, Sambanova, Novita, Hyperbolic, Together AI, Fireworks, Hugging Face
|
- [Hugging Face](https://huggingface.co/) using their [inference providers](https://huggingface.co/docs/inference-providers/index) such as Cerebras, Nebius, Sambanova, Novita, Hyperbolic, Together AI, Fireworks, Hugging Face
|
||||||
- Self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), and [vLLM](https://github.com/vllm-project/vllm)
|
- Self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), and [vLLM](https://github.com/vllm-project/vllm)
|
||||||
- [Groq](https://groq.com/)
|
- [Groq](https://groq.com/)
|
||||||
|
|||||||
@@ -9817,15 +9817,15 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3341379752"] = "Cost-effective"
|
|||||||
-- Flexibility
|
-- Flexibility
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibility"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibility"
|
||||||
|
|
||||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3832631168"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
|
||||||
|
|
||||||
-- Privacy
|
-- Privacy
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Privacy"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Privacy"
|
||||||
|
|
||||||
-- Voice input
|
-- Voice input
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4194938377"] = "Voice input"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4194938377"] = "Voice input"
|
||||||
|
|
||||||
|
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Requesty, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4287735840"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Requesty, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||||
|
|
||||||
-- You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems.
|
-- You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T457410099"] = "You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T457410099"] = "You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems."
|
||||||
|
|
||||||
@@ -10585,6 +10585,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3805270964"] = "Our Titans"
|
|||||||
-- Moderation, Design, Wiki, and Documentation
|
-- Moderation, Design, Wiki, and Documentation
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3821668394"] = "Moderation, Design, Wiki, and Documentation"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3821668394"] = "Moderation, Design, Wiki, and Documentation"
|
||||||
|
|
||||||
|
-- Thanks, Thibault, for bringing Requesty to AI Studio as a new provider.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3856759487"] = "Thanks, Thibault, for bringing Requesty to AI Studio as a new provider."
|
||||||
|
|
||||||
-- Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder.
|
-- Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T4042792376"] = "Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T4042792376"] = "Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder."
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ namespace AIStudio.Models.Hosting;
|
|||||||
/// A wrapping is worked on the name as the provider reported it, never on the normalized one. That
|
/// A wrapping is worked on the name as the provider reported it, never on the normalized one. That
|
||||||
/// is not a detail: normalizing writes every separator as a hyphen, so "meta-llama/Llama-3.3-70B"
|
/// is not a detail: normalizing writes every separator as a hyphen, so "meta-llama/Llama-3.3-70B"
|
||||||
/// and "meta-llama-llama-3.3-70b" are the same text afterwards and nobody can say where the
|
/// and "meta-llama-llama-3.3-70b" are the same text afterwards and nobody can say where the
|
||||||
/// organization ended. The slash, the colon, and the spaces are the whole evidence, and they only
|
/// organization ended. The slash, the colon, the at sign, and the spaces are the whole evidence, and
|
||||||
/// exist in the original.
|
/// they only exist in the original.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static class HostNaming
|
public static class HostNaming
|
||||||
{
|
{
|
||||||
@@ -20,10 +20,15 @@ public static class HostNaming
|
|||||||
private const char ORGANIZATION_SEPARATOR = '/';
|
private const char ORGANIZATION_SEPARATOR = '/';
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// What separates the model from the inference provider it should be routed to.
|
/// What separates the model from how a request for it should be routed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const char ROUTING_SEPARATOR = ':';
|
private const char ROUTING_SEPARATOR = ':';
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What separates the model from the region it should run in.
|
||||||
|
/// </summary>
|
||||||
|
private const char REGION_SEPARATOR = '@';
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Takes the organization off a hub style name.
|
/// Takes the organization off a hub style name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -66,20 +71,46 @@ public static class HostNaming
|
|||||||
/// Takes the routing suffix off a name.
|
/// Takes the routing suffix off a name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The suffix says where a request goes, not what the model is: "google/gemma-4-31B-it:novita"
|
/// The suffix says how a request is served, not what the model is: "google/gemma-4-31B-it:novita"
|
||||||
/// is the same model as "google/gemma-4-31B-it". Names on the hub carry no colon of their own,
|
/// is the same model as "google/gemma-4-31B-it". On the Hugging Face router it names the
|
||||||
/// so the last one always starts the suffix. This is not true everywhere -- Ollama writes the
|
/// inference provider; Requesty writes the service tier there, as in "openai/gpt-6-sol:flex", or
|
||||||
/// variant after a colon, as in "qwen3.8:27b-mlx", and taking that off would throw away which
|
/// a preset reasoning effort, as in "openai/o4-mini:high". Names at both hosts carry no colon of
|
||||||
/// model it is. That is why only the host which has a router asks for this.
|
/// their own, so the last one always starts the suffix. This is not true everywhere -- Ollama
|
||||||
|
/// writes the variant after a colon, as in "qwen3.8:27b-mlx", and taking that off would throw
|
||||||
|
/// away which model it is. That is why only the hosts which put a suffix there ask for this.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="id">The name as it arrived.</param>
|
/// <param name="id">The name as it arrived.</param>
|
||||||
/// <param name="inner">The name without its routing suffix.</param>
|
/// <param name="inner">The name without its routing suffix.</param>
|
||||||
/// <returns>True, when there was a suffix to take off.</returns>
|
/// <returns>True, when there was a suffix to take off.</returns>
|
||||||
public static bool TryStripRoutingSuffix(in ModelId id, out ModelId inner)
|
public static bool TryStripRoutingSuffix(in ModelId id, out ModelId inner) => TryStripSuffix(id, ROUTING_SEPARATOR, out inner);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Takes the region suffix off a name.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The suffix says where a request runs, not what the model is:
|
||||||
|
/// "bedrock/claude-opus-5@eu-central-1" is the same model as "bedrock/claude-opus-5", served from
|
||||||
|
/// Frankfurt. Requesty writes names this way, and every suffix it used on the day we checked was a
|
||||||
|
/// region: "eu", "us", or a data center of AWS, Azure, or Google. No model name carries an at sign
|
||||||
|
/// of its own, so the last one always starts the suffix.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="id">The name as it arrived.</param>
|
||||||
|
/// <param name="inner">The name without its region suffix.</param>
|
||||||
|
/// <returns>True, when there was a suffix to take off.</returns>
|
||||||
|
public static bool TryStripRegionSuffix(in ModelId id, out ModelId inner) => TryStripSuffix(id, REGION_SEPARATOR, out inner);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Takes off everything from the last separator on.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The name as it arrived.</param>
|
||||||
|
/// <param name="separator">The character which starts the suffix.</param>
|
||||||
|
/// <param name="inner">The name without its suffix.</param>
|
||||||
|
/// <returns>True, when there was a suffix to take off.</returns>
|
||||||
|
private static bool TryStripSuffix(in ModelId id, char separator, out ModelId inner)
|
||||||
{
|
{
|
||||||
inner = id;
|
inner = id;
|
||||||
|
|
||||||
var separatorIndex = id.Original.LastIndexOf(ROUTING_SEPARATOR);
|
var separatorIndex = id.Original.LastIndexOf(separator);
|
||||||
if (separatorIndex is -1)
|
if (separatorIndex is -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -166,8 +197,8 @@ public static class HostNaming
|
|||||||
"cohere" or "coherelabs" or "cohereforai" => ModelVendor.COHERE,
|
"cohere" or "coherelabs" or "cohereforai" => ModelVendor.COHERE,
|
||||||
"moonshot" or "moonshotai" => ModelVendor.MOONSHOT_AI,
|
"moonshot" or "moonshotai" => ModelVendor.MOONSHOT_AI,
|
||||||
"tencent" or "tencent-hunyuan" => ModelVendor.TENCENT,
|
"tencent" or "tencent-hunyuan" => ModelVendor.TENCENT,
|
||||||
"z-ai" or "zai-org" => ModelVendor.Z_AI,
|
"z-ai" or "zai" or "zai-org" => ModelVendor.Z_AI,
|
||||||
"minimax" or "minimaxai" => ModelVendor.MINIMAX,
|
"minimax" or "minimaxai" or "minimaxi" => ModelVendor.MINIMAX,
|
||||||
"ai2" or "allenai" => ModelVendor.AI2,
|
"ai2" or "allenai" => ModelVendor.AI2,
|
||||||
"bytedance" or "bytedance-seed" => ModelVendor.BYTE_DANCE,
|
"bytedance" or "bytedance-seed" => ModelVendor.BYTE_DANCE,
|
||||||
"tii" or "tiiuae" => ModelVendor.TII,
|
"tii" or "tiiuae" => ModelVendor.TII,
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using AIStudio.Models.Matching;
|
||||||
|
using AIStudio.Provider;
|
||||||
|
|
||||||
|
namespace AIStudio.Models.Hosting.Hosts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requesty, a gateway which serves other people's models and says who runs them.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A name comes as "provider/model", and the provider is whoever runs the model: the vendor's own
|
||||||
|
/// API, as in "openai/gpt-6-sol", or a cloud serving somebody else's model, as in
|
||||||
|
/// "bedrock/claude-opus-5" or "vertex/gemini-3.5-flash". A cloud which serves weights from the hub
|
||||||
|
/// keeps the hub's organization as a second segment, as in "novita/qwen/qwen3.8-max". The walk
|
||||||
|
/// takes off one segment at a time, and the innermost one which names a vendor is the one that
|
||||||
|
/// counts.
|
||||||
|
///
|
||||||
|
/// Where the provider is also a vendor, its name states that vendor. That is right whenever a
|
||||||
|
/// vendor serves its own models, which is nearly always. It is wrong where the cloud of a vendor
|
||||||
|
/// resells other models, as in "tencent/kimi-k3": on the day we checked, about one in twenty of the
|
||||||
|
/// stated vendors was wrong this way. The price is small, because no built-in rule insists on a
|
||||||
|
/// vendor -- only a model plugin which does would miss such a name and leave it to the built-in
|
||||||
|
/// rules.
|
||||||
|
///
|
||||||
|
/// Behind the model, a name may carry one more thing which does not change the model: the region it
|
||||||
|
/// runs in after an at sign, as in "@eu-central-1" or "@eu", or the service tier or a preset
|
||||||
|
/// reasoning effort after a colon, as in ":flex" or ":high". These come off first. Managed
|
||||||
|
/// policies, which Requesty curates and routes across several providers, carry no prefix at all,
|
||||||
|
/// as in "claude-opus-5@eu", so they only lose their region.
|
||||||
|
/// </remarks>
|
||||||
|
public sealed class HostRequesty : ModelHost
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override LLMProviders Provider => LLMProviders.REQUESTY;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override ModelSource Source => new("https://docs.requesty.ai/features/eu-routing", new DateOnly(2026, 10, 12), "Models are named \"provider/model\", where the provider is whoever runs the model, optionally followed by a region after an at sign; the model list adds a service tier or a reasoning effort after a colon, and managed policies without any prefix. All of them are served through the OpenAI-compatible chat completion API.");
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool TryUnwrap(in ModelId id, out ModelId inner, out ModelVendor? declaredVendor)
|
||||||
|
{
|
||||||
|
declaredVendor = null;
|
||||||
|
if (HostNaming.TryStripRegionSuffix(id, out inner))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (HostNaming.TryStripRoutingSuffix(id, out inner))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return HostNaming.TrySplitOrganization(id, out inner, out declaredVendor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ public partial class Home : MSGComponentBase
|
|||||||
this.itemsAdvantages = [
|
this.itemsAdvantages = [
|
||||||
new(this.T("Free of charge"), this.T("The app is free to use, both for personal and commercial purposes.")),
|
new(this.T("Free of charge"), this.T("The app is free to use, both for personal and commercial purposes.")),
|
||||||
new(this.T("Democratization of AI"), this.T("We want to contribute to the democratization of AI. MindWork AI Studio runs even on low-cost hardware, including computers around 100 EUR such as Raspberry Pi. This makes the app and its full feature set accessible to people and families with limited budgets. You can start with local LLMs or use affordable cloud models.")),
|
new(this.T("Democratization of AI"), this.T("We want to contribute to the democratization of AI. MindWork AI Studio runs even on low-cost hardware, including computers around 100 EUR such as Raspberry Pi. This makes the app and its full feature set accessible to people and families with limited budgets. You can start with local LLMs or use affordable cloud models.")),
|
||||||
new(this.T("Independence"), this.T("You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.")),
|
new(this.T("Independence"), this.T("You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Requesty, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.")),
|
||||||
new(this.T("Assistants"), this.T("You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants.")),
|
new(this.T("Assistants"), this.T("You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants.")),
|
||||||
new(T("Tools"), T("AI models can use tools on their own, such as Web Search, Read Web Page, or Search Confluence. When you ask something a model cannot answer from what it knows, it looks it up and answers with the sources it used. You decide which tools a model may use, and you can watch it work.")),
|
new(T("Tools"), T("AI models can use tools on their own, such as Web Search, Read Web Page, or Search Confluence. When you ask something a model cannot answer from what it knows, it looks it up and answers with the sources it used. You decide which tools a model may use, and you can watch it work.")),
|
||||||
new(T("Voice input"), T("Dictate your messages instead of typing them, and have your recordings as well as audio and video files transcribed. You choose the transcription provider, including one you host yourself.")),
|
new(T("Voice input"), T("Dictate your messages instead of typing them, and have your recordings as well as audio and video files transcribed. You choose the transcription provider, including one you host yourself.")),
|
||||||
|
|||||||
@@ -97,6 +97,7 @@
|
|||||||
<Supporter Name="j-erler" Type="SupporterType.INDIVIDUAL" URL="https://github.com/j-erler" Acknowledgment="@T("Thank you, Jens, for the Batch Processing Assistant, the exact token count in the chat, and everything else you are building for AI Studio with so much dedication.")"/>
|
<Supporter Name="j-erler" Type="SupporterType.INDIVIDUAL" URL="https://github.com/j-erler" Acknowledgment="@T("Thank you, Jens, for the Batch Processing Assistant, the exact token count in the chat, and everything else you are building for AI Studio with so much dedication.")"/>
|
||||||
<Supporter Name="SimonBpunkt" Type="SupporterType.INDIVIDUAL" URL="https://github.com/SimonBpunkt" Acknowledgment="@T("Thanks, Simon, for the detection patterns against prompt injection, their translations, and a fix for switching between light and dark mode.")"/>
|
<Supporter Name="SimonBpunkt" Type="SupporterType.INDIVIDUAL" URL="https://github.com/SimonBpunkt" Acknowledgment="@T("Thanks, Simon, for the detection patterns against prompt injection, their translations, and a fix for switching between light and dark mode.")"/>
|
||||||
<Supporter Name="prodmanpd" Type="SupporterType.INDIVIDUAL" URL="https://github.com/prodmanpd" Acknowledgment="@T("Thank you, Prodman, for bringing LiteLLM to AI Studio as a new provider.")"/>
|
<Supporter Name="prodmanpd" Type="SupporterType.INDIVIDUAL" URL="https://github.com/prodmanpd" Acknowledgment="@T("Thank you, Prodman, for bringing LiteLLM to AI Studio as a new provider.")"/>
|
||||||
|
<Supporter Name="Thibaultjaigu" Type="SupporterType.INDIVIDUAL" URL="https://github.com/Thibaultjaigu" Acknowledgment="@T("Thanks, Thibault, for bringing Requesty to AI Studio as a new provider.")"/>
|
||||||
</MudList>
|
</MudList>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|||||||
@@ -1009,8 +1009,8 @@ CONFIG["SETTINGS"] = {}
|
|||||||
-- Configure a custom confidence scheme.
|
-- Configure a custom confidence scheme.
|
||||||
-- This is used when DataConfidence.ConfidenceScheme is set to CUSTOM.
|
-- This is used when DataConfidence.ConfidenceScheme is set to CUSTOM.
|
||||||
-- Allowed provider keys are: OPEN_AI, ANTHROPIC, MISTRAL, GOOGLE, X, DEEP_SEEK, ALIBABA_CLOUD,
|
-- Allowed provider keys are: OPEN_AI, ANTHROPIC, MISTRAL, GOOGLE, X, DEEP_SEEK, ALIBABA_CLOUD,
|
||||||
-- PERPLEXITY, OPEN_ROUTER, HETZNER, IONOS, LITE_LLM, FIREWORKS, GROQ, HUGGINGFACE, SELF_HOSTED,
|
-- PERPLEXITY, OPEN_ROUTER, HETZNER, IONOS, LITE_LLM, REQUESTY, FIREWORKS, GROQ, HUGGINGFACE,
|
||||||
-- HELMHOLTZ, GWDG
|
-- SELF_HOSTED, HELMHOLTZ, GWDG
|
||||||
-- Allowed confidence values are: UNTRUSTED, VERY_LOW, LOW, MODERATE, MEDIUM, HIGH
|
-- Allowed confidence values are: UNTRUSTED, VERY_LOW, LOW, MODERATE, MEDIUM, HIGH
|
||||||
--
|
--
|
||||||
-- Replaces, does not merge: a configuration with a higher priority replaces the whole
|
-- Replaces, does not merge: a configuration with a higher priority replaces the whole
|
||||||
@@ -1030,6 +1030,7 @@ CONFIG["SETTINGS"] = {}
|
|||||||
-- ["HETZNER"] = "HIGH",
|
-- ["HETZNER"] = "HIGH",
|
||||||
-- ["IONOS"] = "HIGH",
|
-- ["IONOS"] = "HIGH",
|
||||||
-- ["LITE_LLM"] = "MODERATE",
|
-- ["LITE_LLM"] = "MODERATE",
|
||||||
|
-- ["REQUESTY"] = "MODERATE",
|
||||||
-- ["FIREWORKS"] = "MODERATE",
|
-- ["FIREWORKS"] = "MODERATE",
|
||||||
-- ["GROQ"] = "MODERATE",
|
-- ["GROQ"] = "MODERATE",
|
||||||
-- ["HUGGINGFACE"] = "MODERATE",
|
-- ["HUGGINGFACE"] = "MODERATE",
|
||||||
|
|||||||
+6
-3
@@ -9819,15 +9819,15 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3341379752"] = "Kosteneffizient"
|
|||||||
-- Flexibility
|
-- Flexibility
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibilität"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibilität"
|
||||||
|
|
||||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3832631168"] = "Sie sind nicht an einen einzigen Anbieter gebunden. Stattdessen können Sie den Anbieter wählen, der am besten zu Ihren Anforderungen passt. Derzeit unterstützen wir OpenAI (GPT-6, GPT-5 usw.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimentell), IONOS, LiteLLM, Hugging Face, Groq, Fireworks sowie selbst gehostete Modelle mit vLLM, llama.cpp, ollama oder LM Studio. Für Wissenschaftlerinnen und Wissenschaftler sowie Mitarbeitende von Forschungseinrichtungen unterstützen wir außerdem die KI-Dienste von Helmholtz und GWDG. Diese sind über föderierte Logins wie eduGAIN für alle 18 Helmholtz-Zentren, die Max-Planck-Gesellschaft, die meisten deutschen sowie viele internationale Universitäten verfügbar."
|
|
||||||
|
|
||||||
-- Privacy
|
-- Privacy
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Datenschutz"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Datenschutz"
|
||||||
|
|
||||||
-- Voice input
|
-- Voice input
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4194938377"] = "Spracheingabe"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4194938377"] = "Spracheingabe"
|
||||||
|
|
||||||
|
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Requesty, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4287735840"] = "Sie sind nicht an einen einzigen Anbieter gebunden. Stattdessen können Sie den Anbieter wählen, der am besten zu Ihren Anforderungen passt. Derzeit unterstützen wir OpenAI (GPT-6, GPT-5 usw.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimentell), IONOS, LiteLLM, Requesty, Hugging Face, Groq, Fireworks sowie selbst gehostete Modelle mit vLLM, llama.cpp, ollama oder LM Studio. Für Wissenschaftlerinnen und Wissenschaftler sowie Mitarbeitende von Forschungseinrichtungen unterstützen wir außerdem die KI-Dienste von Helmholtz und GWDG. Diese sind über föderierte Logins wie eduGAIN für alle 18 Helmholtz-Zentren, die Max-Planck-Gesellschaft, die meisten deutschen sowie viele internationale Universitäten verfügbar."
|
||||||
|
|
||||||
-- You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems.
|
-- You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T457410099"] = "Sie können über die Einstellungen zur Anbietervertrauenswürdigkeit steuern, welche Anbieter Ihre Daten erhalten. Zum Beispiel können Sie für das Schreiben von E-Mails einen anderen Schutzlevel festlegen als für allgemeine Chats usw. Außerdem garantieren die meisten Anbieter, dass Ihre Daten nicht zum Trainieren neuer KI-Systeme verwendet werden."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T457410099"] = "Sie können über die Einstellungen zur Anbietervertrauenswürdigkeit steuern, welche Anbieter Ihre Daten erhalten. Zum Beispiel können Sie für das Schreiben von E-Mails einen anderen Schutzlevel festlegen als für allgemeine Chats usw. Außerdem garantieren die meisten Anbieter, dass Ihre Daten nicht zum Trainieren neuer KI-Systeme verwendet werden."
|
||||||
|
|
||||||
@@ -10587,6 +10587,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3805270964"] = "Unsere Titanen"
|
|||||||
-- Moderation, Design, Wiki, and Documentation
|
-- Moderation, Design, Wiki, and Documentation
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3821668394"] = "Moderation, Design, Wiki und Dokumentation"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3821668394"] = "Moderation, Design, Wiki und Dokumentation"
|
||||||
|
|
||||||
|
-- Thanks, Thibault, for bringing Requesty to AI Studio as a new provider.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3856759487"] = "Danke, Thibault, dass du Requesty als neuen Anbieter in AI Studio integriert hast."
|
||||||
|
|
||||||
-- Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder.
|
-- Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T4042792376"] = "Danke, Nils, dass du dir die Zeit genommen hast, Rust zu lernen, die Grundlage für die lokale Suche zu schaffen und Assistenten-Plugins sowie den Assistenten-Builder zu entwickeln."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T4042792376"] = "Danke, Nils, dass du dir die Zeit genommen hast, Rust zu lernen, die Grundlage für die lokale Suche zu schaffen und Assistenten-Plugins sowie den Assistenten-Builder zu entwickeln."
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -9819,15 +9819,15 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3341379752"] = "Cost-effective"
|
|||||||
-- Flexibility
|
-- Flexibility
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibility"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibility"
|
||||||
|
|
||||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3832631168"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
|
||||||
|
|
||||||
-- Privacy
|
-- Privacy
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Privacy"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Privacy"
|
||||||
|
|
||||||
-- Voice input
|
-- Voice input
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4194938377"] = "Voice input"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4194938377"] = "Voice input"
|
||||||
|
|
||||||
|
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Requesty, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T4287735840"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT-6, GPT-5, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), OpenRouter, Hetzner (experimental), IONOS, LiteLLM, Requesty, Hugging Face, Groq, Fireworks, and self-hosted models using vLLM, llama.cpp, ollama, or LM Studio. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||||
|
|
||||||
-- You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems.
|
-- You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T457410099"] = "You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T457410099"] = "You can control which providers receive your data using the provider confidence settings. For example, you can set different protection levels for writing emails compared to general chats, etc. Additionally, most providers guarantee that they won't use your data to train new AI systems."
|
||||||
|
|
||||||
@@ -10587,6 +10587,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3805270964"] = "Our Titans"
|
|||||||
-- Moderation, Design, Wiki, and Documentation
|
-- Moderation, Design, Wiki, and Documentation
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3821668394"] = "Moderation, Design, Wiki, and Documentation"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3821668394"] = "Moderation, Design, Wiki, and Documentation"
|
||||||
|
|
||||||
|
-- Thanks, Thibault, for bringing Requesty to AI Studio as a new provider.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T3856759487"] = "Thanks, Thibault, for bringing Requesty to AI Studio as a new provider."
|
||||||
|
|
||||||
-- Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder.
|
-- Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T4042792376"] = "Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::SUPPORTERS::T4042792376"] = "Thanks, Nils, for taking the time to learn Rust, for building the foundation for local retrieval, and for assistant plugins and the Assistant Builder."
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ MODELS = {}
|
|||||||
-- -- the same name means different things depending on who serves it.
|
-- -- the same name means different things depending on who serves it.
|
||||||
-- -- Allowed values are: OPEN_AI, ANTHROPIC, MISTRAL, GOOGLE, X, DEEP_SEEK,
|
-- -- Allowed values are: OPEN_AI, ANTHROPIC, MISTRAL, GOOGLE, X, DEEP_SEEK,
|
||||||
-- -- ALIBABA_CLOUD, PERPLEXITY, OPEN_ROUTER, HETZNER, IONOS, LITE_LLM,
|
-- -- ALIBABA_CLOUD, PERPLEXITY, OPEN_ROUTER, HETZNER, IONOS, LITE_LLM,
|
||||||
-- -- FIREWORKS, GROQ, HUGGINGFACE, SELF_HOSTED, HELMHOLTZ, GWDG
|
-- -- REQUESTY, FIREWORKS, GROQ, HUGGINGFACE, SELF_HOSTED, HELMHOLTZ, GWDG
|
||||||
-- ["ONLY_ON"] = "SELF_HOSTED",
|
-- ["ONLY_ON"] = "SELF_HOSTED",
|
||||||
--
|
--
|
||||||
-- -- Optional: restrict this entry to models of one vendor. Only gateways
|
-- -- Optional: restrict this entry to models of one vendor. Only gateways
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_C
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, n
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
throw this.CreateEmbeddingsNotSupportedException();
|
throw this.CreateEmbeddingsNotSupportedException();
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public sealed class ProviderDeepSeek() : BaseProvider(LLMProviders.DEEP_SEEK, ne
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
throw this.CreateEmbeddingsNotSupportedException();
|
throw this.CreateEmbeddingsNotSupportedException();
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class ProviderFireworks() : BaseProvider(LLMProviders.FIREWORKS, new Uri(
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
throw this.CreateEmbeddingsNotSupportedException();
|
throw this.CreateEmbeddingsNotSupportedException();
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public sealed class ProviderGWDG() : BaseProvider(LLMProviders.GWDG, new Uri("ht
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class ProviderGoogle() : BaseProvider(LLMProviders.GOOGLE, new Uri("https
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class ProviderGroq() : BaseProvider(LLMProviders.GROQ, new Uri("https://a
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
throw this.CreateEmbeddingsNotSupportedException();
|
throw this.CreateEmbeddingsNotSupportedException();
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public sealed class ProviderHelmholtz() : BaseProvider(LLMProviders.HELMHOLTZ, n
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ public sealed class ProviderHuggingFace : BaseProvider
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public sealed class ProviderIONOS() : BaseProvider(LLMProviders.IONOS, new Uri("
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public enum LLMProviders
|
|||||||
HETZNER = 16,
|
HETZNER = 16,
|
||||||
IONOS = 17,
|
IONOS = 17,
|
||||||
LITE_LLM = 18,
|
LITE_LLM = 18,
|
||||||
|
REQUESTY = 19,
|
||||||
|
|
||||||
FIREWORKS = 5,
|
FIREWORKS = 5,
|
||||||
GROQ = 6,
|
GROQ = 6,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using AIStudio.Provider.Mistral;
|
|||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
using AIStudio.Provider.OpenRouter;
|
using AIStudio.Provider.OpenRouter;
|
||||||
using AIStudio.Provider.Perplexity;
|
using AIStudio.Provider.Perplexity;
|
||||||
|
using AIStudio.Provider.Requesty;
|
||||||
using AIStudio.Provider.SelfHosted;
|
using AIStudio.Provider.SelfHosted;
|
||||||
using AIStudio.Provider.X;
|
using AIStudio.Provider.X;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
@@ -62,6 +63,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.HETZNER => "Hetzner (Experimental)",
|
LLMProviders.HETZNER => "Hetzner (Experimental)",
|
||||||
LLMProviders.IONOS => "IONOS",
|
LLMProviders.IONOS => "IONOS",
|
||||||
LLMProviders.LITE_LLM => "LiteLLM",
|
LLMProviders.LITE_LLM => "LiteLLM",
|
||||||
|
LLMProviders.REQUESTY => "Requesty",
|
||||||
|
|
||||||
LLMProviders.GROQ => "Groq",
|
LLMProviders.GROQ => "Groq",
|
||||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||||
@@ -100,6 +102,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.HETZNER => "Hetzner",
|
LLMProviders.HETZNER => "Hetzner",
|
||||||
LLMProviders.IONOS => "IONOS",
|
LLMProviders.IONOS => "IONOS",
|
||||||
LLMProviders.LITE_LLM => "LiteLLM",
|
LLMProviders.LITE_LLM => "LiteLLM",
|
||||||
|
LLMProviders.REQUESTY => "Requesty",
|
||||||
|
|
||||||
LLMProviders.GROQ => "Groq",
|
LLMProviders.GROQ => "Groq",
|
||||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||||
@@ -170,6 +173,18 @@ public static class LLMProvidersExtensions
|
|||||||
// of a self-hosted model here and let the user assign the level themselves.
|
// of a self-hosted model here and let the user assign the level themselves.
|
||||||
LLMProviders.LITE_LLM => Confidence.USER_OPERATED_GATEWAY.WithSources("https://docs.litellm.ai/docs/data_security").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
LLMProviders.LITE_LLM => Confidence.USER_OPERATED_GATEWAY.WithSources("https://docs.litellm.ai/docs/data_security").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
// Requesty Ltd is based in London and works under the UK GDPR and the EU GDPR. Unless logging
|
||||||
|
// is switched off, it keeps requests for up to 30 days, and it may use the content of free
|
||||||
|
// accounts for training. Where a model runs depends on the model rather than on Requesty:
|
||||||
|
// names ending in "@eu" or in another European region stay in Europe, the others may not.
|
||||||
|
// That is why we state the region but leave the trust level unknown.
|
||||||
|
LLMProviders.REQUESTY => Confidence.UNKNOWN.WithRegion("Europe, United Kingdom").WithSources(
|
||||||
|
"https://www.requesty.ai/privacy",
|
||||||
|
"https://www.requesty.ai/privacy/subprocessors",
|
||||||
|
"https://www.requesty.ai/dpa",
|
||||||
|
"https://www.requesty.ai/terms"
|
||||||
|
).WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
LLMProviders.HELMHOLTZ => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
LLMProviders.HELMHOLTZ => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
@@ -197,6 +212,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.GWDG => true,
|
LLMProviders.GWDG => true,
|
||||||
LLMProviders.OPEN_ROUTER => true,
|
LLMProviders.OPEN_ROUTER => true,
|
||||||
LLMProviders.LITE_LLM => true,
|
LLMProviders.LITE_LLM => true,
|
||||||
|
LLMProviders.REQUESTY => true,
|
||||||
|
|
||||||
//
|
//
|
||||||
// Providers that do not support embeddings:
|
// Providers that do not support embeddings:
|
||||||
@@ -237,6 +253,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.HELMHOLTZ => true,
|
LLMProviders.HELMHOLTZ => true,
|
||||||
LLMProviders.GROQ => true,
|
LLMProviders.GROQ => true,
|
||||||
LLMProviders.LITE_LLM => true,
|
LLMProviders.LITE_LLM => true,
|
||||||
|
LLMProviders.REQUESTY => true,
|
||||||
|
|
||||||
//
|
//
|
||||||
// Providers that support transcription but provide no OpenAI-compatible API yet:
|
// Providers that support transcription but provide no OpenAI-compatible API yet:
|
||||||
@@ -319,6 +336,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.HETZNER => new ProviderHetzner { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.HETZNER => new ProviderHetzner { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.IONOS => new ProviderIONOS { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.IONOS => new ProviderIONOS { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.LITE_LLM => new ProviderLiteLLM(hostname) { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.LITE_LLM => new ProviderLiteLLM(hostname) { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
|
LLMProviders.REQUESTY => new ProviderRequesty { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
|
|
||||||
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, TokenizerPath = tokenizerPath, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||||
@@ -357,6 +375,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.OPEN_ROUTER => "https://openrouter.ai/keys",
|
LLMProviders.OPEN_ROUTER => "https://openrouter.ai/keys",
|
||||||
LLMProviders.HETZNER => "https://experiments.hetzner.com",
|
LLMProviders.HETZNER => "https://experiments.hetzner.com",
|
||||||
LLMProviders.IONOS => "https://cloud.ionos.com/compute/sign-up",
|
LLMProviders.IONOS => "https://cloud.ionos.com/compute/sign-up",
|
||||||
|
LLMProviders.REQUESTY => "https://app.requesty.ai/api-keys",
|
||||||
|
|
||||||
LLMProviders.GROQ => "https://console.groq.com/",
|
LLMProviders.GROQ => "https://console.groq.com/",
|
||||||
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
|
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
|
||||||
@@ -384,6 +403,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.HUGGINGFACE => "https://huggingface.co/settings/billing",
|
LLMProviders.HUGGINGFACE => "https://huggingface.co/settings/billing",
|
||||||
LLMProviders.HETZNER => "https://experiments.hetzner.com",
|
LLMProviders.HETZNER => "https://experiments.hetzner.com",
|
||||||
LLMProviders.IONOS => "https://dcd.ionos.com/latest/?page=dcd-ai-model-hub",
|
LLMProviders.IONOS => "https://dcd.ionos.com/latest/?page=dcd-ai-model-hub",
|
||||||
|
LLMProviders.REQUESTY => "https://app.requesty.ai/",
|
||||||
|
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
@@ -404,6 +424,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.HUGGINGFACE => true,
|
LLMProviders.HUGGINGFACE => true,
|
||||||
LLMProviders.HETZNER => true,
|
LLMProviders.HETZNER => true,
|
||||||
LLMProviders.IONOS => true,
|
LLMProviders.IONOS => true,
|
||||||
|
LLMProviders.REQUESTY => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
@@ -492,6 +513,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.HETZNER => true,
|
LLMProviders.HETZNER => true,
|
||||||
LLMProviders.IONOS => true,
|
LLMProviders.IONOS => true,
|
||||||
LLMProviders.LITE_LLM => true,
|
LLMProviders.LITE_LLM => true,
|
||||||
|
LLMProviders.REQUESTY => true,
|
||||||
|
|
||||||
LLMProviders.GROQ => true,
|
LLMProviders.GROQ => true,
|
||||||
LLMProviders.FIREWORKS => true,
|
LLMProviders.FIREWORKS => true,
|
||||||
@@ -521,6 +543,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.OPEN_ROUTER => true,
|
LLMProviders.OPEN_ROUTER => true,
|
||||||
LLMProviders.HETZNER => true,
|
LLMProviders.HETZNER => true,
|
||||||
LLMProviders.IONOS => true,
|
LLMProviders.IONOS => true,
|
||||||
|
LLMProviders.REQUESTY => true,
|
||||||
|
|
||||||
LLMProviders.GROQ => true,
|
LLMProviders.GROQ => true,
|
||||||
LLMProviders.FIREWORKS => true,
|
LLMProviders.FIREWORKS => true,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public static class LLMProvidersIconExtensions
|
|||||||
LLMProviders.HETZNER => $"{ICON_ROOT}/hetzner.svg",
|
LLMProviders.HETZNER => $"{ICON_ROOT}/hetzner.svg",
|
||||||
LLMProviders.IONOS => $"{ICON_ROOT}/ionos.svg",
|
LLMProviders.IONOS => $"{ICON_ROOT}/ionos.svg",
|
||||||
LLMProviders.LITE_LLM => $"{ICON_ROOT}/litellm.svg",
|
LLMProviders.LITE_LLM => $"{ICON_ROOT}/litellm.svg",
|
||||||
|
LLMProviders.REQUESTY => $"{ICON_ROOT}/requesty.svg",
|
||||||
LLMProviders.GROQ => $"{ICON_ROOT}/groq.svg",
|
LLMProviders.GROQ => $"{ICON_ROOT}/groq.svg",
|
||||||
LLMProviders.FIREWORKS => $"{ICON_ROOT}/fireworks.svg",
|
LLMProviders.FIREWORKS => $"{ICON_ROOT}/fireworks.svg",
|
||||||
LLMProviders.HUGGINGFACE => $"{ICON_ROOT}/hugging-face.svg",
|
LLMProviders.HUGGINGFACE => $"{ICON_ROOT}/hugging-face.svg",
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, new U
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Provider.Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Provider.Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ namespace AIStudio.Provider.OpenRouter;
|
|||||||
|
|
||||||
public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER, new Uri("https://openrouter.ai/api/v1/"), ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY, LOGGER)
|
public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER, new Uri("https://openrouter.ai/api/v1/"), ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY, LOGGER)
|
||||||
{
|
{
|
||||||
private const string PROJECT_WEBSITE = "https://github.com/MindWorkAI/AI-Studio";
|
|
||||||
private const string PROJECT_NAME = "MindWork AI Studio";
|
|
||||||
|
|
||||||
private static readonly ILogger<ProviderOpenRouter> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderOpenRouter>();
|
private static readonly ILogger<ProviderOpenRouter> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderOpenRouter>();
|
||||||
|
|
||||||
#region Implementation of IProvider
|
#region Implementation of IProvider
|
||||||
@@ -54,12 +51,7 @@ public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER
|
|||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
headersAction: headers =>
|
headersAction: ProjectAttribution.Apply,
|
||||||
{
|
|
||||||
// Set custom headers for project identification:
|
|
||||||
headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
|
||||||
headers.Add("X-Title", PROJECT_NAME);
|
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
@@ -78,7 +70,7 @@ public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
@@ -120,12 +112,7 @@ public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER
|
|||||||
.Select(n => new Model(n.Id, n.Name))
|
.Select(n => new Model(n.Id, n.Name))
|
||||||
.Where(model => model.IsChatModel(this.Provider)),
|
.Where(model => model.IsChatModel(this.Provider)),
|
||||||
apiKeyProvisional,
|
apiKeyProvisional,
|
||||||
requestConfigurator: (request, secretKey) =>
|
requestConfigurator: ConfigureRequest,
|
||||||
{
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
|
||||||
request.Headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
|
||||||
request.Headers.Add("X-Title", PROJECT_NAME);
|
|
||||||
},
|
|
||||||
listingFactory: modelResponse => modelResponse.Data.Select(n => ModelListing.For(n.Id, n.ContextWindowTokens)),
|
listingFactory: modelResponse => modelResponse.Data.Select(n => ModelListing.For(n.Id, n.ContextWindowTokens)),
|
||||||
token: token);
|
token: token);
|
||||||
}
|
}
|
||||||
@@ -153,12 +140,13 @@ public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER
|
|||||||
"embeddings/models",
|
"embeddings/models",
|
||||||
modelResponse => modelResponse.Data.Select(n => new Model(n.Id, n.Name)),
|
modelResponse => modelResponse.Data.Select(n => new Model(n.Id, n.Name)),
|
||||||
apiKeyProvisional,
|
apiKeyProvisional,
|
||||||
requestConfigurator: (request, secretKey) =>
|
requestConfigurator: ConfigureRequest,
|
||||||
{
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
|
||||||
request.Headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
|
||||||
request.Headers.Add("X-Title", PROJECT_NAME);
|
|
||||||
},
|
|
||||||
token: token);
|
token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ConfigureRequest(HttpRequestMessage request, string secretKey)
|
||||||
|
{
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||||
|
ProjectAttribution.Apply(request.Headers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ public sealed class ProviderPerplexity() : BaseProvider(LLMProviders.PERPLEXITY,
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
throw this.CreateEmbeddingsNotSupportedException();
|
throw this.CreateEmbeddingsNotSupportedException();
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The headers which tell a gateway that a request comes from AI Studio.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// OpenRouter and Requesty read the same two headers: "HTTP-Referer" for the address of the app,
|
||||||
|
/// and "X-Title" for its name. OpenRouter lists apps by them in its rankings (App Attribution), and
|
||||||
|
/// Requesty tags the analytics of its users with them (Origin Headers), so a person can tell the
|
||||||
|
/// requests of AI Studio apart from those of their other tools. OpenRouter calls the second header
|
||||||
|
/// "X-OpenRouter-Title" by now and still reads "X-Title"; staying with the name both gateways
|
||||||
|
/// understand is what lets them share this.
|
||||||
|
///
|
||||||
|
/// Neither header says anything about the user: both values are the same for every installation.
|
||||||
|
/// </remarks>
|
||||||
|
public static class ProjectAttribution
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The address the gateways identify the app by.
|
||||||
|
/// </summary>
|
||||||
|
private const string PROJECT_WEBSITE = "https://github.com/MindWorkAI/AI-Studio";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name the gateways show for the app.
|
||||||
|
/// </summary>
|
||||||
|
private const string PROJECT_NAME = "MindWork AI Studio";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the attribution headers to a request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="headers">The headers of the request.</param>
|
||||||
|
public static void Apply(HttpRequestHeaders headers)
|
||||||
|
{
|
||||||
|
headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
||||||
|
headers.Add("X-Title", PROJECT_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,6 +108,7 @@ public static class ReasoningDispatcher
|
|||||||
LLMProviders.HETZNER or
|
LLMProviders.HETZNER or
|
||||||
LLMProviders.IONOS or
|
LLMProviders.IONOS or
|
||||||
LLMProviders.LITE_LLM or
|
LLMProviders.LITE_LLM or
|
||||||
|
LLMProviders.REQUESTY or
|
||||||
LLMProviders.X or
|
LLMProviders.X or
|
||||||
LLMProviders.DEEP_SEEK or
|
LLMProviders.DEEP_SEEK or
|
||||||
LLMProviders.GROQ or
|
LLMProviders.GROQ or
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
using AIStudio.Chat;
|
||||||
|
using AIStudio.Models.Live;
|
||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.Requesty;
|
||||||
|
|
||||||
|
public sealed class ProviderRequesty() : BaseProvider(LLMProviders.REQUESTY, new Uri("https://router.requesty.ai/v1/"), ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY, LOGGER)
|
||||||
|
{
|
||||||
|
private static readonly ILogger<ProviderRequesty> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderRequesty>();
|
||||||
|
|
||||||
|
#region Implementation of IProvider
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Id => LLMProviders.REQUESTY.ToSecretId();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string InstanceName { get; set; } = "Requesty";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool HasModelLoadingCapability => true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
|
"Requesty",
|
||||||
|
chatModel,
|
||||||
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters, tools) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.CreateSettingsProvider(chatModel));
|
||||||
|
|
||||||
|
return new ChatCompletionAPIRequest
|
||||||
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
|
||||||
|
// Build the messages:
|
||||||
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
|
// Right now, we only support streaming completions:
|
||||||
|
Stream = true,
|
||||||
|
Tools = tools,
|
||||||
|
AdditionalApiParameters = apiParameters
|
||||||
|
};
|
||||||
|
},
|
||||||
|
headersAction: ProjectAttribution.Apply,
|
||||||
|
token: token))
|
||||||
|
yield return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task<TranscriptionResult> TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.TRANSCRIPTION_PROVIDER);
|
||||||
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
|
{
|
||||||
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER);
|
||||||
|
return await this.PerformStandardTextEmbeddingRequest(requestedSecret, embeddingModel, token: token, texts: texts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<ModelLoadResult> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return this.LoadModels(SecretStoreType.LLM_PROVIDER, apiKeyProvisional, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<ModelLoadResult> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(ModelLoadResult.FromModels([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<ModelLoadResult> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return this.LoadModelsOfRoute(SecretStoreType.EMBEDDING_PROVIDER, "models/embedding", apiKeyProvisional, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<ModelLoadResult> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return this.LoadModelsOfRoute(SecretStoreType.TRANSCRIPTION_PROVIDER, "models/transcription", apiKeyProvisional, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the managed policies and the full model catalog, and offers both as one list.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A managed policy, such as "claude-opus-5@eu", bundles several clouds which serve the same
|
||||||
|
/// model, and Requesty decides which of them answers a request. Requesty lists these policies on
|
||||||
|
/// a route of their own, and the catalog does not contain them. Both lists are read before
|
||||||
|
/// anything is reported, because what is reported replaces everything this instance said
|
||||||
|
/// before. When the managed route fails, the catalog alone is still offered.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="storeType">Where the API key is stored.</param>
|
||||||
|
/// <param name="apiKeyProvisional">An API key which is not stored yet.</param>
|
||||||
|
/// <param name="token">The cancellation token to use.</param>
|
||||||
|
/// <returns>The chat models.</returns>
|
||||||
|
private async Task<ModelLoadResult> LoadModels(SecretStoreType storeType, string? apiKeyProvisional, CancellationToken token)
|
||||||
|
{
|
||||||
|
IList<RequestyModel> managedModels = [];
|
||||||
|
await this.LoadModelsResponse<RequestyModelsResponse>(
|
||||||
|
storeType,
|
||||||
|
"models/managed",
|
||||||
|
modelResponse =>
|
||||||
|
{
|
||||||
|
managedModels = modelResponse.Data;
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
apiKeyProvisional,
|
||||||
|
requestConfigurator: ConfigureRequest,
|
||||||
|
token: token);
|
||||||
|
|
||||||
|
return await this.LoadModelsResponse<RequestyModelsResponse>(
|
||||||
|
storeType,
|
||||||
|
"models",
|
||||||
|
modelResponse => managedModels.Concat(modelResponse.Data)
|
||||||
|
.DistinctBy(n => n.Id)
|
||||||
|
.Select(n => new Model(n.Id, null))
|
||||||
|
.Where(model => model.IsChatModel(this.Provider)),
|
||||||
|
apiKeyProvisional,
|
||||||
|
requestConfigurator: ConfigureRequest,
|
||||||
|
listingFactory: modelResponse => managedModels.Concat(modelResponse.Data)
|
||||||
|
.DistinctBy(n => n.Id)
|
||||||
|
.Select(n => ModelListing.For(n.Id, n.ContextWindowTokens)),
|
||||||
|
token: token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the models Requesty offers for embedding or for transcription, each of which lives on
|
||||||
|
/// a route of its own.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Nothing is reported from here: such a route answers with the models of its purpose alone,
|
||||||
|
/// and what is reported replaces everything an instance said before. The windows of the chat
|
||||||
|
/// models would go missing the moment somebody opens the embedding or transcription settings.
|
||||||
|
///
|
||||||
|
/// Nothing is filtered either. The route is the statement: Requesty serves these models to
|
||||||
|
/// embed or to transcribe with, which is more than a name can say. Asking the registry on top
|
||||||
|
/// could only drop a model whose name we do not recognize.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="storeType">Where the API key is stored.</param>
|
||||||
|
/// <param name="route">The route which lists the models, relative to the base URL.</param>
|
||||||
|
/// <param name="apiKeyProvisional">An API key which is not stored yet.</param>
|
||||||
|
/// <param name="token">The cancellation token to use.</param>
|
||||||
|
/// <returns>The models of the route.</returns>
|
||||||
|
private Task<ModelLoadResult> LoadModelsOfRoute(SecretStoreType storeType, string route, string? apiKeyProvisional, CancellationToken token)
|
||||||
|
{
|
||||||
|
return this.LoadModelsResponse<RequestyModelsResponse>(
|
||||||
|
storeType,
|
||||||
|
route,
|
||||||
|
modelResponse => modelResponse.Data.Select(n => new Model(n.Id, null)),
|
||||||
|
apiKeyProvisional,
|
||||||
|
requestConfigurator: ConfigureRequest,
|
||||||
|
token: token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureRequest(HttpRequestMessage request, string secretKey)
|
||||||
|
{
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||||
|
ProjectAttribution.Apply(request.Headers);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.Requesty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A data model for a Requesty model from the API.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Id">The model's ID.</param>
|
||||||
|
/// <param name="ContextWindowTokens">How much the model reads and writes in one conversation, in tokens.</param>
|
||||||
|
public readonly record struct RequestyModel(string Id, [property: JsonPropertyName("context_window")] int? ContextWindowTokens);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace AIStudio.Provider.Requesty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A data model for the response from the Requesty models endpoints.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Data">The list of models.</param>
|
||||||
|
public readonly record struct RequestyModelsResponse(IList<RequestyModel> Data);
|
||||||
@@ -83,7 +83,7 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
|
|||||||
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, host, token);
|
return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, host, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Provider.Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override async Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Provider.Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER, isTrying: true);
|
var requestedSecret = await Program.RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER, isTrying: true);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public sealed class ProviderX() : BaseProvider(LLMProviders.X, new Uri("https://
|
|||||||
return Task.FromResult(TranscriptionResult.Failure());
|
return Task.FromResult(TranscriptionResult.Failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inhertidoc />
|
/// <inheritdoc />
|
||||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||||
{
|
{
|
||||||
throw this.CreateEmbeddingsNotSupportedException();
|
throw this.CreateEmbeddingsNotSupportedException();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
- Added a way for IT departments to allow mailboxes only on the mail servers of your organization. AI Studio then connects to no other server, and the AI no longer reads a mailbox somebody added elsewhere.
|
- Added a way for IT departments to allow mailboxes only on the mail servers of your organization. AI Studio then connects to no other server, and the AI no longer reads a mailbox somebody added elsewhere.
|
||||||
- Added a setting to share which features of AI Studio you use with the operators of self-hosted AI servers, usually your organization. When you switch it on, requests to self-hosted providers and LiteLLM name the feature that sent them, such as the chat, an assistant, or an agent, together with the version of AI Studio and your operating system. Cloud providers never receive these details, and AI Studio sends nothing to its developers. The setting is off by default, and you find it in the app settings. Thanks, Dominic Neuburg (`donework`), for this contribution.
|
- Added a setting to share which features of AI Studio you use with the operators of self-hosted AI servers, usually your organization. When you switch it on, requests to self-hosted providers and LiteLLM name the feature that sent them, such as the chat, an assistant, or an agent, together with the version of AI Studio and your operating system. Cloud providers never receive these details, and AI Studio sends nothing to its developers. The setting is off by default, and you find it in the app settings. Thanks, Dominic Neuburg (`donework`), for this contribution.
|
||||||
- Added a way for IT departments to let requests to the self-hosted AI servers of your organization name the feature of AI Studio that sent them, either for everybody or as a default that users may change. This shows which features are used. The Enterprise IT documentation explains what is sent and what to clarify before you switch it on, for example, with your works council.
|
- Added a way for IT departments to let requests to the self-hosted AI servers of your organization name the feature of AI Studio that sent them, either for everybody or as a default that users may change. This shows which features are used. The Enterprise IT documentation explains what is sent and what to clarify before you switch it on, for example, with your works council.
|
||||||
|
- Added Requesty as a new LLM provider for chats, embeddings, and transcriptions. Requesty is an AI gateway from the United Kingdom that gives you models from many providers with a single API key. Because where your data is processed depends on the model you choose, you set its trust level yourself. Thanks, Thibault Jaigu (`Thibaultjaigu`), for this first contribution.
|
||||||
- Improved the Read Web Page tool. When you have not allowed the AI to choose web addresses freely, the tool now refuses every address that does not appear in your chat or in what the tools returned, instead of only asking the AI not to read it.
|
- Improved the Read Web Page tool. When you have not allowed the AI to choose web addresses freely, the tool now refuses every address that does not appear in your chat or in what the tools returned, instead of only asking the AI not to read it.
|
||||||
- Improved the installation on macOS: AI Studio is now signed and checked by Apple, so it opens like any other app. You no longer need the Terminal.
|
- Improved the installation on macOS: AI Studio is now signed and checked by Apple, so it opens like any other app. You no longer need the Terminal.
|
||||||
- Improved the installation on Windows: AI Studio is now signed, so Windows shows who published it. For a while, Windows may still ask you to confirm the installation until it knows the app better.
|
- Improved the installation on Windows: AI Studio is now signed, so Windows shows who published it. For a while, Windows may still ask you to confirm the installation until it knows the app better.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ The icons serve the LLM providers as well as the mail providers of the mailbox d
|
|||||||
- `openai*.svg` uses the OpenAI mark path from [Simple Icons 15.15.0](https://github.com/simple-icons/simple-icons/blob/15.15.0/icons/openai.svg) and black/white variants following the [OpenAI Design Guidelines](https://openai.com/brand/).
|
- `openai*.svg` uses the OpenAI mark path from [Simple Icons 15.15.0](https://github.com/simple-icons/simple-icons/blob/15.15.0/icons/openai.svg) and black/white variants following the [OpenAI Design Guidelines](https://openai.com/brand/).
|
||||||
- `fireworks.svg` is adapted from the [Fireworks AI site icon](https://fireworks.ai/icon0.svg).
|
- `fireworks.svg` is adapted from the [Fireworks AI site icon](https://fireworks.ai/icon0.svg).
|
||||||
- `groq.svg` is adapted from the [Groq site icon](https://groq.com/favicon.svg).
|
- `groq.svg` is adapted from the [Groq site icon](https://groq.com/favicon.svg).
|
||||||
|
- `requesty.svg` is adapted from the [Requesty site icon](https://www.requesty.ai/apple-icon.png).
|
||||||
- `litellm.svg` is the bullet train emoji from [Twemoji 17.0.3](https://github.com/jdecked/twemoji/tree/v17.0.3), licensed under [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/). LiteLLM has no mark of its own and identifies itself with that emoji. Retrieved on 2026-08-30.
|
- `litellm.svg` is the bullet train emoji from [Twemoji 17.0.3](https://github.com/jdecked/twemoji/tree/v17.0.3), licensed under [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/). LiteLLM has no mark of its own and identifies itself with that emoji. Retrieved on 2026-08-30.
|
||||||
- `provider*.svg` and `self-hosted*.svg` are neutral project-owned fallback graphics.
|
- `provider*.svg` and `self-hosted*.svg` are neutral project-owned fallback graphics.
|
||||||
- `gwdg.svg`, `openrouter.svg`, `google.svg` and `helmholtz.svg` were created by taking the official logo from their respective websites as images and creating a svg from them.
|
- `gwdg.svg`, `openrouter.svg`, `google.svg` and `helmholtz.svg` were created by taking the official logo from their respective websites as images and creating a svg from them.
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g transform="rotate(-5 12 12)"><path d="M4.5 3h15A2.5 2.5 0 0122 5.5v10a2.5 2.5 0 01-2.5 2.5h-8.25L7.5 22v-4h-3A2.5 2.5 0 012 15.5v-10A2.5 2.5 0 014.5 3z" fill="#1A73F5"/><path d="M6.5 7.5l4 2.75-4 2.75" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8"/><path d="M12.5 14.5h4.5" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 487 B |
@@ -195,6 +195,12 @@ PERPLEXITY | sonar-deep-research | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIP
|
|||||||
PERPLEXITY | sonar-pro | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
PERPLEXITY | sonar-pro | CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
PERPLEXITY | sonar-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
PERPLEXITY | sonar-reasoning | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
PERPLEXITY | sonar-reasoning-pro | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
PERPLEXITY | sonar-reasoning-pro | ALWAYS_REASONING, CHAT_COMPLETION_API, MULTIPLE_IMAGE_INPUT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
|
REQUESTY | anthropic/claude-opus-5 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT | 1000000 | 600 per request | PROVIDER_API /v1/messages/count_tokens | (unknown)
|
||||||
|
REQUESTY | bedrock/claude-opus-5@eu-central-1 | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT | 1000000 | 600 per request | PROVIDER_API /v1/messages/count_tokens | (unknown)
|
||||||
|
REQUESTY | claude-opus-5@eu | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT | 1000000 | 600 per request | PROVIDER_API /v1/messages/count_tokens | (unknown)
|
||||||
|
REQUESTY | google/gemma-4-31b-it | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, OPTIONAL_REASONING, TEXT_INPUT, TEXT_OUTPUT | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
|
REQUESTY | novita/qwen/qwen3.8-max | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
|
REQUESTY | openai/gpt-6-sol:flex | CHAT_COMPLETION_API, FUNCTION_CALLING, MULTIPLE_IMAGE_INPUT, REASONING_BY_DEFAULT, TEXT_INPUT, TEXT_OUTPUT, WEB_SEARCH | CHAT | 1050000 | (unknown) | (unknown) | DEVELOPER
|
||||||
SELF_HOSTED | | (nothing) | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
SELF_HOSTED | | (nothing) | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
SELF_HOSTED | --- | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
SELF_HOSTED | --- | CHAT_COMPLETION_API, FUNCTION_CALLING, TEXT_INPUT, TEXT_OUTPUT | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
SELF_HOSTED | 01-ai/yi-large | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
SELF_HOSTED | 01-ai/yi-large | CHAT_COMPLETION_API, TEXT_INPUT, TEXT_OUTPUT | CHAT | (unknown) | (unknown) | (unknown) | (unknown)
|
||||||
|
|||||||
@@ -240,6 +240,12 @@ public static class ModelCorpus
|
|||||||
new(LITE_LLM, "azure/gpt-5.6", QUOTED_AS_A_NAME_SHAPE),
|
new(LITE_LLM, "azure/gpt-5.6", QUOTED_AS_A_NAME_SHAPE),
|
||||||
new(LITE_LLM, "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0", NAMED_BY_NO_RULE),
|
new(LITE_LLM, "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0", NAMED_BY_NO_RULE),
|
||||||
new(LITE_LLM, "the-fast-one", NAMED_BY_NO_RULE),
|
new(LITE_LLM, "the-fast-one", NAMED_BY_NO_RULE),
|
||||||
|
new(REQUESTY, "anthropic/claude-opus-5", QUOTED_AS_A_NAME_SHAPE),
|
||||||
|
new(REQUESTY, "google/gemma-4-31b-it", NAMED_BY_A_RULE),
|
||||||
|
new(REQUESTY, "bedrock/claude-opus-5@eu-central-1", QUOTED_AS_A_NAME_SHAPE),
|
||||||
|
new(REQUESTY, "claude-opus-5@eu", QUOTED_AS_A_NAME_SHAPE),
|
||||||
|
new(REQUESTY, "openai/gpt-6-sol:flex", QUOTED_AS_A_NAME_SHAPE),
|
||||||
|
new(REQUESTY, "novita/qwen/qwen3.8-max", QUOTED_AS_A_NAME_SHAPE),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public sealed class HostNamingTests
|
|||||||
Assert.That(HostNaming.VendorOfOrganization("Qwen"), Is.EqualTo(ModelVendor.ALIBABA));
|
Assert.That(HostNaming.VendorOfOrganization("Qwen"), Is.EqualTo(ModelVendor.ALIBABA));
|
||||||
Assert.That(HostNaming.VendorOfOrganization("deepseek-ai"), Is.EqualTo(ModelVendor.DEEP_SEEK));
|
Assert.That(HostNaming.VendorOfOrganization("deepseek-ai"), Is.EqualTo(ModelVendor.DEEP_SEEK));
|
||||||
Assert.That(HostNaming.VendorOfOrganization("HuggingFaceTB"), Is.EqualTo(ModelVendor.HUGGING_FACE));
|
Assert.That(HostNaming.VendorOfOrganization("HuggingFaceTB"), Is.EqualTo(ModelVendor.HUGGING_FACE));
|
||||||
|
Assert.That(HostNaming.VendorOfOrganization("zai"), Is.EqualTo(ModelVendor.Z_AI));
|
||||||
|
Assert.That(HostNaming.VendorOfOrganization("minimaxi"), Is.EqualTo(ModelVendor.MINIMAX));
|
||||||
Assert.That(HostNaming.VendorOfOrganization("somebody-else"), Is.EqualTo(ModelVendor.UNKNOWN));
|
Assert.That(HostNaming.VendorOfOrganization("somebody-else"), Is.EqualTo(ModelVendor.UNKNOWN));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -111,6 +113,31 @@ public sealed class HostNamingTests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TheRegionSuffixComesOffAndTheModelStaysWhatItWas()
|
||||||
|
{
|
||||||
|
var taken = HostNaming.TryStripRegionSuffix(new ModelId("bedrock/claude-opus-5@eu-central-1"), out var inner);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(taken, Is.True);
|
||||||
|
Assert.That(inner.Original, Is.EqualTo("bedrock/claude-opus-5"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("bedrock/claude-opus-5", TestName = "A name without an at sign has no region to take off")]
|
||||||
|
[TestCase("@eu", TestName = "A region with nothing in front of it is not a wrapping")]
|
||||||
|
public void WhatCarriesNoRegionIsLeftAlone(string modelId)
|
||||||
|
{
|
||||||
|
var taken = HostNaming.TryStripRegionSuffix(new ModelId(modelId), out var inner);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(taken, Is.False);
|
||||||
|
Assert.That(inner.Original, Is.EqualTo(modelId));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void AMenuPositionComesOff()
|
public void AMenuPositionComesOff()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -78,6 +78,55 @@ public sealed class ModelHostTests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ARequestyNameLosesItsRegionAndTheCloudRunningIt()
|
||||||
|
{
|
||||||
|
var unwrapped = Unwrap(LLMProviders.REQUESTY, "bedrock/claude-opus-5@eu-central-1", out var vendor);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(unwrapped.Original, Is.EqualTo("claude-opus-5"));
|
||||||
|
Assert.That(vendor, Is.Null, "Bedrock runs the model; it did not build it.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ARequestyManagedPolicyOnlyLosesItsRegion()
|
||||||
|
{
|
||||||
|
var unwrapped = Unwrap(LLMProviders.REQUESTY, "claude-opus-5@eu", out var vendor);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(unwrapped.Original, Is.EqualTo("claude-opus-5"));
|
||||||
|
Assert.That(vendor, Is.Null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TheRequestyServiceTierComesOffAndTheVendorStays()
|
||||||
|
{
|
||||||
|
var unwrapped = Unwrap(LLMProviders.REQUESTY, "openai/gpt-6-sol:flex", out var vendor);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(unwrapped.Original, Is.EqualTo("gpt-6-sol"));
|
||||||
|
Assert.That(vendor, Is.EqualTo(ModelVendor.OPEN_AI));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("novita/qwen/qwen3.8-max", "qwen3.8-max", ModelVendor.ALIBABA, TestName = "Behind the cloud, the organization from the hub names the vendor")]
|
||||||
|
[TestCase("azure/openai/text-embedding-3-large@francecentral", "text-embedding-3-large", ModelVendor.OPEN_AI, TestName = "Behind the cloud and before the region, the organization names the vendor")]
|
||||||
|
public void TheInnermostRequestyOrganizationNamesTheVendor(string modelId, string expectedModel, ModelVendor expectedVendor)
|
||||||
|
{
|
||||||
|
var unwrapped = Unwrap(LLMProviders.REQUESTY, modelId, out var vendor);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(unwrapped.Original, Is.EqualTo(expectedModel));
|
||||||
|
Assert.That(vendor, Is.EqualTo(expectedVendor));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TheFireworksAccountPathComesOffWholeWithoutAnybodyCountingItsSegments()
|
public void TheFireworksAccountPathComesOffWholeWithoutAnybodyCountingItsSegments()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Text.Json;
|
|||||||
|
|
||||||
using AIStudio.Provider.Groq;
|
using AIStudio.Provider.Groq;
|
||||||
using AIStudio.Provider.OpenRouter;
|
using AIStudio.Provider.OpenRouter;
|
||||||
|
using AIStudio.Provider.Requesty;
|
||||||
|
|
||||||
using MistralModelsResponse = AIStudio.Provider.Mistral.ModelsResponse;
|
using MistralModelsResponse = AIStudio.Provider.Mistral.ModelsResponse;
|
||||||
using SelfHostedModelsResponse = AIStudio.Provider.SelfHosted.ModelsResponse;
|
using SelfHostedModelsResponse = AIStudio.Provider.SelfHosted.ModelsResponse;
|
||||||
@@ -105,6 +106,19 @@ public sealed class ModelListMetadataTests
|
|||||||
Assert.That(response.Data[0].ContextWindowTokens, Is.EqualTo(131_072));
|
Assert.That(response.Data[0].ContextWindowTokens, Is.EqualTo(131_072));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RequestyStatesTheWindowAsTheContextWindow()
|
||||||
|
{
|
||||||
|
var response = JsonSerializer.Deserialize<RequestyModelsResponse>("""
|
||||||
|
{
|
||||||
|
"object": "list",
|
||||||
|
"data": [ { "id": "openai/gpt-4o-mini", "object": "model", "api": "chat", "context_window": 128000, "max_output_tokens": 16384 } ]
|
||||||
|
}
|
||||||
|
""", AS_THE_PROVIDERS_READ_IT);
|
||||||
|
|
||||||
|
Assert.That(response.Data[0].ContextWindowTokens, Is.EqualTo(128_000));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void MistralStatesTheWindowAsAMaximumLength()
|
public void MistralStatesTheWindowAsAMaximumLength()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ Both are documented for administrators in `documentation/Enterprise IT.md`. Mode
|
|||||||
|
|
||||||
## Live Metadata From The Model Lists
|
## Live Metadata From The Model Lists
|
||||||
|
|
||||||
Some providers state the context window in the model list they answer with anyway. AI Studio reads it where it is there: OpenRouter (`context_length`), Groq (`context_window`), Mistral (`max_context_length`), the Hugging Face router (per inference provider), and any OpenAI-compatible self-hosted engine that fills `max_model_len`, which vLLM does.
|
Some providers state the context window in the model list they answer with anyway. AI Studio reads it where it is there: OpenRouter (`context_length`), Groq (`context_window`), Requesty (`context_window`), Mistral (`max_context_length`), the Hugging Face router (per inference provider), and any OpenAI-compatible self-hosted engine that fills `max_model_len`, which vLLM does.
|
||||||
|
|
||||||
Three things to know when adding another one:
|
Three things to know when adding another one:
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
<li>
|
<li>
|
||||||
Independence: You are not tied to any single provider. Choose the providers that best
|
Independence: You are not tied to any single provider. Choose the providers that best
|
||||||
suit your needs, including OpenAI, Perplexity, Mistral, Anthropic, Google Gemini, xAI,
|
suit your needs, including OpenAI, Perplexity, Mistral, Anthropic, Google Gemini, xAI,
|
||||||
DeepSeek, Alibaba Cloud, OpenRouter, Hetzner, IONOS, LiteLLM, Hugging Face, Groq,
|
DeepSeek, Alibaba Cloud, OpenRouter, Hetzner, IONOS, LiteLLM, Requesty, Hugging Face,
|
||||||
Fireworks, Helmholtz, GWDG, and self-hosted models.
|
Groq, Fireworks, Helmholtz, GWDG, and self-hosted models.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Assistants: Use ready-made assistants for common business and other tasks without writing prompts yourself.
|
Assistants: Use ready-made assistants for common business and other tasks without writing prompts yourself.
|
||||||
|
|||||||
Reference in new issue
Block a user