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/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
- getProviderLogo: () => getProviderLogo
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
- getProviderLogo
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