noosphere 0.8.0 → 0.9.1
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 +126 -8
- package/dist/index.cjs +712 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -1
- package/dist/index.d.ts +88 -1
- package/dist/index.js +688 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -21,17 +21,40 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AudioCraftProvider: () => AudioCraftProvider,
|
|
24
|
+
GoogleMediaProvider: () => GoogleMediaProvider,
|
|
24
25
|
HfLocalProvider: () => HfLocalProvider,
|
|
25
26
|
Noosphere: () => Noosphere,
|
|
26
27
|
NoosphereError: () => NoosphereError,
|
|
27
28
|
OllamaProvider: () => OllamaProvider,
|
|
28
29
|
OpenAICompatProvider: () => OpenAICompatProvider,
|
|
30
|
+
OpenAIMediaProvider: () => OpenAIMediaProvider,
|
|
29
31
|
PROVIDER_IDS: () => PROVIDER_IDS,
|
|
30
32
|
PROVIDER_LOGOS: () => PROVIDER_LOGOS,
|
|
31
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,
|
|
32
46
|
detectOpenAICompatServers: () => detectOpenAICompatServers,
|
|
33
47
|
getAllProviderLogos: () => getAllProviderLogos,
|
|
34
|
-
|
|
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
|
|
35
58
|
});
|
|
36
59
|
module.exports = __toCommonJS(index_exports);
|
|
37
60
|
|
|
@@ -138,6 +161,136 @@ function resolveConfig(input) {
|
|
|
138
161
|
};
|
|
139
162
|
}
|
|
140
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
|
+
|
|
141
294
|
// src/logos.ts
|
|
142
295
|
var CDN_BASE = "https://blockchainstarter.nyc3.digitaloceanspaces.com/noosphere/logos";
|
|
143
296
|
var PROVIDER_IDS = [
|
|
@@ -2538,6 +2691,522 @@ async function detectOpenAICompatServers() {
|
|
|
2538
2691
|
return providers;
|
|
2539
2692
|
}
|
|
2540
2693
|
|
|
2694
|
+
// src/providers/openai-media.ts
|
|
2695
|
+
var OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
2696
|
+
var FETCH_TIMEOUT_MS5 = 8e3;
|
|
2697
|
+
var MODEL_PREFIX_MAP = [
|
|
2698
|
+
{ prefix: "dall-e-", modality: "image" },
|
|
2699
|
+
{ prefix: "gpt-image-", modality: "image" },
|
|
2700
|
+
{ prefix: "sora-", modality: "video" },
|
|
2701
|
+
{ prefix: "tts-", modality: "tts" },
|
|
2702
|
+
{ prefix: "whisper-", modality: "stt" }
|
|
2703
|
+
];
|
|
2704
|
+
function classifyModel(id) {
|
|
2705
|
+
for (const { prefix, modality } of MODEL_PREFIX_MAP) {
|
|
2706
|
+
if (id.startsWith(prefix)) return modality;
|
|
2707
|
+
}
|
|
2708
|
+
return null;
|
|
2709
|
+
}
|
|
2710
|
+
var OpenAIMediaProvider = class {
|
|
2711
|
+
constructor(apiKey) {
|
|
2712
|
+
this.apiKey = apiKey;
|
|
2713
|
+
}
|
|
2714
|
+
id = "openai-media";
|
|
2715
|
+
name = "OpenAI (Image, Video, TTS, STT)";
|
|
2716
|
+
modalities = ["image", "video", "tts", "stt"];
|
|
2717
|
+
isLocal = false;
|
|
2718
|
+
modelsCache = null;
|
|
2719
|
+
async ping() {
|
|
2720
|
+
try {
|
|
2721
|
+
const controller = new AbortController();
|
|
2722
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS5);
|
|
2723
|
+
try {
|
|
2724
|
+
const res = await fetch(`${OPENAI_API_BASE}/models`, {
|
|
2725
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
2726
|
+
signal: controller.signal
|
|
2727
|
+
});
|
|
2728
|
+
return res.ok;
|
|
2729
|
+
} finally {
|
|
2730
|
+
clearTimeout(timer);
|
|
2731
|
+
}
|
|
2732
|
+
} catch {
|
|
2733
|
+
return false;
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
async listModels(modality) {
|
|
2737
|
+
if (this.modelsCache) {
|
|
2738
|
+
return modality ? this.modelsCache.filter((m) => m.modality === modality) : this.modelsCache;
|
|
2739
|
+
}
|
|
2740
|
+
try {
|
|
2741
|
+
const controller = new AbortController();
|
|
2742
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS5);
|
|
2743
|
+
let data;
|
|
2744
|
+
try {
|
|
2745
|
+
const res = await fetch(`${OPENAI_API_BASE}/models`, {
|
|
2746
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
2747
|
+
signal: controller.signal
|
|
2748
|
+
});
|
|
2749
|
+
if (!res.ok) return [];
|
|
2750
|
+
data = await res.json();
|
|
2751
|
+
} finally {
|
|
2752
|
+
clearTimeout(timer);
|
|
2753
|
+
}
|
|
2754
|
+
const entries = data?.data ?? [];
|
|
2755
|
+
const logo = getProviderLogo("openai");
|
|
2756
|
+
const models = [];
|
|
2757
|
+
for (const entry of entries) {
|
|
2758
|
+
const mod = classifyModel(entry.id);
|
|
2759
|
+
if (!mod) continue;
|
|
2760
|
+
const info = {
|
|
2761
|
+
id: entry.id,
|
|
2762
|
+
provider: "openai-media",
|
|
2763
|
+
name: entry.id,
|
|
2764
|
+
modality: mod,
|
|
2765
|
+
local: false,
|
|
2766
|
+
cost: { price: 0, unit: "per_request" },
|
|
2767
|
+
logo,
|
|
2768
|
+
description: entry.description,
|
|
2769
|
+
capabilities: this.getCapabilities(entry.id, mod)
|
|
2770
|
+
};
|
|
2771
|
+
models.push(info);
|
|
2772
|
+
}
|
|
2773
|
+
this.modelsCache = models;
|
|
2774
|
+
return modality ? models.filter((m) => m.modality === modality) : models;
|
|
2775
|
+
} catch {
|
|
2776
|
+
return [];
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
async image(options) {
|
|
2780
|
+
const model = options.model ?? "gpt-image-1";
|
|
2781
|
+
const width = options.width ?? 1024;
|
|
2782
|
+
const height = options.height ?? 1024;
|
|
2783
|
+
const start = Date.now();
|
|
2784
|
+
const isGptImage = model.startsWith("gpt-image-");
|
|
2785
|
+
const body = {
|
|
2786
|
+
model,
|
|
2787
|
+
prompt: options.prompt,
|
|
2788
|
+
n: 1,
|
|
2789
|
+
size: `${width}x${height}`
|
|
2790
|
+
};
|
|
2791
|
+
if (!isGptImage) {
|
|
2792
|
+
body.response_format = "url";
|
|
2793
|
+
}
|
|
2794
|
+
const res = await fetch(`${OPENAI_API_BASE}/images/generations`, {
|
|
2795
|
+
method: "POST",
|
|
2796
|
+
headers: {
|
|
2797
|
+
"Content-Type": "application/json",
|
|
2798
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2799
|
+
},
|
|
2800
|
+
body: JSON.stringify(body)
|
|
2801
|
+
});
|
|
2802
|
+
if (!res.ok) {
|
|
2803
|
+
const errorBody = await res.text();
|
|
2804
|
+
throw new Error(`OpenAI image generation failed (${res.status}): ${errorBody}`);
|
|
2805
|
+
}
|
|
2806
|
+
const data = await res.json();
|
|
2807
|
+
const item = data?.data?.[0];
|
|
2808
|
+
const result = {
|
|
2809
|
+
provider: "openai-media",
|
|
2810
|
+
model,
|
|
2811
|
+
modality: "image",
|
|
2812
|
+
latencyMs: Date.now() - start,
|
|
2813
|
+
usage: {
|
|
2814
|
+
cost: 0,
|
|
2815
|
+
unit: "per_image"
|
|
2816
|
+
},
|
|
2817
|
+
media: {
|
|
2818
|
+
width,
|
|
2819
|
+
height,
|
|
2820
|
+
format: "png"
|
|
2821
|
+
}
|
|
2822
|
+
};
|
|
2823
|
+
if (item?.b64_json) {
|
|
2824
|
+
result.buffer = Buffer.from(item.b64_json, "base64");
|
|
2825
|
+
} else if (item?.url) {
|
|
2826
|
+
result.url = item.url;
|
|
2827
|
+
}
|
|
2828
|
+
return result;
|
|
2829
|
+
}
|
|
2830
|
+
async speak(options) {
|
|
2831
|
+
const model = options.model ?? "tts-1";
|
|
2832
|
+
const voice = options.voice ?? "alloy";
|
|
2833
|
+
const format = options.format ?? "mp3";
|
|
2834
|
+
const speed = options.speed ?? 1;
|
|
2835
|
+
const start = Date.now();
|
|
2836
|
+
const body = {
|
|
2837
|
+
model,
|
|
2838
|
+
input: options.text,
|
|
2839
|
+
voice,
|
|
2840
|
+
response_format: format,
|
|
2841
|
+
speed
|
|
2842
|
+
};
|
|
2843
|
+
const res = await fetch(`${OPENAI_API_BASE}/audio/speech`, {
|
|
2844
|
+
method: "POST",
|
|
2845
|
+
headers: {
|
|
2846
|
+
"Content-Type": "application/json",
|
|
2847
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2848
|
+
},
|
|
2849
|
+
body: JSON.stringify(body)
|
|
2850
|
+
});
|
|
2851
|
+
if (!res.ok) {
|
|
2852
|
+
const errorBody = await res.text();
|
|
2853
|
+
throw new Error(`OpenAI TTS failed (${res.status}): ${errorBody}`);
|
|
2854
|
+
}
|
|
2855
|
+
const arrayBuffer = await res.arrayBuffer();
|
|
2856
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
2857
|
+
return {
|
|
2858
|
+
buffer,
|
|
2859
|
+
provider: "openai-media",
|
|
2860
|
+
model,
|
|
2861
|
+
modality: "tts",
|
|
2862
|
+
latencyMs: Date.now() - start,
|
|
2863
|
+
usage: {
|
|
2864
|
+
cost: 0,
|
|
2865
|
+
input: options.text.length,
|
|
2866
|
+
unit: "per_1k_chars"
|
|
2867
|
+
},
|
|
2868
|
+
media: {
|
|
2869
|
+
format
|
|
2870
|
+
}
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
async video(options) {
|
|
2874
|
+
const model = options.model ?? "sora-2";
|
|
2875
|
+
const start = Date.now();
|
|
2876
|
+
const body = {
|
|
2877
|
+
model,
|
|
2878
|
+
prompt: options.prompt,
|
|
2879
|
+
n: 1
|
|
2880
|
+
};
|
|
2881
|
+
if (options.duration) body.duration = options.duration;
|
|
2882
|
+
if (options.width && options.height) body.size = `${options.width}x${options.height}`;
|
|
2883
|
+
const res = await fetch(`${OPENAI_API_BASE}/videos/generations`, {
|
|
2884
|
+
method: "POST",
|
|
2885
|
+
headers: {
|
|
2886
|
+
"Content-Type": "application/json",
|
|
2887
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2888
|
+
},
|
|
2889
|
+
body: JSON.stringify(body)
|
|
2890
|
+
});
|
|
2891
|
+
if (!res.ok) {
|
|
2892
|
+
const errorBody = await res.text();
|
|
2893
|
+
throw new Error(`OpenAI video generation failed (${res.status}): ${errorBody}`);
|
|
2894
|
+
}
|
|
2895
|
+
const data = await res.json();
|
|
2896
|
+
const videoUrl = data?.data?.[0]?.url;
|
|
2897
|
+
return {
|
|
2898
|
+
url: videoUrl,
|
|
2899
|
+
provider: "openai-media",
|
|
2900
|
+
model,
|
|
2901
|
+
modality: "video",
|
|
2902
|
+
latencyMs: Date.now() - start,
|
|
2903
|
+
usage: {
|
|
2904
|
+
cost: 0,
|
|
2905
|
+
unit: "per_video"
|
|
2906
|
+
},
|
|
2907
|
+
media: {
|
|
2908
|
+
duration: options.duration,
|
|
2909
|
+
width: options.width,
|
|
2910
|
+
height: options.height
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
getCapabilities(id, modality) {
|
|
2915
|
+
if (modality === "image") {
|
|
2916
|
+
return {
|
|
2917
|
+
maxWidth: id.startsWith("dall-e-3") ? 1792 : 1024,
|
|
2918
|
+
maxHeight: id.startsWith("dall-e-3") ? 1792 : 1024
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
if (modality === "tts") {
|
|
2922
|
+
return {
|
|
2923
|
+
voices: ["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"]
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
if (modality === "video") {
|
|
2927
|
+
return {
|
|
2928
|
+
maxDuration: id.includes("pro") ? 20 : 10,
|
|
2929
|
+
supportsStreaming: false
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
if (modality === "stt") {
|
|
2933
|
+
return {
|
|
2934
|
+
languages: ["en", "zh", "de", "es", "ru", "ko", "fr", "ja", "pt", "tr", "pl", "ca", "nl", "ar", "sv", "it", "id", "hi", "fi", "vi", "he", "uk", "el", "ms", "cs", "ro", "da", "hu", "ta", "no", "th", "ur", "hr", "bg", "lt", "la", "mi", "ml", "cy", "sk", "te", "fa", "lv", "bn", "sr", "az", "sl", "kn", "et", "mk", "br", "eu", "is", "hy", "ne", "mn", "bs", "kk", "sq", "sw", "gl", "mr", "pa", "si", "km", "sn", "yo", "so", "af", "oc", "ka", "be", "tg", "sd", "gu", "am", "yi", "lo", "uz", "fo", "ht", "ps", "tk", "nn", "mt", "sa", "lb", "my", "bo", "tl", "mg", "as", "tt", "haw", "ln", "ha", "ba", "jw", "su"]
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
return void 0;
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
|
|
2941
|
+
// src/providers/google-media.ts
|
|
2942
|
+
var GOOGLE_API_BASE = "https://generativelanguage.googleapis.com/v1beta";
|
|
2943
|
+
var FETCH_TIMEOUT_MS6 = 8e3;
|
|
2944
|
+
var GOOGLE_TTS_VOICES = [
|
|
2945
|
+
"Aoede",
|
|
2946
|
+
"Charon",
|
|
2947
|
+
"Fenrir",
|
|
2948
|
+
"Kore",
|
|
2949
|
+
"Puck",
|
|
2950
|
+
"Leda",
|
|
2951
|
+
"Orus",
|
|
2952
|
+
"Perseus",
|
|
2953
|
+
"Zephyr",
|
|
2954
|
+
"Callirrhoe"
|
|
2955
|
+
];
|
|
2956
|
+
function classifyGoogleModel(model) {
|
|
2957
|
+
const name = (model.name ?? "").replace("models/", "");
|
|
2958
|
+
const methods = model.supportedGenerationMethods ?? [];
|
|
2959
|
+
if (name.startsWith("imagen") && methods.includes("predict")) return "image";
|
|
2960
|
+
if (name.startsWith("veo") && methods.includes("predictLongRunning")) return "video";
|
|
2961
|
+
if (name.includes("-tts") && methods.includes("generateContent")) return "tts";
|
|
2962
|
+
return null;
|
|
2963
|
+
}
|
|
2964
|
+
var GoogleMediaProvider = class {
|
|
2965
|
+
constructor(apiKey) {
|
|
2966
|
+
this.apiKey = apiKey;
|
|
2967
|
+
}
|
|
2968
|
+
id = "google-media";
|
|
2969
|
+
name = "Google (Image, Video, TTS)";
|
|
2970
|
+
modalities = ["image", "video", "tts"];
|
|
2971
|
+
isLocal = false;
|
|
2972
|
+
modelsCache = null;
|
|
2973
|
+
async ping() {
|
|
2974
|
+
try {
|
|
2975
|
+
const controller = new AbortController();
|
|
2976
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS6);
|
|
2977
|
+
try {
|
|
2978
|
+
const res = await fetch(`${GOOGLE_API_BASE}/models?key=${this.apiKey}`, {
|
|
2979
|
+
signal: controller.signal
|
|
2980
|
+
});
|
|
2981
|
+
return res.ok;
|
|
2982
|
+
} finally {
|
|
2983
|
+
clearTimeout(timer);
|
|
2984
|
+
}
|
|
2985
|
+
} catch {
|
|
2986
|
+
return false;
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
async listModels(modality) {
|
|
2990
|
+
if (this.modelsCache) {
|
|
2991
|
+
return modality ? this.modelsCache.filter((m) => m.modality === modality) : this.modelsCache;
|
|
2992
|
+
}
|
|
2993
|
+
try {
|
|
2994
|
+
const controller = new AbortController();
|
|
2995
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS6);
|
|
2996
|
+
let data;
|
|
2997
|
+
try {
|
|
2998
|
+
const res = await fetch(`${GOOGLE_API_BASE}/models?key=${this.apiKey}`, {
|
|
2999
|
+
signal: controller.signal
|
|
3000
|
+
});
|
|
3001
|
+
if (!res.ok) return [];
|
|
3002
|
+
data = await res.json();
|
|
3003
|
+
} finally {
|
|
3004
|
+
clearTimeout(timer);
|
|
3005
|
+
}
|
|
3006
|
+
const entries = data?.models ?? [];
|
|
3007
|
+
const logo = getProviderLogo("google");
|
|
3008
|
+
const models = [];
|
|
3009
|
+
for (const entry of entries) {
|
|
3010
|
+
const modality2 = classifyGoogleModel(entry);
|
|
3011
|
+
if (!modality2) continue;
|
|
3012
|
+
const fullName = entry.name ?? "";
|
|
3013
|
+
const modelId = fullName.startsWith("models/") ? fullName.slice("models/".length) : fullName;
|
|
3014
|
+
const info = {
|
|
3015
|
+
id: modelId,
|
|
3016
|
+
provider: "google-media",
|
|
3017
|
+
name: entry.displayName ?? modelId,
|
|
3018
|
+
modality: modality2,
|
|
3019
|
+
local: false,
|
|
3020
|
+
cost: { price: 0, unit: modality2 === "video" ? "per_video" : "per_image" },
|
|
3021
|
+
logo,
|
|
3022
|
+
description: entry.description,
|
|
3023
|
+
capabilities: modality2 === "video" ? { maxDuration: 8, supportsStreaming: false } : modality2 === "tts" ? { voices: GOOGLE_TTS_VOICES } : void 0
|
|
3024
|
+
};
|
|
3025
|
+
models.push(info);
|
|
3026
|
+
}
|
|
3027
|
+
this.modelsCache = models;
|
|
3028
|
+
return modality ? models.filter((m) => m.modality === modality) : models;
|
|
3029
|
+
} catch {
|
|
3030
|
+
return [];
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
async image(options) {
|
|
3034
|
+
const model = options.model ?? "imagen-4.0-generate-001";
|
|
3035
|
+
const start = Date.now();
|
|
3036
|
+
const body = {
|
|
3037
|
+
instances: [{ prompt: options.prompt }],
|
|
3038
|
+
parameters: {
|
|
3039
|
+
sampleCount: 1
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
const res = await fetch(
|
|
3043
|
+
`${GOOGLE_API_BASE}/models/${model}:predict?key=${this.apiKey}`,
|
|
3044
|
+
{
|
|
3045
|
+
method: "POST",
|
|
3046
|
+
headers: { "Content-Type": "application/json" },
|
|
3047
|
+
body: JSON.stringify(body)
|
|
3048
|
+
}
|
|
3049
|
+
);
|
|
3050
|
+
if (!res.ok) {
|
|
3051
|
+
const errorBody = await res.text();
|
|
3052
|
+
throw new Error(`Google image generation failed (${res.status}): ${errorBody}`);
|
|
3053
|
+
}
|
|
3054
|
+
const data = await res.json();
|
|
3055
|
+
const base64 = data?.predictions?.[0]?.bytesBase64Encoded ?? data?.generatedImages?.[0]?.image?.imageBytes;
|
|
3056
|
+
if (!base64) {
|
|
3057
|
+
throw new Error("Google image generation returned no image data");
|
|
3058
|
+
}
|
|
3059
|
+
const buffer = Buffer.from(base64, "base64");
|
|
3060
|
+
return {
|
|
3061
|
+
buffer,
|
|
3062
|
+
provider: "google-media",
|
|
3063
|
+
model,
|
|
3064
|
+
modality: "image",
|
|
3065
|
+
latencyMs: Date.now() - start,
|
|
3066
|
+
usage: {
|
|
3067
|
+
cost: 0,
|
|
3068
|
+
unit: "per_image"
|
|
3069
|
+
},
|
|
3070
|
+
media: {
|
|
3071
|
+
format: "png"
|
|
3072
|
+
}
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
3075
|
+
async speak(options) {
|
|
3076
|
+
const model = options.model ?? "gemini-2.5-flash-preview-tts";
|
|
3077
|
+
const voice = options.voice ?? "Kore";
|
|
3078
|
+
const start = Date.now();
|
|
3079
|
+
const body = {
|
|
3080
|
+
contents: [{ parts: [{ text: options.text }] }],
|
|
3081
|
+
generationConfig: {
|
|
3082
|
+
response_modalities: ["AUDIO"],
|
|
3083
|
+
speech_config: {
|
|
3084
|
+
voiceConfig: {
|
|
3085
|
+
prebuiltVoiceConfig: { voiceName: voice }
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
};
|
|
3090
|
+
const res = await fetch(
|
|
3091
|
+
`${GOOGLE_API_BASE}/models/${model}:generateContent?key=${this.apiKey}`,
|
|
3092
|
+
{
|
|
3093
|
+
method: "POST",
|
|
3094
|
+
headers: { "Content-Type": "application/json" },
|
|
3095
|
+
body: JSON.stringify(body)
|
|
3096
|
+
}
|
|
3097
|
+
);
|
|
3098
|
+
if (!res.ok) {
|
|
3099
|
+
const errorBody = await res.text();
|
|
3100
|
+
throw new Error(`Google TTS failed (${res.status}): ${errorBody}`);
|
|
3101
|
+
}
|
|
3102
|
+
const data = await res.json();
|
|
3103
|
+
const inlineData = data?.candidates?.[0]?.content?.parts?.[0]?.inlineData;
|
|
3104
|
+
if (!inlineData?.data) {
|
|
3105
|
+
throw new Error("Google TTS returned no audio data");
|
|
3106
|
+
}
|
|
3107
|
+
const buffer = Buffer.from(inlineData.data, "base64");
|
|
3108
|
+
return {
|
|
3109
|
+
buffer,
|
|
3110
|
+
provider: "google-media",
|
|
3111
|
+
model,
|
|
3112
|
+
modality: "tts",
|
|
3113
|
+
latencyMs: Date.now() - start,
|
|
3114
|
+
usage: {
|
|
3115
|
+
cost: 0,
|
|
3116
|
+
input: options.text.length,
|
|
3117
|
+
unit: "per_1k_chars"
|
|
3118
|
+
},
|
|
3119
|
+
media: {
|
|
3120
|
+
format: "wav"
|
|
3121
|
+
// Google returns PCM L16, essentially WAV
|
|
3122
|
+
}
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3125
|
+
async video(options) {
|
|
3126
|
+
const model = options.model ?? "veo-2.0-generate-001";
|
|
3127
|
+
const start = Date.now();
|
|
3128
|
+
const body = {
|
|
3129
|
+
instances: [{ prompt: options.prompt }],
|
|
3130
|
+
parameters: {
|
|
3131
|
+
sampleCount: 1
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
if (options.duration) body.parameters.durationSeconds = options.duration;
|
|
3135
|
+
const res = await fetch(
|
|
3136
|
+
`${GOOGLE_API_BASE}/models/${model}:predictLongRunning?key=${this.apiKey}`,
|
|
3137
|
+
{
|
|
3138
|
+
method: "POST",
|
|
3139
|
+
headers: { "Content-Type": "application/json" },
|
|
3140
|
+
body: JSON.stringify(body)
|
|
3141
|
+
}
|
|
3142
|
+
);
|
|
3143
|
+
if (!res.ok) {
|
|
3144
|
+
const errorBody = await res.text();
|
|
3145
|
+
throw new Error(`Google video generation failed (${res.status}): ${errorBody}`);
|
|
3146
|
+
}
|
|
3147
|
+
const operation = await res.json();
|
|
3148
|
+
const operationName = operation?.name;
|
|
3149
|
+
if (!operationName) {
|
|
3150
|
+
throw new Error("Google video generation returned no operation name");
|
|
3151
|
+
}
|
|
3152
|
+
const deadline = Date.now() + 3e5;
|
|
3153
|
+
while (Date.now() < deadline) {
|
|
3154
|
+
await new Promise((r) => setTimeout(r, 5e3));
|
|
3155
|
+
const pollRes = await fetch(
|
|
3156
|
+
`${GOOGLE_API_BASE}/${operationName}?key=${this.apiKey}`
|
|
3157
|
+
);
|
|
3158
|
+
if (!pollRes.ok) continue;
|
|
3159
|
+
const status = await pollRes.json();
|
|
3160
|
+
if (status.done) {
|
|
3161
|
+
if (status.error) {
|
|
3162
|
+
throw new Error(`Google video generation error: ${status.error.message ?? JSON.stringify(status.error)}`);
|
|
3163
|
+
}
|
|
3164
|
+
const resp = status.response ?? {};
|
|
3165
|
+
const samples = resp.generateVideoResponse?.generatedSamples ?? resp.generatedSamples ?? [];
|
|
3166
|
+
const video = samples[0]?.video;
|
|
3167
|
+
if (video?.bytesBase64Encoded) {
|
|
3168
|
+
return {
|
|
3169
|
+
buffer: Buffer.from(video.bytesBase64Encoded, "base64"),
|
|
3170
|
+
provider: "google-media",
|
|
3171
|
+
model,
|
|
3172
|
+
modality: "video",
|
|
3173
|
+
latencyMs: Date.now() - start,
|
|
3174
|
+
usage: { cost: 0, unit: "per_video" },
|
|
3175
|
+
media: { format: "mp4", duration: options.duration }
|
|
3176
|
+
};
|
|
3177
|
+
}
|
|
3178
|
+
if (video?.uri) {
|
|
3179
|
+
const separator = video.uri.includes("?") ? "&" : "?";
|
|
3180
|
+
const videoRes = await fetch(`${video.uri}${separator}key=${this.apiKey}`, { redirect: "follow" });
|
|
3181
|
+
if (videoRes.ok) {
|
|
3182
|
+
const buffer = Buffer.from(await videoRes.arrayBuffer());
|
|
3183
|
+
return {
|
|
3184
|
+
buffer,
|
|
3185
|
+
provider: "google-media",
|
|
3186
|
+
model,
|
|
3187
|
+
modality: "video",
|
|
3188
|
+
latencyMs: Date.now() - start,
|
|
3189
|
+
usage: { cost: 0, unit: "per_video" },
|
|
3190
|
+
media: { format: "mp4", duration: options.duration }
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
return {
|
|
3194
|
+
url: video.uri,
|
|
3195
|
+
provider: "google-media",
|
|
3196
|
+
model,
|
|
3197
|
+
modality: "video",
|
|
3198
|
+
latencyMs: Date.now() - start,
|
|
3199
|
+
usage: { cost: 0, unit: "per_video" },
|
|
3200
|
+
media: { format: "mp4", duration: options.duration }
|
|
3201
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
throw new Error("Google video generation completed but returned no video data");
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
throw new Error(`Google video generation timed out after 5 minutes`);
|
|
3207
|
+
}
|
|
3208
|
+
};
|
|
3209
|
+
|
|
2541
3210
|
// src/noosphere.ts
|
|
2542
3211
|
var Noosphere = class {
|
|
2543
3212
|
config;
|
|
@@ -2728,6 +3397,12 @@ var Noosphere = class {
|
|
|
2728
3397
|
getUsage(options) {
|
|
2729
3398
|
return this.tracker.getSummary(options);
|
|
2730
3399
|
}
|
|
3400
|
+
async countTokens(options) {
|
|
3401
|
+
const keys = {};
|
|
3402
|
+
if (this.config.keys?.google) keys.google = this.config.keys.google;
|
|
3403
|
+
if (this.config.keys?.anthropic) keys.anthropic = this.config.keys.anthropic;
|
|
3404
|
+
return countTokens(options, keys);
|
|
3405
|
+
}
|
|
2731
3406
|
// --- Local Model Management ---
|
|
2732
3407
|
async installModel(name) {
|
|
2733
3408
|
if (!this.initialized) await this.init();
|
|
@@ -2780,6 +3455,12 @@ var Noosphere = class {
|
|
|
2780
3455
|
if (hasAnyLLMKey) {
|
|
2781
3456
|
this.registry.addProvider(new PiAiProvider(llmKeys));
|
|
2782
3457
|
}
|
|
3458
|
+
if (keys.openai) {
|
|
3459
|
+
this.registry.addProvider(new OpenAIMediaProvider(keys.openai));
|
|
3460
|
+
}
|
|
3461
|
+
if (keys.google) {
|
|
3462
|
+
this.registry.addProvider(new GoogleMediaProvider(keys.google));
|
|
3463
|
+
}
|
|
2783
3464
|
if (keys.fal) {
|
|
2784
3465
|
this.registry.addProvider(new FalProvider(keys.fal));
|
|
2785
3466
|
}
|
|
@@ -2956,19 +3637,48 @@ var Noosphere = class {
|
|
|
2956
3637
|
await this.tracker.record(event);
|
|
2957
3638
|
}
|
|
2958
3639
|
};
|
|
3640
|
+
|
|
3641
|
+
// src/index.ts
|
|
3642
|
+
var import_pi_ai3 = require("@mariozechner/pi-ai");
|
|
3643
|
+
var import_pi_ai4 = require("@mariozechner/pi-ai");
|
|
3644
|
+
var import_pi_ai5 = require("@mariozechner/pi-ai");
|
|
3645
|
+
var import_pi_ai6 = require("@mariozechner/pi-ai");
|
|
2959
3646
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2960
3647
|
0 && (module.exports = {
|
|
2961
3648
|
AudioCraftProvider,
|
|
3649
|
+
GoogleMediaProvider,
|
|
2962
3650
|
HfLocalProvider,
|
|
2963
3651
|
Noosphere,
|
|
2964
3652
|
NoosphereError,
|
|
2965
3653
|
OllamaProvider,
|
|
2966
3654
|
OpenAICompatProvider,
|
|
3655
|
+
OpenAIMediaProvider,
|
|
2967
3656
|
PROVIDER_IDS,
|
|
2968
3657
|
PROVIDER_LOGOS,
|
|
2969
3658
|
WhisperLocalProvider,
|
|
3659
|
+
agentLoop,
|
|
3660
|
+
calculateCost,
|
|
3661
|
+
countTokens,
|
|
3662
|
+
countTokensAnthropic,
|
|
3663
|
+
countTokensCerebras,
|
|
3664
|
+
countTokensGoogle,
|
|
3665
|
+
countTokensGroq,
|
|
3666
|
+
countTokensMistral,
|
|
3667
|
+
countTokensOllama,
|
|
3668
|
+
countTokensOpenAI,
|
|
3669
|
+
countTokensOpenRouter,
|
|
3670
|
+
countTokensXai,
|
|
2970
3671
|
detectOpenAICompatServers,
|
|
2971
3672
|
getAllProviderLogos,
|
|
2972
|
-
|
|
3673
|
+
getApiKey,
|
|
3674
|
+
getPiModel,
|
|
3675
|
+
getPiModels,
|
|
3676
|
+
getPiProviders,
|
|
3677
|
+
getProviderLogo,
|
|
3678
|
+
piComplete,
|
|
3679
|
+
piCompleteSimple,
|
|
3680
|
+
piStream,
|
|
3681
|
+
piStreamSimple,
|
|
3682
|
+
setApiKey
|
|
2973
3683
|
});
|
|
2974
3684
|
//# sourceMappingURL=index.cjs.map
|