noosphere 0.9.0 → 0.9.2
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 +98 -0
- package/dist/index.cjs +288 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -1
- package/dist/index.d.ts +61 -1
- package/dist/index.js +266 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ One import. Every model. Every modality.
|
|
|
16
16
|
- **30+ HuggingFace tasks** — LLM, image, TTS, translation, summarization, classification, and more
|
|
17
17
|
- **Local-first architecture** — Auto-detects Ollama, ComfyUI, Whisper, AudioCraft, Piper, and Kokoro on your machine
|
|
18
18
|
- **Org-aware logos** — HuggingFace models show the real org logo (Meta, Google, NVIDIA) instead of generic HF logo
|
|
19
|
+
- **Pre-request token counting** — Count tokens before sending, for ALL providers (OpenAI/Groq/Ollama via tiktoken, Google/Anthropic via API)
|
|
20
|
+
- **Full pi-ai access** — Agent loop with tool calling, preprocessor (compaction hook), `calculateCost`, direct stream/complete APIs — all re-exported
|
|
19
21
|
- **Agentic capabilities** — Tool use, function calling, reasoning/thinking, vision, and agent loops via Pi-AI
|
|
20
22
|
- **Failover & retry** — Automatic retries with exponential backoff and cross-provider failover
|
|
21
23
|
- **Usage tracking** — Real-time cost, latency, and token tracking across all providers
|
|
@@ -629,6 +631,102 @@ Noosphere auto-detects all local runtimes on startup:
|
|
|
629
631
|
|
|
630
632
|
---
|
|
631
633
|
|
|
634
|
+
## Pre-Request Token Counting
|
|
635
|
+
|
|
636
|
+
Count tokens **before** sending a request to any provider. Know the cost upfront.
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
// Via Noosphere instance (auto-routes by model)
|
|
640
|
+
const result = await ai.countTokens({
|
|
641
|
+
messages: [
|
|
642
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
643
|
+
{ role: 'user', content: 'Explain quantum computing.' },
|
|
644
|
+
],
|
|
645
|
+
model: 'gpt-4o',
|
|
646
|
+
});
|
|
647
|
+
console.log(result.tokens); // 26
|
|
648
|
+
console.log(result.method); // "tiktoken" (instant, local)
|
|
649
|
+
console.log(result.provider); // "openai"
|
|
650
|
+
|
|
651
|
+
// Google — exact count via API
|
|
652
|
+
const google = await ai.countTokens({
|
|
653
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
654
|
+
model: 'gemini-2.5-flash',
|
|
655
|
+
});
|
|
656
|
+
console.log(google.tokens); // 3
|
|
657
|
+
console.log(google.method); // "api" (exact)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
**Token counting by provider:**
|
|
661
|
+
|
|
662
|
+
| Provider | Method | Speed | Accuracy |
|
|
663
|
+
|---|---|---|---|
|
|
664
|
+
| **OpenAI** (GPT-4o, o1, o3, o4, GPT-5) | tiktoken (local) | Instant | Exact |
|
|
665
|
+
| **Google** (Gemini) | `/countTokens` API | ~200ms | Exact |
|
|
666
|
+
| **Anthropic** (Claude) | `/messages/count_tokens` API | ~200ms | Exact |
|
|
667
|
+
| **Groq** (Llama, Mixtral, Gemma) | tiktoken (local) | Instant | Exact |
|
|
668
|
+
| **Cerebras** (Llama) | tiktoken (local) | Instant | Exact |
|
|
669
|
+
| **Mistral** (Mistral, Mixtral, Codestral) | tiktoken (local) | Instant | Close approx |
|
|
670
|
+
| **xAI** (Grok) | tiktoken (local) | Instant | Close approx |
|
|
671
|
+
| **OpenRouter** (all models) | tiktoken (local) | Instant | Close approx |
|
|
672
|
+
| **Ollama** (all local models) | tiktoken (local) | Instant | Close approx |
|
|
673
|
+
|
|
674
|
+
You can also use standalone functions without a Noosphere instance:
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
import {
|
|
678
|
+
countTokensOpenAI, countTokensGoogle, countTokensAnthropic,
|
|
679
|
+
countTokensGroq, countTokensMistral, countTokensXai,
|
|
680
|
+
countTokensCerebras, countTokensOpenRouter, countTokensOllama,
|
|
681
|
+
} from 'noosphere';
|
|
682
|
+
|
|
683
|
+
// Local (instant, no API key needed)
|
|
684
|
+
const tokens = countTokensOpenAI(messages, 'gpt-4o'); // 26
|
|
685
|
+
const groq = countTokensGroq(messages, 'llama-3.3-70b'); // 26
|
|
686
|
+
const ollama = countTokensOllama(messages, 'qwen3:8b'); // 26
|
|
687
|
+
|
|
688
|
+
// API-based (exact, needs key)
|
|
689
|
+
const google = await countTokensGoogle(messages, GEMINI_KEY, 'gemini-2.5-flash'); // 16
|
|
690
|
+
const claude = await countTokensAnthropic(messages, ANTHROPIC_KEY, 'claude-sonnet-4-20250514'); // exact
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
## Agent Loop & pi-ai Access
|
|
696
|
+
|
|
697
|
+
Noosphere re-exports the full [pi-ai](https://github.com/nicholasgriffintn/pi-ai) library for direct access to agent loops, tool calling, cost calculation, and streaming APIs.
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
import {
|
|
701
|
+
agentLoop, calculateCost,
|
|
702
|
+
piStream, piComplete, piStreamSimple, piCompleteSimple,
|
|
703
|
+
setApiKey, getApiKey, getPiModel, getPiModels, getPiProviders,
|
|
704
|
+
} from 'noosphere';
|
|
705
|
+
|
|
706
|
+
// Agent loop with tool calling and preprocessor (compaction hook)
|
|
707
|
+
import type { AgentLoopConfig, AgentContext, AgentTool } from 'noosphere';
|
|
708
|
+
|
|
709
|
+
const config: AgentLoopConfig = {
|
|
710
|
+
model: getPiModel('openai', 'gpt-4o'),
|
|
711
|
+
// Preprocessor runs before each LLM call — use for context compaction
|
|
712
|
+
preprocessor: async (messages) => {
|
|
713
|
+
// Truncate old messages, summarize, etc.
|
|
714
|
+
if (messages.length > 50) {
|
|
715
|
+
return messages.slice(-20); // keep last 20
|
|
716
|
+
}
|
|
717
|
+
return messages;
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// Calculate cost before sending
|
|
722
|
+
const model = getPiModel('openai', 'gpt-4o');
|
|
723
|
+
const usage = { input: 1000, output: 500, cacheRead: 0, cacheWrite: 0 };
|
|
724
|
+
const cost = calculateCost(model, usage);
|
|
725
|
+
console.log(cost.total); // $0.00625
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
---
|
|
729
|
+
|
|
632
730
|
## Configuration
|
|
633
731
|
|
|
634
732
|
API keys are resolved from the constructor config or environment variables (config takes priority):
|
package/dist/index.cjs
CHANGED
|
@@ -31,9 +31,30 @@ __export(index_exports, {
|
|
|
31
31
|
PROVIDER_IDS: () => PROVIDER_IDS,
|
|
32
32
|
PROVIDER_LOGOS: () => PROVIDER_LOGOS,
|
|
33
33
|
WhisperLocalProvider: () => WhisperLocalProvider,
|
|
34
|
+
agentLoop: () => import_pi_ai3.agentLoop,
|
|
35
|
+
calculateCost: () => import_pi_ai4.calculateCost,
|
|
36
|
+
countTokens: () => countTokens,
|
|
37
|
+
countTokensAnthropic: () => countTokensAnthropic,
|
|
38
|
+
countTokensCerebras: () => countTokensCerebras,
|
|
39
|
+
countTokensGoogle: () => countTokensGoogle,
|
|
40
|
+
countTokensGroq: () => countTokensGroq,
|
|
41
|
+
countTokensMistral: () => countTokensMistral,
|
|
42
|
+
countTokensOllama: () => countTokensOllama,
|
|
43
|
+
countTokensOpenAI: () => countTokensOpenAI,
|
|
44
|
+
countTokensOpenRouter: () => countTokensOpenRouter,
|
|
45
|
+
countTokensXai: () => countTokensXai,
|
|
34
46
|
detectOpenAICompatServers: () => detectOpenAICompatServers,
|
|
35
47
|
getAllProviderLogos: () => getAllProviderLogos,
|
|
36
|
-
|
|
48
|
+
getApiKey: () => import_pi_ai6.getApiKey,
|
|
49
|
+
getPiModel: () => import_pi_ai4.getModel,
|
|
50
|
+
getPiModels: () => import_pi_ai4.getModels,
|
|
51
|
+
getPiProviders: () => import_pi_ai4.getProviders,
|
|
52
|
+
getProviderLogo: () => getProviderLogo,
|
|
53
|
+
piComplete: () => import_pi_ai5.complete,
|
|
54
|
+
piCompleteSimple: () => import_pi_ai5.completeSimple,
|
|
55
|
+
piStream: () => import_pi_ai5.stream,
|
|
56
|
+
piStreamSimple: () => import_pi_ai5.streamSimple,
|
|
57
|
+
setApiKey: () => import_pi_ai6.setApiKey
|
|
37
58
|
});
|
|
38
59
|
module.exports = __toCommonJS(index_exports);
|
|
39
60
|
|
|
@@ -140,6 +161,136 @@ function resolveConfig(input) {
|
|
|
140
161
|
};
|
|
141
162
|
}
|
|
142
163
|
|
|
164
|
+
// src/token-counter.ts
|
|
165
|
+
var import_tiktoken = require("tiktoken");
|
|
166
|
+
var TIKTOKEN_MODEL_MAP = {
|
|
167
|
+
"gpt-4o": "gpt-4o",
|
|
168
|
+
"gpt-4o-mini": "gpt-4o-mini",
|
|
169
|
+
"gpt-4-turbo": "gpt-4-turbo",
|
|
170
|
+
"gpt-4": "gpt-4",
|
|
171
|
+
"gpt-3.5-turbo": "gpt-3.5-turbo"
|
|
172
|
+
};
|
|
173
|
+
function resolveTiktokenModel(model) {
|
|
174
|
+
if (model in TIKTOKEN_MODEL_MAP) return TIKTOKEN_MODEL_MAP[model];
|
|
175
|
+
for (const [prefix, tikModel] of Object.entries(TIKTOKEN_MODEL_MAP)) {
|
|
176
|
+
if (model.startsWith(prefix)) return tikModel;
|
|
177
|
+
}
|
|
178
|
+
return "gpt-4o";
|
|
179
|
+
}
|
|
180
|
+
function countWithTiktoken(messages, model) {
|
|
181
|
+
const tikModel = resolveTiktokenModel(model);
|
|
182
|
+
const enc = (0, import_tiktoken.encoding_for_model)(tikModel);
|
|
183
|
+
let tokens = 0;
|
|
184
|
+
for (const msg of messages) {
|
|
185
|
+
tokens += 4;
|
|
186
|
+
tokens += enc.encode(msg.role).length;
|
|
187
|
+
tokens += enc.encode(msg.content).length;
|
|
188
|
+
}
|
|
189
|
+
tokens += 2;
|
|
190
|
+
enc.free();
|
|
191
|
+
return tokens;
|
|
192
|
+
}
|
|
193
|
+
function countTokensOpenAI(messages, model = "gpt-4o") {
|
|
194
|
+
return countWithTiktoken(messages, model);
|
|
195
|
+
}
|
|
196
|
+
async function countTokensGoogle(messages, apiKey, model = "gemini-2.5-flash") {
|
|
197
|
+
const contents = messages.map((m) => ({
|
|
198
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
199
|
+
parts: [{ text: m.content }]
|
|
200
|
+
}));
|
|
201
|
+
const res = await fetch(
|
|
202
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${model}:countTokens?key=${apiKey}`,
|
|
203
|
+
{
|
|
204
|
+
method: "POST",
|
|
205
|
+
headers: { "Content-Type": "application/json" },
|
|
206
|
+
body: JSON.stringify({ contents })
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
if (!res.ok) {
|
|
210
|
+
throw new Error(`Google countTokens failed (${res.status}): ${await res.text()}`);
|
|
211
|
+
}
|
|
212
|
+
const data = await res.json();
|
|
213
|
+
return data.totalTokens;
|
|
214
|
+
}
|
|
215
|
+
async function countTokensAnthropic(messages, apiKey, model = "claude-sonnet-4-20250514") {
|
|
216
|
+
const anthropicMessages = messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
|
|
217
|
+
const systemPrompt = messages.find((m) => m.role === "system")?.content;
|
|
218
|
+
const body = {
|
|
219
|
+
model,
|
|
220
|
+
messages: anthropicMessages
|
|
221
|
+
};
|
|
222
|
+
if (systemPrompt) body.system = systemPrompt;
|
|
223
|
+
const res = await fetch("https://api.anthropic.com/v1/messages/count_tokens", {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: {
|
|
226
|
+
"x-api-key": apiKey,
|
|
227
|
+
"anthropic-version": "2023-06-01",
|
|
228
|
+
"Content-Type": "application/json"
|
|
229
|
+
},
|
|
230
|
+
body: JSON.stringify(body)
|
|
231
|
+
});
|
|
232
|
+
if (!res.ok) {
|
|
233
|
+
throw new Error(`Anthropic countTokens failed (${res.status}): ${await res.text()}`);
|
|
234
|
+
}
|
|
235
|
+
const data = await res.json();
|
|
236
|
+
return data.input_tokens;
|
|
237
|
+
}
|
|
238
|
+
function countTokensGroq(messages, model = "llama-3.3-70b-versatile") {
|
|
239
|
+
return countWithTiktoken(messages, model);
|
|
240
|
+
}
|
|
241
|
+
function countTokensMistral(messages, model = "mistral-large-latest") {
|
|
242
|
+
return countWithTiktoken(messages, model);
|
|
243
|
+
}
|
|
244
|
+
function countTokensXai(messages, model = "grok-3") {
|
|
245
|
+
return countWithTiktoken(messages, model);
|
|
246
|
+
}
|
|
247
|
+
function countTokensCerebras(messages, model = "llama-3.3-70b") {
|
|
248
|
+
return countWithTiktoken(messages, model);
|
|
249
|
+
}
|
|
250
|
+
function countTokensOpenRouter(messages, model = "openai/gpt-4o") {
|
|
251
|
+
return countWithTiktoken(messages, model);
|
|
252
|
+
}
|
|
253
|
+
function countTokensOllama(messages, model = "llama3.2") {
|
|
254
|
+
return countWithTiktoken(messages, model);
|
|
255
|
+
}
|
|
256
|
+
var PROVIDER_MODEL_PREFIXES = [
|
|
257
|
+
{ prefixes: ["gemini", "imagen", "veo"], provider: "google" },
|
|
258
|
+
{ prefixes: ["claude"], provider: "anthropic" },
|
|
259
|
+
{ prefixes: ["gpt-", "o1", "o3", "o4", "chatgpt", "dall-e", "gpt-image", "tts-", "whisper", "sora"], provider: "openai" },
|
|
260
|
+
{ prefixes: ["grok"], provider: "xai" },
|
|
261
|
+
{ prefixes: ["mistral", "mixtral", "codestral", "ministral"], provider: "mistral" },
|
|
262
|
+
{ prefixes: ["llama", "gemma", "qwen", "deepseek", "phi"], provider: "groq" }
|
|
263
|
+
];
|
|
264
|
+
function inferProvider(model) {
|
|
265
|
+
const lower = model.toLowerCase();
|
|
266
|
+
for (const { prefixes, provider } of PROVIDER_MODEL_PREFIXES) {
|
|
267
|
+
for (const prefix of prefixes) {
|
|
268
|
+
if (lower.startsWith(prefix)) return provider;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return "unknown";
|
|
272
|
+
}
|
|
273
|
+
async function countTokens(options, apiKeys) {
|
|
274
|
+
const model = options.model ?? "gpt-4o";
|
|
275
|
+
const provider = options.provider ?? inferProvider(model);
|
|
276
|
+
if (provider === "google" && apiKeys?.google) {
|
|
277
|
+
const tokens2 = await countTokensGoogle(options.messages, apiKeys.google, model);
|
|
278
|
+
return { tokens: tokens2, model, provider: "google", method: "api" };
|
|
279
|
+
}
|
|
280
|
+
if (provider === "anthropic" && apiKeys?.anthropic) {
|
|
281
|
+
const tokens2 = await countTokensAnthropic(options.messages, apiKeys.anthropic, model);
|
|
282
|
+
return { tokens: tokens2, model, provider: "anthropic", method: "api" };
|
|
283
|
+
}
|
|
284
|
+
const tokens = countWithTiktoken(options.messages, model);
|
|
285
|
+
const resolvedProvider = provider === "unknown" ? "openai" : provider;
|
|
286
|
+
return {
|
|
287
|
+
tokens,
|
|
288
|
+
model,
|
|
289
|
+
provider: resolvedProvider,
|
|
290
|
+
method: provider === "openai" ? "tiktoken" : "tiktoken"
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
143
294
|
// src/logos.ts
|
|
144
295
|
var CDN_BASE = "https://blockchainstarter.nyc3.digitaloceanspaces.com/noosphere/logos";
|
|
145
296
|
var PROVIDER_IDS = [
|
|
@@ -2565,6 +2716,36 @@ var OpenAIMediaProvider = class {
|
|
|
2565
2716
|
modalities = ["image", "video", "tts", "stt"];
|
|
2566
2717
|
isLocal = false;
|
|
2567
2718
|
modelsCache = null;
|
|
2719
|
+
voicesCache = null;
|
|
2720
|
+
/** Auto-fetch available TTS voices by sending an invalid voice and parsing the error. */
|
|
2721
|
+
async fetchVoices() {
|
|
2722
|
+
if (this.voicesCache) return this.voicesCache;
|
|
2723
|
+
try {
|
|
2724
|
+
const res = await fetch(`${OPENAI_API_BASE}/audio/speech`, {
|
|
2725
|
+
method: "POST",
|
|
2726
|
+
headers: {
|
|
2727
|
+
"Content-Type": "application/json",
|
|
2728
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2729
|
+
},
|
|
2730
|
+
body: JSON.stringify({ model: "tts-1", input: ".", voice: "__discover_voices__" })
|
|
2731
|
+
});
|
|
2732
|
+
if (!res.ok) {
|
|
2733
|
+
const data = await res.json();
|
|
2734
|
+
const msg = data?.error?.message ?? "";
|
|
2735
|
+
const shouldBe = msg.match(/Input should be ([^"]+)/);
|
|
2736
|
+
if (shouldBe) {
|
|
2737
|
+
const voiceList = shouldBe[1].match(/'([a-z]+)'/g);
|
|
2738
|
+
if (voiceList && voiceList.length > 0) {
|
|
2739
|
+
this.voicesCache = voiceList.map((v) => v.replace(/'/g, ""));
|
|
2740
|
+
return this.voicesCache;
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
} catch {
|
|
2745
|
+
}
|
|
2746
|
+
this.voicesCache = [];
|
|
2747
|
+
return this.voicesCache;
|
|
2748
|
+
}
|
|
2568
2749
|
async ping() {
|
|
2569
2750
|
try {
|
|
2570
2751
|
const controller = new AbortController();
|
|
@@ -2600,6 +2781,7 @@ var OpenAIMediaProvider = class {
|
|
|
2600
2781
|
} finally {
|
|
2601
2782
|
clearTimeout(timer);
|
|
2602
2783
|
}
|
|
2784
|
+
const voices = await this.fetchVoices();
|
|
2603
2785
|
const entries = data?.data ?? [];
|
|
2604
2786
|
const logo = getProviderLogo("openai");
|
|
2605
2787
|
const models = [];
|
|
@@ -2615,7 +2797,7 @@ var OpenAIMediaProvider = class {
|
|
|
2615
2797
|
cost: { price: 0, unit: "per_request" },
|
|
2616
2798
|
logo,
|
|
2617
2799
|
description: entry.description,
|
|
2618
|
-
capabilities: this.getCapabilities(entry.id, mod)
|
|
2800
|
+
capabilities: this.getCapabilities(entry.id, mod, voices)
|
|
2619
2801
|
};
|
|
2620
2802
|
models.push(info);
|
|
2621
2803
|
}
|
|
@@ -2760,7 +2942,7 @@ var OpenAIMediaProvider = class {
|
|
|
2760
2942
|
}
|
|
2761
2943
|
};
|
|
2762
2944
|
}
|
|
2763
|
-
getCapabilities(id, modality) {
|
|
2945
|
+
getCapabilities(id, modality, voices) {
|
|
2764
2946
|
if (modality === "image") {
|
|
2765
2947
|
return {
|
|
2766
2948
|
maxWidth: id.startsWith("dall-e-3") ? 1792 : 1024,
|
|
@@ -2769,7 +2951,7 @@ var OpenAIMediaProvider = class {
|
|
|
2769
2951
|
}
|
|
2770
2952
|
if (modality === "tts") {
|
|
2771
2953
|
return {
|
|
2772
|
-
voices:
|
|
2954
|
+
voices: voices && voices.length > 0 ? voices : void 0
|
|
2773
2955
|
};
|
|
2774
2956
|
}
|
|
2775
2957
|
if (modality === "video") {
|
|
@@ -2790,18 +2972,34 @@ var OpenAIMediaProvider = class {
|
|
|
2790
2972
|
// src/providers/google-media.ts
|
|
2791
2973
|
var GOOGLE_API_BASE = "https://generativelanguage.googleapis.com/v1beta";
|
|
2792
2974
|
var FETCH_TIMEOUT_MS6 = 8e3;
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2975
|
+
async function fetchGoogleVoices(apiKey) {
|
|
2976
|
+
try {
|
|
2977
|
+
const res = await fetch(
|
|
2978
|
+
`${GOOGLE_API_BASE}/models/gemini-2.5-flash-preview-tts:generateContent?key=${apiKey}`,
|
|
2979
|
+
{
|
|
2980
|
+
method: "POST",
|
|
2981
|
+
headers: { "Content-Type": "application/json" },
|
|
2982
|
+
body: JSON.stringify({
|
|
2983
|
+
contents: [{ parts: [{ text: "." }] }],
|
|
2984
|
+
generationConfig: {
|
|
2985
|
+
response_modalities: ["AUDIO"],
|
|
2986
|
+
speech_config: { voiceConfig: { prebuiltVoiceConfig: { voiceName: "__discover_voices__" } } }
|
|
2987
|
+
}
|
|
2988
|
+
})
|
|
2989
|
+
}
|
|
2990
|
+
);
|
|
2991
|
+
if (!res.ok) {
|
|
2992
|
+
const data = await res.json();
|
|
2993
|
+
const msg = data?.error?.message ?? "";
|
|
2994
|
+
const match = msg.match(/Allowed voice names are:\s*(.+)/i);
|
|
2995
|
+
if (match) {
|
|
2996
|
+
return match[1].split(",").map((v) => v.trim()).filter(Boolean);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
} catch {
|
|
3000
|
+
}
|
|
3001
|
+
return [];
|
|
3002
|
+
}
|
|
2805
3003
|
function classifyGoogleModel(model) {
|
|
2806
3004
|
const name = (model.name ?? "").replace("models/", "");
|
|
2807
3005
|
const methods = model.supportedGenerationMethods ?? [];
|
|
@@ -2819,6 +3017,7 @@ var GoogleMediaProvider = class {
|
|
|
2819
3017
|
modalities = ["image", "video", "tts"];
|
|
2820
3018
|
isLocal = false;
|
|
2821
3019
|
modelsCache = null;
|
|
3020
|
+
voicesCache = null;
|
|
2822
3021
|
async ping() {
|
|
2823
3022
|
try {
|
|
2824
3023
|
const controller = new AbortController();
|
|
@@ -2853,6 +3052,9 @@ var GoogleMediaProvider = class {
|
|
|
2853
3052
|
clearTimeout(timer);
|
|
2854
3053
|
}
|
|
2855
3054
|
const entries = data?.models ?? [];
|
|
3055
|
+
if (!this.voicesCache) {
|
|
3056
|
+
this.voicesCache = await fetchGoogleVoices(this.apiKey);
|
|
3057
|
+
}
|
|
2856
3058
|
const logo = getProviderLogo("google");
|
|
2857
3059
|
const models = [];
|
|
2858
3060
|
for (const entry of entries) {
|
|
@@ -2869,7 +3071,7 @@ var GoogleMediaProvider = class {
|
|
|
2869
3071
|
cost: { price: 0, unit: modality2 === "video" ? "per_video" : "per_image" },
|
|
2870
3072
|
logo,
|
|
2871
3073
|
description: entry.description,
|
|
2872
|
-
capabilities: modality2 === "video" ? { maxDuration: 8, supportsStreaming: false } : modality2 === "tts" ? { voices:
|
|
3074
|
+
capabilities: modality2 === "video" ? { maxDuration: 8, supportsStreaming: false } : modality2 === "tts" ? { voices: this.voicesCache && this.voicesCache.length > 0 ? this.voicesCache : void 0 } : void 0
|
|
2873
3075
|
};
|
|
2874
3076
|
models.push(info);
|
|
2875
3077
|
}
|
|
@@ -2972,7 +3174,7 @@ var GoogleMediaProvider = class {
|
|
|
2972
3174
|
};
|
|
2973
3175
|
}
|
|
2974
3176
|
async video(options) {
|
|
2975
|
-
const model = options.model ?? "veo-
|
|
3177
|
+
const model = options.model ?? "veo-2.0-generate-001";
|
|
2976
3178
|
const start = Date.now();
|
|
2977
3179
|
const body = {
|
|
2978
3180
|
instances: [{ prompt: options.prompt }],
|
|
@@ -3007,10 +3209,15 @@ var GoogleMediaProvider = class {
|
|
|
3007
3209
|
if (!pollRes.ok) continue;
|
|
3008
3210
|
const status = await pollRes.json();
|
|
3009
3211
|
if (status.done) {
|
|
3010
|
-
|
|
3011
|
-
|
|
3212
|
+
if (status.error) {
|
|
3213
|
+
throw new Error(`Google video generation error: ${status.error.message ?? JSON.stringify(status.error)}`);
|
|
3214
|
+
}
|
|
3215
|
+
const resp = status.response ?? {};
|
|
3216
|
+
const samples = resp.generateVideoResponse?.generatedSamples ?? resp.generatedSamples ?? [];
|
|
3217
|
+
const video = samples[0]?.video;
|
|
3218
|
+
if (video?.bytesBase64Encoded) {
|
|
3012
3219
|
return {
|
|
3013
|
-
buffer: Buffer.from(
|
|
3220
|
+
buffer: Buffer.from(video.bytesBase64Encoded, "base64"),
|
|
3014
3221
|
provider: "google-media",
|
|
3015
3222
|
model,
|
|
3016
3223
|
modality: "video",
|
|
@@ -3019,16 +3226,32 @@ var GoogleMediaProvider = class {
|
|
|
3019
3226
|
media: { format: "mp4", duration: options.duration }
|
|
3020
3227
|
};
|
|
3021
3228
|
}
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3229
|
+
if (video?.uri) {
|
|
3230
|
+
const separator = video.uri.includes("?") ? "&" : "?";
|
|
3231
|
+
const videoRes = await fetch(`${video.uri}${separator}key=${this.apiKey}`, { redirect: "follow" });
|
|
3232
|
+
if (videoRes.ok) {
|
|
3233
|
+
const buffer = Buffer.from(await videoRes.arrayBuffer());
|
|
3234
|
+
return {
|
|
3235
|
+
buffer,
|
|
3236
|
+
provider: "google-media",
|
|
3237
|
+
model,
|
|
3238
|
+
modality: "video",
|
|
3239
|
+
latencyMs: Date.now() - start,
|
|
3240
|
+
usage: { cost: 0, unit: "per_video" },
|
|
3241
|
+
media: { format: "mp4", duration: options.duration }
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
return {
|
|
3245
|
+
url: video.uri,
|
|
3246
|
+
provider: "google-media",
|
|
3247
|
+
model,
|
|
3248
|
+
modality: "video",
|
|
3249
|
+
latencyMs: Date.now() - start,
|
|
3250
|
+
usage: { cost: 0, unit: "per_video" },
|
|
3251
|
+
media: { format: "mp4", duration: options.duration }
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
throw new Error("Google video generation completed but returned no video data");
|
|
3032
3255
|
}
|
|
3033
3256
|
}
|
|
3034
3257
|
throw new Error(`Google video generation timed out after 5 minutes`);
|
|
@@ -3225,6 +3448,12 @@ var Noosphere = class {
|
|
|
3225
3448
|
getUsage(options) {
|
|
3226
3449
|
return this.tracker.getSummary(options);
|
|
3227
3450
|
}
|
|
3451
|
+
async countTokens(options) {
|
|
3452
|
+
const keys = {};
|
|
3453
|
+
if (this.config.keys?.google) keys.google = this.config.keys.google;
|
|
3454
|
+
if (this.config.keys?.anthropic) keys.anthropic = this.config.keys.anthropic;
|
|
3455
|
+
return countTokens(options, keys);
|
|
3456
|
+
}
|
|
3228
3457
|
// --- Local Model Management ---
|
|
3229
3458
|
async installModel(name) {
|
|
3230
3459
|
if (!this.initialized) await this.init();
|
|
@@ -3459,6 +3688,12 @@ var Noosphere = class {
|
|
|
3459
3688
|
await this.tracker.record(event);
|
|
3460
3689
|
}
|
|
3461
3690
|
};
|
|
3691
|
+
|
|
3692
|
+
// src/index.ts
|
|
3693
|
+
var import_pi_ai3 = require("@mariozechner/pi-ai");
|
|
3694
|
+
var import_pi_ai4 = require("@mariozechner/pi-ai");
|
|
3695
|
+
var import_pi_ai5 = require("@mariozechner/pi-ai");
|
|
3696
|
+
var import_pi_ai6 = require("@mariozechner/pi-ai");
|
|
3462
3697
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3463
3698
|
0 && (module.exports = {
|
|
3464
3699
|
AudioCraftProvider,
|
|
@@ -3472,8 +3707,29 @@ var Noosphere = class {
|
|
|
3472
3707
|
PROVIDER_IDS,
|
|
3473
3708
|
PROVIDER_LOGOS,
|
|
3474
3709
|
WhisperLocalProvider,
|
|
3710
|
+
agentLoop,
|
|
3711
|
+
calculateCost,
|
|
3712
|
+
countTokens,
|
|
3713
|
+
countTokensAnthropic,
|
|
3714
|
+
countTokensCerebras,
|
|
3715
|
+
countTokensGoogle,
|
|
3716
|
+
countTokensGroq,
|
|
3717
|
+
countTokensMistral,
|
|
3718
|
+
countTokensOllama,
|
|
3719
|
+
countTokensOpenAI,
|
|
3720
|
+
countTokensOpenRouter,
|
|
3721
|
+
countTokensXai,
|
|
3475
3722
|
detectOpenAICompatServers,
|
|
3476
3723
|
getAllProviderLogos,
|
|
3477
|
-
|
|
3724
|
+
getApiKey,
|
|
3725
|
+
getPiModel,
|
|
3726
|
+
getPiModels,
|
|
3727
|
+
getPiProviders,
|
|
3728
|
+
getProviderLogo,
|
|
3729
|
+
piComplete,
|
|
3730
|
+
piCompleteSimple,
|
|
3731
|
+
piStream,
|
|
3732
|
+
piStreamSimple,
|
|
3733
|
+
setApiKey
|
|
3478
3734
|
});
|
|
3479
3735
|
//# sourceMappingURL=index.cjs.map
|