noosphere 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,6 +63,56 @@ const audio = await ai.speak({
63
63
 
64
64
  Noosphere **automatically discovers the latest models from EVERY provider's API at runtime** — across **all 4 modalities** (LLM, image, video, TTS). When Google releases a new Gemini model, when OpenAI drops GPT-5, when FAL adds a new video model, when a new image model trends on HuggingFace — **you get them immediately**, without updating Noosphere or any dependency.
65
65
 
66
+ ### Provider Logos — SVG & PNG for Every Model
67
+
68
+ Every model returned by the auto-fetch includes a `logo` field with the provider's official logo in SVG and PNG formats. For aggregator providers (OpenRouter, HuggingFace), logos are resolved to the **real upstream provider** — so an `x-ai/grok-4` model gets the xAI logo, not OpenRouter's.
69
+
70
+ ```typescript
71
+ const models = await ai.getModels('llm');
72
+
73
+ for (const model of models) {
74
+ console.log(model.id, model.logo);
75
+ // "gpt-5" { svg: "https://cdn.simpleicons.org/openai", png: "https://cdn.brandfetch.io/.../icon.png" }
76
+ // "claude-opus-4-6" { svg: "https://cdn.simpleicons.org/anthropic", png: "https://cdn.brandfetch.io/.../icon.png" }
77
+ // "gemini-2.5-pro" { svg: "https://cdn.simpleicons.org/google", png: "https://cdn.brandfetch.io/.../icon.png" }
78
+ }
79
+
80
+ // Providers also have logos:
81
+ const providers = await ai.getProviders();
82
+ providers.forEach(p => console.log(p.id, p.logo));
83
+
84
+ // Use in your UI:
85
+ // <img src={model.logo.svg} alt={model.provider} />
86
+ ```
87
+
88
+ **Covered providers:** OpenAI, Anthropic, Google, Groq, Mistral, xAI, OpenRouter, Cerebras, Meta, DeepSeek, Microsoft, NVIDIA, Qwen, Cohere, Perplexity, Amazon, FAL, HuggingFace, ComfyUI, Piper, Kokoro, Ollama, SambaNova, Together, Fireworks, Replicate, Nebius, Novita.
89
+
90
+ You can also import the logo registry directly:
91
+
92
+ ```typescript
93
+ import { getProviderLogo, PROVIDER_LOGOS } from 'noosphere';
94
+
95
+ const logo = getProviderLogo('anthropic');
96
+ // { svg: "https://cdn.simpleicons.org/anthropic", png: "https://cdn.brandfetch.io/.../icon.png" }
97
+
98
+ // Or access the full map:
99
+ console.log(Object.keys(PROVIDER_LOGOS));
100
+ // ['openai', 'anthropic', 'google', 'groq', 'mistral', 'xai', 'openrouter', ...]
101
+ ```
102
+
103
+ For HuggingFace models with multiple inference providers, per-provider logos are available in `capabilities.inferenceProviderLogos`:
104
+
105
+ ```typescript
106
+ const hfModels = await ai.getModels('llm');
107
+ const qwen = hfModels.find(m => m.id === 'Qwen/Qwen2.5-72B-Instruct');
108
+
109
+ console.log(qwen.capabilities.inferenceProviderLogos);
110
+ // {
111
+ // "together": { svg: "https://cdn.simpleicons.org/togetherai", png: "..." },
112
+ // "fireworks-ai": { png: "https://cdn.brandfetch.io/.../icon.png" },
113
+ // }
114
+ ```
115
+
66
116
  ### The Problem It Solves
67
117
 
68
118
  Traditional AI libraries rely on **static model catalogs** hardcoded at build time. The `@mariozechner/pi-ai` dependency ships with ~246 LLM models in a pre-generated `models.generated.js` file. HuggingFace providers typically hardcode 3-5 default models. When a provider releases a new model, you'd have to wait for the library maintainer to update, publish, and then you'd `npm update`. This lag can be days or weeks.
package/dist/index.cjs CHANGED
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Noosphere: () => Noosphere,
24
- NoosphereError: () => NoosphereError
24
+ NoosphereError: () => NoosphereError,
25
+ PROVIDER_LOGOS: () => PROVIDER_LOGOS,
26
+ getProviderLogo: () => getProviderLogo
25
27
  });
26
28
  module.exports = __toCommonJS(index_exports);
27
29
 
@@ -53,6 +55,16 @@ var NoosphereError = class extends Error {
53
55
  };
54
56
 
55
57
  // src/config.ts
58
+ var _envLoaded = false;
59
+ function loadEnv() {
60
+ if (_envLoaded) return;
61
+ _envLoaded = true;
62
+ try {
63
+ const dotenvx = require("@dotenvx/dotenvx");
64
+ dotenvx.config({ quiet: true });
65
+ } catch {
66
+ }
67
+ }
56
68
  var ENV_KEY_MAP = {
57
69
  openai: "OPENAI_API_KEY",
58
70
  anthropic: "ANTHROPIC_API_KEY",
@@ -76,6 +88,7 @@ var DEFAULT_RETRYABLE = [
76
88
  "TIMEOUT"
77
89
  ];
78
90
  function resolveConfig(input) {
91
+ loadEnv();
79
92
  const keys = {};
80
93
  for (const [name, envVar] of Object.entries(ENV_KEY_MAP)) {
81
94
  keys[name] = input.keys?.[name] ?? process.env[envVar];
@@ -117,6 +130,132 @@ function resolveConfig(input) {
117
130
  };
118
131
  }
119
132
 
133
+ // src/logos.ts
134
+ var PROVIDER_LOGOS = {
135
+ // --- Cloud LLM Providers ---
136
+ openai: {
137
+ svg: "https://cdn.simpleicons.org/openai",
138
+ png: "https://cdn.brandfetch.io/idR3duQxYl/w/512/h/512/theme/dark/icon.png"
139
+ },
140
+ anthropic: {
141
+ svg: "https://cdn.simpleicons.org/anthropic",
142
+ png: "https://cdn.brandfetch.io/id2S-kXbuM/w/512/h/512/theme/dark/icon.png"
143
+ },
144
+ google: {
145
+ svg: "https://cdn.simpleicons.org/google",
146
+ png: "https://cdn.brandfetch.io/id6O2oGzv-/w/512/h/512/theme/dark/icon.png"
147
+ },
148
+ groq: {
149
+ svg: "https://cdn.simpleicons.org/groq",
150
+ png: "https://cdn.brandfetch.io/idTEBPz5KO/w/512/h/512/theme/dark/icon.png"
151
+ },
152
+ mistral: {
153
+ svg: "https://cdn.simpleicons.org/mistral",
154
+ png: "https://cdn.brandfetch.io/idnBOFq5eF/w/512/h/512/theme/dark/icon.png"
155
+ },
156
+ xai: {
157
+ svg: "https://cdn.simpleicons.org/x",
158
+ png: "https://cdn.brandfetch.io/idS5WhqBbM/w/512/h/512/theme/dark/icon.png"
159
+ },
160
+ openrouter: {
161
+ svg: "https://openrouter.ai/favicon.svg",
162
+ png: "https://openrouter.ai/favicon.png"
163
+ },
164
+ cerebras: {
165
+ svg: "https://cdn.simpleicons.org/cerebras",
166
+ png: "https://cdn.brandfetch.io/idGa4PRFP0/w/512/h/512/theme/dark/icon.png"
167
+ },
168
+ // --- Media Providers ---
169
+ fal: {
170
+ svg: "https://fal.ai/favicon.svg",
171
+ png: "https://fal.ai/favicon.png"
172
+ },
173
+ huggingface: {
174
+ svg: "https://cdn.simpleicons.org/huggingface",
175
+ png: "https://cdn.brandfetch.io/idnrPPHe87/w/512/h/512/theme/dark/icon.png"
176
+ },
177
+ // --- Local Providers ---
178
+ comfyui: {
179
+ svg: "https://raw.githubusercontent.com/comfyanonymous/ComfyUI/master/web/assets/icon.svg",
180
+ png: "https://raw.githubusercontent.com/comfyanonymous/ComfyUI/master/web/assets/icon.png"
181
+ },
182
+ piper: {
183
+ png: "https://raw.githubusercontent.com/rhasspy/piper/master/logo.png"
184
+ },
185
+ kokoro: {
186
+ png: "https://raw.githubusercontent.com/hexgrad/kokoro/main/assets/icon.png"
187
+ },
188
+ ollama: {
189
+ svg: "https://cdn.simpleicons.org/ollama",
190
+ png: "https://cdn.brandfetch.io/idtesMoSFj/w/512/h/512/theme/dark/icon.png"
191
+ },
192
+ // --- Model Org Providers (from OpenRouter model prefixes) ---
193
+ meta: {
194
+ svg: "https://cdn.simpleicons.org/meta",
195
+ png: "https://cdn.brandfetch.io/idmKk_rq7Y/w/512/h/512/theme/dark/icon.png"
196
+ },
197
+ deepseek: {
198
+ png: "https://cdn.brandfetch.io/id1BWKUVWI/w/512/h/512/theme/dark/icon.png"
199
+ },
200
+ microsoft: {
201
+ svg: "https://cdn.simpleicons.org/microsoft",
202
+ png: "https://cdn.brandfetch.io/idchmboHEZ/w/512/h/512/theme/dark/icon.png"
203
+ },
204
+ nvidia: {
205
+ svg: "https://cdn.simpleicons.org/nvidia",
206
+ png: "https://cdn.brandfetch.io/id1JcGHuZN/w/512/h/512/theme/dark/icon.png"
207
+ },
208
+ qwen: {
209
+ png: "https://img.alicdn.com/imgextra/i1/O1CN01BUp2gU1sRZigvazUo_!!6000000005764-2-tps-228-228.png"
210
+ },
211
+ cohere: {
212
+ svg: "https://cdn.simpleicons.org/cohere",
213
+ png: "https://cdn.brandfetch.io/idiDnz1fvB/w/512/h/512/theme/dark/icon.png"
214
+ },
215
+ perplexity: {
216
+ svg: "https://cdn.simpleicons.org/perplexity",
217
+ png: "https://cdn.brandfetch.io/idwWX3Neii/w/512/h/512/theme/dark/icon.png"
218
+ },
219
+ amazon: {
220
+ svg: "https://cdn.simpleicons.org/amazonaws",
221
+ png: "https://cdn.brandfetch.io/idawORoPJZ/w/512/h/512/theme/dark/icon.png"
222
+ },
223
+ // --- HuggingFace Inference Providers ---
224
+ "hf-inference": {
225
+ svg: "https://cdn.simpleicons.org/huggingface",
226
+ png: "https://cdn.brandfetch.io/idnrPPHe87/w/512/h/512/theme/dark/icon.png"
227
+ },
228
+ "sambanova": {
229
+ png: "https://cdn.brandfetch.io/id__2e5yMY/w/512/h/512/theme/dark/icon.png"
230
+ },
231
+ "together": {
232
+ svg: "https://cdn.simpleicons.org/togetherai",
233
+ png: "https://cdn.brandfetch.io/idH5EoFVaH/w/512/h/512/theme/dark/icon.png"
234
+ },
235
+ "fireworks-ai": {
236
+ png: "https://cdn.brandfetch.io/idj1VQ2O4C/w/512/h/512/theme/dark/icon.png"
237
+ },
238
+ "replicate": {
239
+ svg: "https://cdn.simpleicons.org/replicate",
240
+ png: "https://cdn.brandfetch.io/idWKE4rRaH/w/512/h/512/theme/dark/icon.png"
241
+ },
242
+ "nebius": {
243
+ png: "https://cdn.brandfetch.io/idiUqSQ52b/w/512/h/512/theme/dark/icon.png"
244
+ },
245
+ "novita": {
246
+ png: "https://novita.ai/favicon.png"
247
+ }
248
+ };
249
+ function getProviderLogo(providerId) {
250
+ if (!providerId) return void 0;
251
+ if (PROVIDER_LOGOS[providerId]) return PROVIDER_LOGOS[providerId];
252
+ const normalized = providerId.toLowerCase().replace(/[-_\s]/g, "");
253
+ for (const [key, logo] of Object.entries(PROVIDER_LOGOS)) {
254
+ if (key.replace(/[-_\s]/g, "") === normalized) return logo;
255
+ }
256
+ return void 0;
257
+ }
258
+
120
259
  // src/registry.ts
121
260
  var Registry = class {
122
261
  providers = /* @__PURE__ */ new Map();
@@ -220,7 +359,8 @@ var Registry = class {
220
359
  local: provider.isLocal,
221
360
  status: "online",
222
361
  // ping-based status is set externally
223
- modelCount: cached?.models.length ?? 0
362
+ modelCount: cached?.models.length ?? 0,
363
+ logo: getProviderLogo(provider.id)
224
364
  });
225
365
  }
226
366
  return infos;
@@ -420,6 +560,8 @@ var PiAiProvider = class {
420
560
  await this.ensureDynamicModels();
421
561
  const models = [];
422
562
  for (const [, m] of this.dynamicModels) {
563
+ const providerName = String(m.provider);
564
+ const logoProvider = this.inferLogoProvider(m.id, providerName);
423
565
  models.push({
424
566
  id: m.id,
425
567
  provider: "pi-ai",
@@ -430,6 +572,7 @@ var PiAiProvider = class {
430
572
  price: m.cost.input ?? 0,
431
573
  unit: m.cost.input > 0 ? "per_1m_tokens" : "free"
432
574
  },
575
+ logo: getProviderLogo(logoProvider),
433
576
  capabilities: {
434
577
  contextWindow: m.contextWindow,
435
578
  maxTokens: m.maxTokens,
@@ -621,6 +764,42 @@ var PiAiProvider = class {
621
764
  this.dynamicModels.clear();
622
765
  await this.ensureDynamicModels();
623
766
  }
767
+ /**
768
+ * Infer the real provider from model ID for logo resolution.
769
+ * e.g. "x-ai/grok-4" → "xai", "anthropic/claude-4" → "anthropic"
770
+ */
771
+ inferLogoProvider(modelId, fallback) {
772
+ const MODEL_PREFIX_TO_PROVIDER = {
773
+ "openai/": "openai",
774
+ "gpt-": "openai",
775
+ "o1-": "openai",
776
+ "o3-": "openai",
777
+ "o4-": "openai",
778
+ "chatgpt-": "openai",
779
+ "anthropic/": "anthropic",
780
+ "claude-": "anthropic",
781
+ "google/": "google",
782
+ "gemini-": "google",
783
+ "gemma-": "google",
784
+ "x-ai/": "xai",
785
+ "grok-": "xai",
786
+ "meta-llama/": "meta",
787
+ "mistralai/": "mistral",
788
+ "mistral-": "mistral",
789
+ "deepseek/": "deepseek",
790
+ "microsoft/": "microsoft",
791
+ "nvidia/": "nvidia",
792
+ "qwen/": "qwen",
793
+ "cohere/": "cohere",
794
+ "perplexity/": "perplexity",
795
+ "amazon/": "amazon"
796
+ };
797
+ const lower = modelId.toLowerCase();
798
+ for (const [prefix, provider] of Object.entries(MODEL_PREFIX_TO_PROVIDER)) {
799
+ if (lower.startsWith(prefix)) return provider;
800
+ }
801
+ return fallback;
802
+ }
624
803
  findModel(modelId) {
625
804
  if (modelId) {
626
805
  const dynamic = this.dynamicModels.get(modelId);
@@ -680,7 +859,8 @@ var FalProvider = class {
680
859
  name: entry.modelId.replace("fal-ai/", ""),
681
860
  modality: inferredModality,
682
861
  local: false,
683
- cost: { price: entry.price, unit: entry.unit }
862
+ cost: { price: entry.price, unit: entry.unit },
863
+ logo: getProviderLogo("fal")
684
864
  });
685
865
  }
686
866
  return models;
@@ -833,6 +1013,7 @@ var ComfyUIProvider = class {
833
1013
  const res = await fetch(`${this.baseUrl}/object_info`);
834
1014
  if (!res.ok) return [];
835
1015
  const models = [];
1016
+ const logo = getProviderLogo("comfyui");
836
1017
  if (!modality || modality === "image") {
837
1018
  models.push({
838
1019
  id: "comfyui-txt2img",
@@ -841,6 +1022,7 @@ var ComfyUIProvider = class {
841
1022
  modality: "image",
842
1023
  local: true,
843
1024
  cost: { price: 0, unit: "free" },
1025
+ logo,
844
1026
  capabilities: { maxWidth: 2048, maxHeight: 2048, supportsNegativePrompt: true }
845
1027
  });
846
1028
  }
@@ -852,6 +1034,7 @@ var ComfyUIProvider = class {
852
1034
  modality: "video",
853
1035
  local: true,
854
1036
  cost: { price: 0, unit: "free" },
1037
+ logo,
855
1038
  capabilities: { maxDuration: 10, supportsImageToVideo: true }
856
1039
  });
857
1040
  }
@@ -963,6 +1146,7 @@ var LocalTTSProvider = class {
963
1146
  voices = data.data ?? [];
964
1147
  }
965
1148
  }
1149
+ const logo = getProviderLogo(this.id);
966
1150
  return voices.map((v) => ({
967
1151
  id: v.id,
968
1152
  provider: this.id,
@@ -970,6 +1154,7 @@ var LocalTTSProvider = class {
970
1154
  modality: "tts",
971
1155
  local: true,
972
1156
  cost: { price: 0, unit: "free" },
1157
+ logo,
973
1158
  capabilities: { voices: voices.map((vv) => vv.id) }
974
1159
  }));
975
1160
  } catch {
@@ -1080,7 +1265,13 @@ var HuggingFaceProvider = class {
1080
1265
  const data = await res.json();
1081
1266
  return data.filter((entry) => entry.id || entry.modelId).map((entry) => {
1082
1267
  const id = entry.id ?? entry.modelId;
1083
- const providers = (entry.inferenceProviderMapping ?? []).filter((p) => p.status === "live").map((p) => p.provider);
1268
+ const liveProviders = (entry.inferenceProviderMapping ?? []).filter((p) => p.status === "live");
1269
+ const providers = liveProviders.map((p) => p.provider);
1270
+ const inferenceProviderLogos = {};
1271
+ for (const p of liveProviders) {
1272
+ const pLogo = getProviderLogo(p.provider);
1273
+ if (pLogo) inferenceProviderLogos[p.provider] = pLogo;
1274
+ }
1084
1275
  const pricingProvider = (entry.inferenceProviderMapping ?? []).find((p) => p.providerDetails?.pricing);
1085
1276
  const pricing = pricingProvider?.providerDetails?.pricing;
1086
1277
  const contextLength = (entry.inferenceProviderMapping ?? []).find((p) => p.providerDetails?.context_length)?.providerDetails?.context_length;
@@ -1094,12 +1285,14 @@ var HuggingFaceProvider = class {
1094
1285
  price: pricing?.input ?? 0,
1095
1286
  unit: pricing ? "per_1m_tokens" : "free"
1096
1287
  },
1288
+ logo: getProviderLogo("huggingface"),
1097
1289
  capabilities: {
1098
1290
  ...modality === "llm" ? {
1099
1291
  contextWindow: contextLength,
1100
1292
  supportsStreaming: true
1101
1293
  } : {},
1102
- ...providers.length > 0 ? { inferenceProviders: providers } : {}
1294
+ ...providers.length > 0 ? { inferenceProviders: providers } : {},
1295
+ ...Object.keys(inferenceProviderLogos).length > 0 ? { inferenceProviderLogos } : {}
1103
1296
  }
1104
1297
  };
1105
1298
  });
@@ -1551,6 +1744,8 @@ var Noosphere = class {
1551
1744
  // Annotate the CommonJS export names for ESM import in node:
1552
1745
  0 && (module.exports = {
1553
1746
  Noosphere,
1554
- NoosphereError
1747
+ NoosphereError,
1748
+ PROVIDER_LOGOS,
1749
+ getProviderLogo
1555
1750
  });
1556
1751
  //# sourceMappingURL=index.cjs.map