diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index b49906f9..b5808297 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -6547,9 +6547,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T1356621346"] = "Cr -- Failed to validate the selected tokenizer. Please try again. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T1384494471"] = "Failed to validate the selected tokenizer. Please try again." --- Please enter an embedding model name. -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T1661085403"] = "Please enter an embedding model name." - -- Hostname UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T1727440780"] = "Hostname" @@ -6604,9 +6601,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2810182573"] = "No -- Instance Name UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2842060373"] = "Instance Name" --- Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually. -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T290547799"] = "Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually." - -- Token limit UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2961294165"] = "Token limit" @@ -6616,6 +6610,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T3316544737"] = "Pl -- Show Expert Settings UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T3361153305"] = "Show Expert Settings" +-- None of the models on this server is one we recognize as an embedding model, so all of them are listed. Please pick the one you run for embeddings. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T3363221762"] = "None of the models on this server is one we recognize as an embedding model, so all of them are listed. Please pick the one you run for embeddings." + +-- This server does not offer the selected model right now. It stays selected, so the documents you already prepared keep working. Choosing another model means every document of the data sources behind this provider is prepared again. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T3571276758"] = "This server does not offer the selected model right now. It stays selected, so the documents you already prepared keep working. Choosing another model means every document of the data sources behind this provider is prepared again." + -- How many chunks are sent to the embedding provider at once. The default is 1. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T3780233303"] = "How many chunks are sent to the embedding provider at once. The default is 1." @@ -8716,9 +8716,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1324664716"] = -- Create account UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1356621346"] = "Create account" --- Currently, we cannot query the transcription models for the selected provider and/or host. Therefore, please enter the model name manually. -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1381635232"] = "Currently, we cannot query the transcription models for the selected provider and/or host. Therefore, please enter the model name manually." - -- Hostname UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1727440780"] = "Hostname" @@ -8752,9 +8749,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2842060373"] = -- Hugging Face transcribes audio through a few of its inference providers only, which is why this list is shorter than the one for chatting. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T3397943774"] = "Hugging Face transcribes audio through a few of its inference providers only, which is why this list is shorter than the one for chatting." --- Please enter a transcription model name. -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T3703662664"] = "Please enter a transcription model name." - -- This host uses the model configured at the provider level. No model selection is available. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T3783329915"] = "This host uses the model configured at the provider level. No model selection is available." diff --git a/app/MindWork AI Studio/Models/Alibaba/ModelStudioEmbeddingFamily.cs b/app/MindWork AI Studio/Models/Alibaba/ModelStudioEmbeddingFamily.cs index 392c2026..fd0c4147 100644 --- a/app/MindWork AI Studio/Models/Alibaba/ModelStudioEmbeddingFamily.cs +++ b/app/MindWork AI Studio/Models/Alibaba/ModelStudioEmbeddingFamily.cs @@ -9,9 +9,10 @@ namespace AIStudio.Models.Alibaba; /// /// /// The previous rules answered for these with the Model Studio default and told them they call -/// functions. The prefix is Alibaba's own: the app filters the catalog by "text-embedding-" to find -/// them, which is also why the rule may be written that broadly -- bound to this provider, it can -/// only ever meet the models Alibaba names that way. +/// functions. The prefix is Alibaba's own, and the rule may be written that broadly because it is +/// bound to this provider: it can only ever meet the models Alibaba names that way. The provider +/// carried the same prefix as a filter of its own until it started asking here, so this is now the +/// only place which says what those names mean. /// public sealed class ModelStudioEmbeddingFamily : ModelFamily { @@ -19,7 +20,7 @@ public sealed class ModelStudioEmbeddingFamily : ModelFamily public override ModelVendor Vendor => ModelVendor.ALIBABA; /// - public override ModelSource Source => new("https://www.alibabacloud.com/help/en/model-studio/embedding", new DateOnly(2026, 9, 11), "Provider/AlibabaCloud/ProviderAlibabaCloud.cs adds these in GetEmbeddingModels and filters the catalog by the prefix \"text-embedding-\"."); + public override ModelSource Source => new("https://www.alibabacloud.com/help/en/model-studio/embedding", new DateOnly(2026, 9, 11), "Provider/AlibabaCloud/ProviderAlibabaCloud.cs used to add these in GetEmbeddingModels and to filter the catalog by the prefix \"text-embedding-\"; it asks this rule instead."); /// protected override void Declare(ModelFamilyBuilder builder) => diff --git a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs index 8a735ebe..af95d336 100644 --- a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs +++ b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs @@ -76,40 +76,8 @@ public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_C /// public override async Task GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default) { - var additionalModels = new[] - { - new Model("qwq-plus", "QwQ plus"), // reasoning model - new Model("qwen-max-latest", "Qwen-Max (Latest)"), - new Model("qwen-plus-latest", "Qwen-Plus (Latest)"), - new Model("qwen-turbo-latest", "Qwen-Turbo (Latest)"), - new Model("qvq-max", "QVQ Max"), // visual reasoning model - new Model("qvq-max-latest", "QVQ Max (Latest)"), // visual reasoning model - new Model("qwen-vl-max", "Qwen-VL Max"), // text generation model that can understand and process images - new Model("qwen-vl-plus", "Qwen-VL Plus"), // text generation model that can understand and process images - new Model("qwen-mt-plus", "Qwen-MT Plus"), // machine translation - new Model("qwen-mt-turbo", "Qwen-MT Turbo"), // machine translation - - //Open source - new Model("qwen2.5-14b-instruct-1m", "Qwen2.5 14b 1m context"), - new Model("qwen2.5-7b-instruct-1m", "Qwen2.5 7b 1m context"), - new Model("qwen2.5-72b-instruct", "Qwen2.5 72b"), - new Model("qwen2.5-32b-instruct", "Qwen2.5 32b"), - new Model("qwen2.5-14b-instruct", "Qwen2.5 14b"), - new Model("qwen2.5-7b-instruct", "Qwen2.5 7b"), - new Model("qwen2.5-omni-7b", "Qwen2.5-Omni 7b"), // omni-modal understanding and generation model - new Model("qwen2.5-vl-72b-instruct", "Qwen2.5-VL 72b"), - new Model("qwen2.5-vl-32b-instruct", "Qwen2.5-VL 32b"), - new Model("qwen2.5-vl-7b-instruct", "Qwen2.5-VL 7b"), - new Model("qwen2.5-vl-3b-instruct", "Qwen2.5-VL 3b"), - }; - - var result = await this.LoadModels(["q"], SecretStoreType.LLM_PROVIDER, apiKeyProvisional, token); - return result with - { - // The API is the authority: when it reports a model we also keep as a fallback above, - // its entry comes first and the fallback is dropped. - Models = [..result.Models.Concat(additionalModels).DistinctBy(x => x.Id).OrderBy(x => x.Id)] - }; + var result = await this.LoadModels(SecretStoreType.LLM_PROVIDER, apiKeyProvisional, token); + return result with { Models = [..result.Models.Where(model => model.IsChatModel(this.Provider)).OrderBy(x => x.Id)] }; } /// @@ -121,19 +89,8 @@ public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_C /// public override async Task GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default) { - - var additionalModels = new[] - { - new Model("text-embedding-v3", "text-embedding-v3"), - }; - - var result = await this.LoadModels(["text-embedding-"], SecretStoreType.EMBEDDING_PROVIDER, apiKeyProvisional, token); - return result with - { - // The API is the authority: when it reports a model we also keep as a fallback above, - // its entry comes first and the fallback is dropped. - Models = [..result.Models.Concat(additionalModels).DistinctBy(x => x.Id).OrderBy(x => x.Id)] - }; + var result = await this.LoadModels(SecretStoreType.EMBEDDING_PROVIDER, apiKeyProvisional, token); + return result with { Models = [..result.Models.Where(model => model.IsEmbeddingModel(this.Provider)).OrderBy(x => x.Id)] }; } #region Overrides of BaseProvider @@ -148,12 +105,23 @@ public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_C #endregion - private Task LoadModels(string[] prefixes, SecretStoreType storeType, string? apiKeyProvisional, CancellationToken token) + /// + /// Reads Model Studio's catalog, whole. + /// + /// + /// It used to be read through a prefix per list -- a single "q" for the models to talk to, and + /// "text-embedding-" for the ones which answer in vectors. Neither survived what Model Studio + /// became: the letter also brings qwen-image, qwen-tts, qwen3-asr and qwen-vl-ocr into the chat + /// list, while it locks out DeepSeek, Kimi, GLM and MiniMax, which Alibaba serves through this + /// very endpoint. A name has never been a statement about what a model is for; the callers ask + /// the registry instead. + /// + private Task LoadModels(SecretStoreType storeType, string? apiKeyProvisional, CancellationToken token) { return this.LoadModelsResponse( storeType, "models", - modelResponse => modelResponse.Data.Where(model => prefixes.Any(prefix => model.Id.StartsWith(prefix, StringComparison.InvariantCulture))), + modelResponse => modelResponse.Data, apiKeyProvisional, token: token); } } \ No newline at end of file diff --git a/app/Tests/Models/Corpus/ModelKindCorpus.cs b/app/Tests/Models/Corpus/ModelKindCorpus.cs index 74b32140..309ad007 100644 --- a/app/Tests/Models/Corpus/ModelKindCorpus.cs +++ b/app/Tests/Models/Corpus/ModelKindCorpus.cs @@ -39,6 +39,11 @@ public static class ModelKindCorpus // model is for is said by the word which says it, not by the family it was built from. new(MISTRAL, "codestral-embed", EMBEDDING), + // Alibaba names its own by the prefix the provider used to filter the catalog by. The rule + // says it now, so the prefix is free to go: + new(ALIBABA_CLOUD, "text-embedding-v3", EMBEDDING), + new(ALIBABA_CLOUD, "text-embedding-v4", EMBEDDING), + // // What a local Ollama installation serves, taken off its models endpoint rather than // written from memory. The last two are the ones worth having: neither name carries the @@ -85,6 +90,8 @@ public static class ModelKindCorpus new(GOOGLE, "gemini-3-pro-image", IMAGE_GENERATION), new(GOOGLE, "imagen-4.0-generate-001", IMAGE_GENERATION, AnsweredTodayAs: CHAT, Reason: "The markers never knew the name; the family ported in the Google step states it. Nobody noticed because the Google provider shows only names beginning with gemini."), + + new(ALIBABA_CLOUD, "qwen-image-edit", IMAGE_GENERATION), ]; /// @@ -196,6 +203,7 @@ public static class ModelKindCorpus private static readonly ModelKindExample[] OCR_ENTRIES = [ new(MISTRAL, "mistral-ocr-latest", OCR), + new(ALIBABA_CLOUD, "qwen-vl-ocr", OCR), ]; /// @@ -247,6 +255,17 @@ public static class ModelKindCorpus // is one letter longer than it. A name part is what keeps the two apart: new(SELF_HOSTED, "mistral-7b-instruct", CHAT), + // + // What somebody actually comes to Alibaba Cloud for, held here because the provider is + // about to stop keeping its chat list by the letter every one of these begins with. The + // last one translates rather than converses, and it does so through the chat completion + // API like the others, so this is where it belongs. + // + new(ALIBABA_CLOUD, "qwen-max-latest", CHAT), + new(ALIBABA_CLOUD, "qwq-plus", CHAT), + new(ALIBABA_CLOUD, "qvq-max", CHAT), + new(ALIBABA_CLOUD, "qwen-mt-turbo", CHAT), + // // Three which were questioned while testing and stay all the same. Grok Build is the coding // model behind the xAI CLI and answers like any other Grok. The Groq compound systems are