Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/MeshWeaver.AI/BuiltInLanguageModelProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ public IEnumerable<MeshNode> GetStaticNodes()
NodeType = ModelProviderNodeType.NodeType,
Name = source.ProviderName,
Category = "Providers",
Icon = "Key",
// Brand logo when the provider name resolves to a known maker; the
// generic key SVG otherwise. Fall back to the self-contained SVG URL
// (not the legacy "Key" Fluent name, which MeshNodeImageHelper filters
// out) so node.Icon is always directly renderable. See ModelProviderIcons.
Icon = ModelProviderIcons.ForProvider(source.ProviderName) ?? "/static/NodeTypeIcons/key.svg",
// create-if-absent: importer seeds it once, then admin owns it.
SyncBehavior = SyncBehavior.ExcludeThisAndChildren,
Content = providerConfig
Expand Down Expand Up @@ -194,7 +198,11 @@ public IEnumerable<MeshNode> GetStaticNodes()
NodeType = LanguageModelNodeType.NodeType,
Name = modelId,
Category = "Models",
Icon = "Sparkle",
// Brand logo inferred from the model id (then provider) so a model reads
// as its maker; the generic sparkle SVG when unknown. Fall back to the
// self-contained SVG URL (not the legacy "Sparkle" Fluent name, which
// MeshNodeImageHelper filters out). See ModelProviderIcons.
Icon = ModelProviderIcons.ForModel(source.ProviderName, modelId) ?? "/static/NodeTypeIcons/sparkle.svg",
Content = def
});
}
Expand Down
92 changes: 92 additions & 0 deletions src/MeshWeaver.AI/ModelProviderIcons.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Collections.Immutable;

namespace MeshWeaver.AI;

/// <summary>
/// Resolves a brand icon for an AI model or model provider so the picker shows a
/// recognizable logo instead of the generic sparkle. Icons are the self-contained
/// coloured-tile SVGs shipped under <c>src/MeshWeaver.Graph/Icons</c> (served at
/// <c>/static/NodeTypeIcons/*.svg</c>).
///
/// <para>The brand is inferred by substring match against the MODEL ID first, then
/// the provider name — so a gateway provider (AzureFoundry, OpenRouter) that serves
/// many brands still resolves each model to its true maker (a <c>claude-*</c> served
/// through Azure gets the Anthropic mark). An unknown model/provider returns
/// <c>null</c> so the caller can fall back to the neutral sparkle.</para>
/// </summary>
public static class ModelProviderIcons
{
private const string Base = "/static/NodeTypeIcons/";

/// <summary>
/// Brand tokens, checked in order via <see cref="string.Contains(string)"/> against a
/// lower-cased id/name. Order matters only where several tokens could match the same
/// string: the first match wins, so brand-specific tokens (<c>mixtral</c>, <c>codestral</c>,
/// <c>llama</c>) precede broader ones (<c>mistral</c>, <c>meta</c>). Each icon file exists
/// under <c>MeshWeaver.Graph/Icons</c>.
/// </summary>
private static readonly ImmutableArray<(string Token, string Icon)> Brands =
[
("claude", "anthropic"),
("anthropic", "anthropic"),
("gemini", "google"),
("gemma", "google"),
("deepseek", "deepseek"),
("mixtral", "mistral"),
("codestral", "mistral"),
("ministral", "mistral"),
("magistral", "mistral"),
("devstral", "mistral"),
("mistral", "mistral"),
("llama", "meta"),
("grok", "xai"),
("qwen", "qwen"),
("qwq", "qwen"),
("copilot", "githubcopilot"),
("openrouter", "openrouter"),
("perplexity", "perplexity"),
("sonar", "perplexity"),
("ollama", "ollama"),
("gpt", "openai"),
("chatgpt", "openai"),
("davinci", "openai"),
("dall-e", "openai"),
("openai", "openai"),
("meta", "meta"),
("xai", "xai"),
("azure", "azure"),
("microsoft", "azure"),
];

/// <summary>
/// Resolves the icon path for a model, preferring the brand implied by
/// <paramref name="modelId"/> and falling back to <paramref name="provider"/>.
/// Returns <c>null</c> when no brand matches.
/// </summary>
public static string? ForModel(string? provider, string? modelId)
{
var icon = Match(modelId) ?? Match(provider);
return icon is null ? null : Base + icon + ".svg";
}

/// <summary>
/// Resolves the icon path for a provider by its name. Returns <c>null</c> when no
/// brand matches.
/// </summary>
public static string? ForProvider(string? provider)
{
var icon = Match(provider);
return icon is null ? null : Base + icon + ".svg";
}

private static string? Match(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
var v = value.ToLowerInvariant();
foreach (var (token, icon) in Brands)
if (v.Contains(token))
return icon;
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
Name: AI models now show their provider's logo
Category: What's New
Description: The model picker displays each model's brand mark instead of a generic sparkle.
Icon: Sparkle
---

# AI models now show their provider's logo

Picking a model is easier to scan: every model now carries its maker's logo — Anthropic, OpenAI, Google, Mistral, Meta, DeepSeek, xAI, Qwen, Perplexity, GitHub Copilot and more — in place of the identical sparkle every model used to show.

The logo is inferred from the model itself, so a Claude model served through Azure or an OpenRouter gateway still reads as Anthropic. Models from a provider we don't yet recognise keep the neutral sparkle.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/anthropic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/azure.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/deepseek.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/githubcopilot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/google.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/meta.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/mistral.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/ollama.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MeshWeaver.Graph/Icons/openai.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading