// AI provider logo SVGs — regex-based matching for self-hosted model names // Uses official logos from Simple Icons where available, custom minimal SVGs otherwise // All SVGs use viewBox="0 0 24 24" fill="currentColor" const _PROVIDERS = [ // Ollama [/ollama|:11434/i, ''], // OpenAI — GPT, o1, o3, dall-e, chatgpt [/openai|gpt-|^o[13]-|chatgpt|dall-e/i, ''], // OpenCode (Zen / Go) — official brand mark [/opencode/i, ''], // GitHub / Copilot [/github|copilot/i, ''], // OpenRouter [/openrouter|open router/i, ''], // Ollama / Ollama Cloud [/ollama/i, ''], // Anthropic — Claude (official Simple Icons) [/anthropic|claude/i, ''], // Google Gemini (official Simple Icons) [/google|gemini|gemma/i, ''], // Meta — Llama models (official Simple Icons). Exclude the llama.cpp / llama-cpp // / llamacpp inference engine — that's an independent project (ggml), not Meta. [/meta|llama(?![.\-_ ]?cpp)/i, ''], // Mistral AI (official Simple Icons). Match Mixtral and Ministral too. [/mi[sx]tral|ministral/i, ''], // Qwen (Tongyi Qianwen) — official geometric hexagonal logo [/qwen|alibaba/i, ''], // DeepSeek (official whale logo from LobeHub icons) [/deepseek/i, ''], // xAI — Grok (stylized X) [/x-ai|xai|grok/i, ''], // Cohere — Command (stylized C) [/cohere|command-r/i, ''], // Perplexity (official Simple Icons) [/perplexity|sonar/i, ''], // Nous Research / Hermes [/nous|hermes/i, ''], // Microsoft / Phi (four squares) [/microsoft|phi-/i, ''], // Zhipu AI — GLM, ChatGLM (official Z logo) [/zhipu|glm|chatglm/i, ''], // MiniMax [/minimax/i, ''], // Kimi / Moonshot AI (crescent moon) [/kimi|moonshot/i, ''], // NVIDIA / Nemotron (official Simple Icons) [/nvidia|nemotron/i, ''], ]; // Returns an SVG string for the given model ID, or null if no match export function providerLogo(modelId) { if (!modelId) return null; for (const [re, svg] of _PROVIDERS) { if (re.test(modelId)) return svg; } return null; } // Host suffix → friendly provider label. The model-info card shows this so the // SAME model name served by DIFFERENT routes is distinguishable (e.g. // `claude-haiku` via OpenRouter vs GitHub Copilot vs Anthropic direct); the logo // only reflects the model vendor, not the actual endpoint. Patterns are anchored // to the end of the hostname (^|.)domain$ so a host like `max.airlines.com` // doesn't match `x.ai`. const _ENDPOINT_LABELS = [ [/(^|\.)githubcopilot\.com$/i, "GitHub Copilot"], [/(^|\.)chatgpt\.com$/i, "ChatGPT Subscription"], [/(^|\.)openrouter\.ai$/i, "OpenRouter"], [/(^|\.)anthropic\.com$/i, "Anthropic"], [/(^|\.)openai\.com$/i, "OpenAI"], [/(^|\.)(generativelanguage|aiplatform)\.googleapis\.com$/i, "Google"], [/(^|\.)bedrock[\w.-]*\.amazonaws\.com$/i, "AWS Bedrock"], [/(^|\.)deepseek\.com$/i, "DeepSeek"], [/(^|\.)mistral\.ai$/i, "Mistral"], [/(^|\.)groq\.com$/i, "Groq"], [/(^|\.)together\.(ai|xyz)$/i, "Together"], [/(^|\.)fireworks\.ai$/i, "Fireworks"], [/(^|\.)perplexity\.ai$/i, "Perplexity"], [/(^|\.)x\.ai$/i, "xAI"], ]; /** * Friendly label for the endpoint that served a model, from its URL. * Returns "Local" for loopback/LAN hosts, a known provider name when matched, * else the bare host. Null when no URL is available. */ export function providerLabel(endpointUrl) { if (!endpointUrl || typeof endpointUrl !== "string") return null; let host; try { host = new URL(endpointUrl).hostname; } catch (_) { // Not a full URL (e.g. bare host[:port]) — strip scheme/path/port best-effort. host = endpointUrl.replace(/^[a-z]+:\/\//i, "").split("/")[0].split(":")[0]; } if (!host) return null; if (/^(localhost|127\.|0\.0\.0\.0|::1|192\.168\.|10\.|172\.(1[6-9]|2\d|3[01])\.)/i.test(host)) { return "Local"; } for (const [re, label] of _ENDPOINT_LABELS) { if (re.test(host)) return label; } // Unknown host → drop a leading "api." for a cleaner readout. return host.replace(/^api\./i, ""); } // Map endpoint URL → logo SVG using the same model-id regex catalog. // Tests host + port + path so loopback servers (e.g. Ollama on // localhost:11434) still match by port. Falls back to null when nothing // recognises the URL, so callers can render a neutral placeholder. export function providerLogoFromUrl(url) { if (!url) return null; let host = '', port = '', path = ''; try { const u = new URL(url); host = u.hostname; port = u.port; path = u.pathname || ''; } catch (_) { const raw = String(url).replace(/^[a-z]+:\/\//i, ''); const slashIdx = raw.indexOf('/'); const hostport = slashIdx >= 0 ? raw.slice(0, slashIdx) : raw; path = slashIdx >= 0 ? raw.slice(slashIdx) : ''; const colon = hostport.lastIndexOf(':'); host = colon >= 0 ? hostport.slice(0, colon) : hostport; port = colon >= 0 ? hostport.slice(colon + 1) : ''; } // Build candidate strings to test against the provider catalog. const candidates = [host, port ? `${host}:${port}` : '', port ? `:${port}` : '', path].filter(Boolean); for (const [re, svg] of _PROVIDERS) { if (candidates.some(c => re.test(c))) return svg; } return null; } export default { providerLogo, providerLabel, providerLogoFromUrl };