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.js CHANGED
@@ -108,6 +108,136 @@ function resolveConfig(input) {
108
108
  };
109
109
  }
110
110
 
111
+ // src/token-counter.ts
112
+ import { encoding_for_model } from "tiktoken";
113
+ var TIKTOKEN_MODEL_MAP = {
114
+ "gpt-4o": "gpt-4o",
115
+ "gpt-4o-mini": "gpt-4o-mini",
116
+ "gpt-4-turbo": "gpt-4-turbo",
117
+ "gpt-4": "gpt-4",
118
+ "gpt-3.5-turbo": "gpt-3.5-turbo"
119
+ };
120
+ function resolveTiktokenModel(model) {
121
+ if (model in TIKTOKEN_MODEL_MAP) return TIKTOKEN_MODEL_MAP[model];
122
+ for (const [prefix, tikModel] of Object.entries(TIKTOKEN_MODEL_MAP)) {
123
+ if (model.startsWith(prefix)) return tikModel;
124
+ }
125
+ return "gpt-4o";
126
+ }
127
+ function countWithTiktoken(messages, model) {
128
+ const tikModel = resolveTiktokenModel(model);
129
+ const enc = encoding_for_model(tikModel);
130
+ let tokens = 0;
131
+ for (const msg of messages) {
132
+ tokens += 4;
133
+ tokens += enc.encode(msg.role).length;
134
+ tokens += enc.encode(msg.content).length;
135
+ }
136
+ tokens += 2;
137
+ enc.free();
138
+ return tokens;
139
+ }
140
+ function countTokensOpenAI(messages, model = "gpt-4o") {
141
+ return countWithTiktoken(messages, model);
142
+ }
143
+ async function countTokensGoogle(messages, apiKey, model = "gemini-2.5-flash") {
144
+ const contents = messages.map((m) => ({
145
+ role: m.role === "assistant" ? "model" : "user",
146
+ parts: [{ text: m.content }]
147
+ }));
148
+ const res = await fetch(
149
+ `https://generativelanguage.googleapis.com/v1beta/models/${model}:countTokens?key=${apiKey}`,
150
+ {
151
+ method: "POST",
152
+ headers: { "Content-Type": "application/json" },
153
+ body: JSON.stringify({ contents })
154
+ }
155
+ );
156
+ if (!res.ok) {
157
+ throw new Error(`Google countTokens failed (${res.status}): ${await res.text()}`);
158
+ }
159
+ const data = await res.json();
160
+ return data.totalTokens;
161
+ }
162
+ async function countTokensAnthropic(messages, apiKey, model = "claude-sonnet-4-20250514") {
163
+ const anthropicMessages = messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
164
+ const systemPrompt = messages.find((m) => m.role === "system")?.content;
165
+ const body = {
166
+ model,
167
+ messages: anthropicMessages
168
+ };
169
+ if (systemPrompt) body.system = systemPrompt;
170
+ const res = await fetch("https://api.anthropic.com/v1/messages/count_tokens", {
171
+ method: "POST",
172
+ headers: {
173
+ "x-api-key": apiKey,
174
+ "anthropic-version": "2023-06-01",
175
+ "Content-Type": "application/json"
176
+ },
177
+ body: JSON.stringify(body)
178
+ });
179
+ if (!res.ok) {
180
+ throw new Error(`Anthropic countTokens failed (${res.status}): ${await res.text()}`);
181
+ }
182
+ const data = await res.json();
183
+ return data.input_tokens;
184
+ }
185
+ function countTokensGroq(messages, model = "llama-3.3-70b-versatile") {
186
+ return countWithTiktoken(messages, model);
187
+ }
188
+ function countTokensMistral(messages, model = "mistral-large-latest") {
189
+ return countWithTiktoken(messages, model);
190
+ }
191
+ function countTokensXai(messages, model = "grok-3") {
192
+ return countWithTiktoken(messages, model);
193
+ }
194
+ function countTokensCerebras(messages, model = "llama-3.3-70b") {
195
+ return countWithTiktoken(messages, model);
196
+ }
197
+ function countTokensOpenRouter(messages, model = "openai/gpt-4o") {
198
+ return countWithTiktoken(messages, model);
199
+ }
200
+ function countTokensOllama(messages, model = "llama3.2") {
201
+ return countWithTiktoken(messages, model);
202
+ }
203
+ var PROVIDER_MODEL_PREFIXES = [
204
+ { prefixes: ["gemini", "imagen", "veo"], provider: "google" },
205
+ { prefixes: ["claude"], provider: "anthropic" },
206
+ { prefixes: ["gpt-", "o1", "o3", "o4", "chatgpt", "dall-e", "gpt-image", "tts-", "whisper", "sora"], provider: "openai" },
207
+ { prefixes: ["grok"], provider: "xai" },
208
+ { prefixes: ["mistral", "mixtral", "codestral", "ministral"], provider: "mistral" },
209
+ { prefixes: ["llama", "gemma", "qwen", "deepseek", "phi"], provider: "groq" }
210
+ ];
211
+ function inferProvider(model) {
212
+ const lower = model.toLowerCase();
213
+ for (const { prefixes, provider } of PROVIDER_MODEL_PREFIXES) {
214
+ for (const prefix of prefixes) {
215
+ if (lower.startsWith(prefix)) return provider;
216
+ }
217
+ }
218
+ return "unknown";
219
+ }
220
+ async function countTokens(options, apiKeys) {
221
+ const model = options.model ?? "gpt-4o";
222
+ const provider = options.provider ?? inferProvider(model);
223
+ if (provider === "google" && apiKeys?.google) {
224
+ const tokens2 = await countTokensGoogle(options.messages, apiKeys.google, model);
225
+ return { tokens: tokens2, model, provider: "google", method: "api" };
226
+ }
227
+ if (provider === "anthropic" && apiKeys?.anthropic) {
228
+ const tokens2 = await countTokensAnthropic(options.messages, apiKeys.anthropic, model);
229
+ return { tokens: tokens2, model, provider: "anthropic", method: "api" };
230
+ }
231
+ const tokens = countWithTiktoken(options.messages, model);
232
+ const resolvedProvider = provider === "unknown" ? "openai" : provider;
233
+ return {
234
+ tokens,
235
+ model,
236
+ provider: resolvedProvider,
237
+ method: provider === "openai" ? "tiktoken" : "tiktoken"
238
+ };
239
+ }
240
+
111
241
  // src/logos.ts
112
242
  var CDN_BASE = "https://blockchainstarter.nyc3.digitaloceanspaces.com/noosphere/logos";
113
243
  var PROVIDER_IDS = [
@@ -2508,6 +2638,522 @@ async function detectOpenAICompatServers() {
2508
2638
  return providers;
2509
2639
  }
2510
2640
 
2641
+ // src/providers/openai-media.ts
2642
+ var OPENAI_API_BASE = "https://api.openai.com/v1";
2643
+ var FETCH_TIMEOUT_MS5 = 8e3;
2644
+ var MODEL_PREFIX_MAP = [
2645
+ { prefix: "dall-e-", modality: "image" },
2646
+ { prefix: "gpt-image-", modality: "image" },
2647
+ { prefix: "sora-", modality: "video" },
2648
+ { prefix: "tts-", modality: "tts" },
2649
+ { prefix: "whisper-", modality: "stt" }
2650
+ ];
2651
+ function classifyModel(id) {
2652
+ for (const { prefix, modality } of MODEL_PREFIX_MAP) {
2653
+ if (id.startsWith(prefix)) return modality;
2654
+ }
2655
+ return null;
2656
+ }
2657
+ var OpenAIMediaProvider = class {
2658
+ constructor(apiKey) {
2659
+ this.apiKey = apiKey;
2660
+ }
2661
+ id = "openai-media";
2662
+ name = "OpenAI (Image, Video, TTS, STT)";
2663
+ modalities = ["image", "video", "tts", "stt"];
2664
+ isLocal = false;
2665
+ modelsCache = null;
2666
+ async ping() {
2667
+ try {
2668
+ const controller = new AbortController();
2669
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS5);
2670
+ try {
2671
+ const res = await fetch(`${OPENAI_API_BASE}/models`, {
2672
+ headers: { Authorization: `Bearer ${this.apiKey}` },
2673
+ signal: controller.signal
2674
+ });
2675
+ return res.ok;
2676
+ } finally {
2677
+ clearTimeout(timer);
2678
+ }
2679
+ } catch {
2680
+ return false;
2681
+ }
2682
+ }
2683
+ async listModels(modality) {
2684
+ if (this.modelsCache) {
2685
+ return modality ? this.modelsCache.filter((m) => m.modality === modality) : this.modelsCache;
2686
+ }
2687
+ try {
2688
+ const controller = new AbortController();
2689
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS5);
2690
+ let data;
2691
+ try {
2692
+ const res = await fetch(`${OPENAI_API_BASE}/models`, {
2693
+ headers: { Authorization: `Bearer ${this.apiKey}` },
2694
+ signal: controller.signal
2695
+ });
2696
+ if (!res.ok) return [];
2697
+ data = await res.json();
2698
+ } finally {
2699
+ clearTimeout(timer);
2700
+ }
2701
+ const entries = data?.data ?? [];
2702
+ const logo = getProviderLogo("openai");
2703
+ const models = [];
2704
+ for (const entry of entries) {
2705
+ const mod = classifyModel(entry.id);
2706
+ if (!mod) continue;
2707
+ const info = {
2708
+ id: entry.id,
2709
+ provider: "openai-media",
2710
+ name: entry.id,
2711
+ modality: mod,
2712
+ local: false,
2713
+ cost: { price: 0, unit: "per_request" },
2714
+ logo,
2715
+ description: entry.description,
2716
+ capabilities: this.getCapabilities(entry.id, mod)
2717
+ };
2718
+ models.push(info);
2719
+ }
2720
+ this.modelsCache = models;
2721
+ return modality ? models.filter((m) => m.modality === modality) : models;
2722
+ } catch {
2723
+ return [];
2724
+ }
2725
+ }
2726
+ async image(options) {
2727
+ const model = options.model ?? "gpt-image-1";
2728
+ const width = options.width ?? 1024;
2729
+ const height = options.height ?? 1024;
2730
+ const start = Date.now();
2731
+ const isGptImage = model.startsWith("gpt-image-");
2732
+ const body = {
2733
+ model,
2734
+ prompt: options.prompt,
2735
+ n: 1,
2736
+ size: `${width}x${height}`
2737
+ };
2738
+ if (!isGptImage) {
2739
+ body.response_format = "url";
2740
+ }
2741
+ const res = await fetch(`${OPENAI_API_BASE}/images/generations`, {
2742
+ method: "POST",
2743
+ headers: {
2744
+ "Content-Type": "application/json",
2745
+ Authorization: `Bearer ${this.apiKey}`
2746
+ },
2747
+ body: JSON.stringify(body)
2748
+ });
2749
+ if (!res.ok) {
2750
+ const errorBody = await res.text();
2751
+ throw new Error(`OpenAI image generation failed (${res.status}): ${errorBody}`);
2752
+ }
2753
+ const data = await res.json();
2754
+ const item = data?.data?.[0];
2755
+ const result = {
2756
+ provider: "openai-media",
2757
+ model,
2758
+ modality: "image",
2759
+ latencyMs: Date.now() - start,
2760
+ usage: {
2761
+ cost: 0,
2762
+ unit: "per_image"
2763
+ },
2764
+ media: {
2765
+ width,
2766
+ height,
2767
+ format: "png"
2768
+ }
2769
+ };
2770
+ if (item?.b64_json) {
2771
+ result.buffer = Buffer.from(item.b64_json, "base64");
2772
+ } else if (item?.url) {
2773
+ result.url = item.url;
2774
+ }
2775
+ return result;
2776
+ }
2777
+ async speak(options) {
2778
+ const model = options.model ?? "tts-1";
2779
+ const voice = options.voice ?? "alloy";
2780
+ const format = options.format ?? "mp3";
2781
+ const speed = options.speed ?? 1;
2782
+ const start = Date.now();
2783
+ const body = {
2784
+ model,
2785
+ input: options.text,
2786
+ voice,
2787
+ response_format: format,
2788
+ speed
2789
+ };
2790
+ const res = await fetch(`${OPENAI_API_BASE}/audio/speech`, {
2791
+ method: "POST",
2792
+ headers: {
2793
+ "Content-Type": "application/json",
2794
+ Authorization: `Bearer ${this.apiKey}`
2795
+ },
2796
+ body: JSON.stringify(body)
2797
+ });
2798
+ if (!res.ok) {
2799
+ const errorBody = await res.text();
2800
+ throw new Error(`OpenAI TTS failed (${res.status}): ${errorBody}`);
2801
+ }
2802
+ const arrayBuffer = await res.arrayBuffer();
2803
+ const buffer = Buffer.from(arrayBuffer);
2804
+ return {
2805
+ buffer,
2806
+ provider: "openai-media",
2807
+ model,
2808
+ modality: "tts",
2809
+ latencyMs: Date.now() - start,
2810
+ usage: {
2811
+ cost: 0,
2812
+ input: options.text.length,
2813
+ unit: "per_1k_chars"
2814
+ },
2815
+ media: {
2816
+ format
2817
+ }
2818
+ };
2819
+ }
2820
+ async video(options) {
2821
+ const model = options.model ?? "sora-2";
2822
+ const start = Date.now();
2823
+ const body = {
2824
+ model,
2825
+ prompt: options.prompt,
2826
+ n: 1
2827
+ };
2828
+ if (options.duration) body.duration = options.duration;
2829
+ if (options.width && options.height) body.size = `${options.width}x${options.height}`;
2830
+ const res = await fetch(`${OPENAI_API_BASE}/videos/generations`, {
2831
+ method: "POST",
2832
+ headers: {
2833
+ "Content-Type": "application/json",
2834
+ Authorization: `Bearer ${this.apiKey}`
2835
+ },
2836
+ body: JSON.stringify(body)
2837
+ });
2838
+ if (!res.ok) {
2839
+ const errorBody = await res.text();
2840
+ throw new Error(`OpenAI video generation failed (${res.status}): ${errorBody}`);
2841
+ }
2842
+ const data = await res.json();
2843
+ const videoUrl = data?.data?.[0]?.url;
2844
+ return {
2845
+ url: videoUrl,
2846
+ provider: "openai-media",
2847
+ model,
2848
+ modality: "video",
2849
+ latencyMs: Date.now() - start,
2850
+ usage: {
2851
+ cost: 0,
2852
+ unit: "per_video"
2853
+ },
2854
+ media: {
2855
+ duration: options.duration,
2856
+ width: options.width,
2857
+ height: options.height
2858
+ }
2859
+ };
2860
+ }
2861
+ getCapabilities(id, modality) {
2862
+ if (modality === "image") {
2863
+ return {
2864
+ maxWidth: id.startsWith("dall-e-3") ? 1792 : 1024,
2865
+ maxHeight: id.startsWith("dall-e-3") ? 1792 : 1024
2866
+ };
2867
+ }
2868
+ if (modality === "tts") {
2869
+ return {
2870
+ voices: ["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"]
2871
+ };
2872
+ }
2873
+ if (modality === "video") {
2874
+ return {
2875
+ maxDuration: id.includes("pro") ? 20 : 10,
2876
+ supportsStreaming: false
2877
+ };
2878
+ }
2879
+ if (modality === "stt") {
2880
+ return {
2881
+ 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"]
2882
+ };
2883
+ }
2884
+ return void 0;
2885
+ }
2886
+ };
2887
+
2888
+ // src/providers/google-media.ts
2889
+ var GOOGLE_API_BASE = "https://generativelanguage.googleapis.com/v1beta";
2890
+ var FETCH_TIMEOUT_MS6 = 8e3;
2891
+ var GOOGLE_TTS_VOICES = [
2892
+ "Aoede",
2893
+ "Charon",
2894
+ "Fenrir",
2895
+ "Kore",
2896
+ "Puck",
2897
+ "Leda",
2898
+ "Orus",
2899
+ "Perseus",
2900
+ "Zephyr",
2901
+ "Callirrhoe"
2902
+ ];
2903
+ function classifyGoogleModel(model) {
2904
+ const name = (model.name ?? "").replace("models/", "");
2905
+ const methods = model.supportedGenerationMethods ?? [];
2906
+ if (name.startsWith("imagen") && methods.includes("predict")) return "image";
2907
+ if (name.startsWith("veo") && methods.includes("predictLongRunning")) return "video";
2908
+ if (name.includes("-tts") && methods.includes("generateContent")) return "tts";
2909
+ return null;
2910
+ }
2911
+ var GoogleMediaProvider = class {
2912
+ constructor(apiKey) {
2913
+ this.apiKey = apiKey;
2914
+ }
2915
+ id = "google-media";
2916
+ name = "Google (Image, Video, TTS)";
2917
+ modalities = ["image", "video", "tts"];
2918
+ isLocal = false;
2919
+ modelsCache = null;
2920
+ async ping() {
2921
+ try {
2922
+ const controller = new AbortController();
2923
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS6);
2924
+ try {
2925
+ const res = await fetch(`${GOOGLE_API_BASE}/models?key=${this.apiKey}`, {
2926
+ signal: controller.signal
2927
+ });
2928
+ return res.ok;
2929
+ } finally {
2930
+ clearTimeout(timer);
2931
+ }
2932
+ } catch {
2933
+ return false;
2934
+ }
2935
+ }
2936
+ async listModels(modality) {
2937
+ if (this.modelsCache) {
2938
+ return modality ? this.modelsCache.filter((m) => m.modality === modality) : this.modelsCache;
2939
+ }
2940
+ try {
2941
+ const controller = new AbortController();
2942
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS6);
2943
+ let data;
2944
+ try {
2945
+ const res = await fetch(`${GOOGLE_API_BASE}/models?key=${this.apiKey}`, {
2946
+ signal: controller.signal
2947
+ });
2948
+ if (!res.ok) return [];
2949
+ data = await res.json();
2950
+ } finally {
2951
+ clearTimeout(timer);
2952
+ }
2953
+ const entries = data?.models ?? [];
2954
+ const logo = getProviderLogo("google");
2955
+ const models = [];
2956
+ for (const entry of entries) {
2957
+ const modality2 = classifyGoogleModel(entry);
2958
+ if (!modality2) continue;
2959
+ const fullName = entry.name ?? "";
2960
+ const modelId = fullName.startsWith("models/") ? fullName.slice("models/".length) : fullName;
2961
+ const info = {
2962
+ id: modelId,
2963
+ provider: "google-media",
2964
+ name: entry.displayName ?? modelId,
2965
+ modality: modality2,
2966
+ local: false,
2967
+ cost: { price: 0, unit: modality2 === "video" ? "per_video" : "per_image" },
2968
+ logo,
2969
+ description: entry.description,
2970
+ capabilities: modality2 === "video" ? { maxDuration: 8, supportsStreaming: false } : modality2 === "tts" ? { voices: GOOGLE_TTS_VOICES } : void 0
2971
+ };
2972
+ models.push(info);
2973
+ }
2974
+ this.modelsCache = models;
2975
+ return modality ? models.filter((m) => m.modality === modality) : models;
2976
+ } catch {
2977
+ return [];
2978
+ }
2979
+ }
2980
+ async image(options) {
2981
+ const model = options.model ?? "imagen-4.0-generate-001";
2982
+ const start = Date.now();
2983
+ const body = {
2984
+ instances: [{ prompt: options.prompt }],
2985
+ parameters: {
2986
+ sampleCount: 1
2987
+ }
2988
+ };
2989
+ const res = await fetch(
2990
+ `${GOOGLE_API_BASE}/models/${model}:predict?key=${this.apiKey}`,
2991
+ {
2992
+ method: "POST",
2993
+ headers: { "Content-Type": "application/json" },
2994
+ body: JSON.stringify(body)
2995
+ }
2996
+ );
2997
+ if (!res.ok) {
2998
+ const errorBody = await res.text();
2999
+ throw new Error(`Google image generation failed (${res.status}): ${errorBody}`);
3000
+ }
3001
+ const data = await res.json();
3002
+ const base64 = data?.predictions?.[0]?.bytesBase64Encoded ?? data?.generatedImages?.[0]?.image?.imageBytes;
3003
+ if (!base64) {
3004
+ throw new Error("Google image generation returned no image data");
3005
+ }
3006
+ const buffer = Buffer.from(base64, "base64");
3007
+ return {
3008
+ buffer,
3009
+ provider: "google-media",
3010
+ model,
3011
+ modality: "image",
3012
+ latencyMs: Date.now() - start,
3013
+ usage: {
3014
+ cost: 0,
3015
+ unit: "per_image"
3016
+ },
3017
+ media: {
3018
+ format: "png"
3019
+ }
3020
+ };
3021
+ }
3022
+ async speak(options) {
3023
+ const model = options.model ?? "gemini-2.5-flash-preview-tts";
3024
+ const voice = options.voice ?? "Kore";
3025
+ const start = Date.now();
3026
+ const body = {
3027
+ contents: [{ parts: [{ text: options.text }] }],
3028
+ generationConfig: {
3029
+ response_modalities: ["AUDIO"],
3030
+ speech_config: {
3031
+ voiceConfig: {
3032
+ prebuiltVoiceConfig: { voiceName: voice }
3033
+ }
3034
+ }
3035
+ }
3036
+ };
3037
+ const res = await fetch(
3038
+ `${GOOGLE_API_BASE}/models/${model}:generateContent?key=${this.apiKey}`,
3039
+ {
3040
+ method: "POST",
3041
+ headers: { "Content-Type": "application/json" },
3042
+ body: JSON.stringify(body)
3043
+ }
3044
+ );
3045
+ if (!res.ok) {
3046
+ const errorBody = await res.text();
3047
+ throw new Error(`Google TTS failed (${res.status}): ${errorBody}`);
3048
+ }
3049
+ const data = await res.json();
3050
+ const inlineData = data?.candidates?.[0]?.content?.parts?.[0]?.inlineData;
3051
+ if (!inlineData?.data) {
3052
+ throw new Error("Google TTS returned no audio data");
3053
+ }
3054
+ const buffer = Buffer.from(inlineData.data, "base64");
3055
+ return {
3056
+ buffer,
3057
+ provider: "google-media",
3058
+ model,
3059
+ modality: "tts",
3060
+ latencyMs: Date.now() - start,
3061
+ usage: {
3062
+ cost: 0,
3063
+ input: options.text.length,
3064
+ unit: "per_1k_chars"
3065
+ },
3066
+ media: {
3067
+ format: "wav"
3068
+ // Google returns PCM L16, essentially WAV
3069
+ }
3070
+ };
3071
+ }
3072
+ async video(options) {
3073
+ const model = options.model ?? "veo-2.0-generate-001";
3074
+ const start = Date.now();
3075
+ const body = {
3076
+ instances: [{ prompt: options.prompt }],
3077
+ parameters: {
3078
+ sampleCount: 1
3079
+ }
3080
+ };
3081
+ if (options.duration) body.parameters.durationSeconds = options.duration;
3082
+ const res = await fetch(
3083
+ `${GOOGLE_API_BASE}/models/${model}:predictLongRunning?key=${this.apiKey}`,
3084
+ {
3085
+ method: "POST",
3086
+ headers: { "Content-Type": "application/json" },
3087
+ body: JSON.stringify(body)
3088
+ }
3089
+ );
3090
+ if (!res.ok) {
3091
+ const errorBody = await res.text();
3092
+ throw new Error(`Google video generation failed (${res.status}): ${errorBody}`);
3093
+ }
3094
+ const operation = await res.json();
3095
+ const operationName = operation?.name;
3096
+ if (!operationName) {
3097
+ throw new Error("Google video generation returned no operation name");
3098
+ }
3099
+ const deadline = Date.now() + 3e5;
3100
+ while (Date.now() < deadline) {
3101
+ await new Promise((r) => setTimeout(r, 5e3));
3102
+ const pollRes = await fetch(
3103
+ `${GOOGLE_API_BASE}/${operationName}?key=${this.apiKey}`
3104
+ );
3105
+ if (!pollRes.ok) continue;
3106
+ const status = await pollRes.json();
3107
+ if (status.done) {
3108
+ if (status.error) {
3109
+ throw new Error(`Google video generation error: ${status.error.message ?? JSON.stringify(status.error)}`);
3110
+ }
3111
+ const resp = status.response ?? {};
3112
+ const samples = resp.generateVideoResponse?.generatedSamples ?? resp.generatedSamples ?? [];
3113
+ const video = samples[0]?.video;
3114
+ if (video?.bytesBase64Encoded) {
3115
+ return {
3116
+ buffer: Buffer.from(video.bytesBase64Encoded, "base64"),
3117
+ provider: "google-media",
3118
+ model,
3119
+ modality: "video",
3120
+ latencyMs: Date.now() - start,
3121
+ usage: { cost: 0, unit: "per_video" },
3122
+ media: { format: "mp4", duration: options.duration }
3123
+ };
3124
+ }
3125
+ if (video?.uri) {
3126
+ const separator = video.uri.includes("?") ? "&" : "?";
3127
+ const videoRes = await fetch(`${video.uri}${separator}key=${this.apiKey}`, { redirect: "follow" });
3128
+ if (videoRes.ok) {
3129
+ const buffer = Buffer.from(await videoRes.arrayBuffer());
3130
+ return {
3131
+ buffer,
3132
+ provider: "google-media",
3133
+ model,
3134
+ modality: "video",
3135
+ latencyMs: Date.now() - start,
3136
+ usage: { cost: 0, unit: "per_video" },
3137
+ media: { format: "mp4", duration: options.duration }
3138
+ };
3139
+ }
3140
+ return {
3141
+ url: video.uri,
3142
+ provider: "google-media",
3143
+ model,
3144
+ modality: "video",
3145
+ latencyMs: Date.now() - start,
3146
+ usage: { cost: 0, unit: "per_video" },
3147
+ media: { format: "mp4", duration: options.duration }
3148
+ };
3149
+ }
3150
+ throw new Error("Google video generation completed but returned no video data");
3151
+ }
3152
+ }
3153
+ throw new Error(`Google video generation timed out after 5 minutes`);
3154
+ }
3155
+ };
3156
+
2511
3157
  // src/noosphere.ts
2512
3158
  var Noosphere = class {
2513
3159
  config;
@@ -2698,6 +3344,12 @@ var Noosphere = class {
2698
3344
  getUsage(options) {
2699
3345
  return this.tracker.getSummary(options);
2700
3346
  }
3347
+ async countTokens(options) {
3348
+ const keys = {};
3349
+ if (this.config.keys?.google) keys.google = this.config.keys.google;
3350
+ if (this.config.keys?.anthropic) keys.anthropic = this.config.keys.anthropic;
3351
+ return countTokens(options, keys);
3352
+ }
2701
3353
  // --- Local Model Management ---
2702
3354
  async installModel(name) {
2703
3355
  if (!this.initialized) await this.init();
@@ -2750,6 +3402,12 @@ var Noosphere = class {
2750
3402
  if (hasAnyLLMKey) {
2751
3403
  this.registry.addProvider(new PiAiProvider(llmKeys));
2752
3404
  }
3405
+ if (keys.openai) {
3406
+ this.registry.addProvider(new OpenAIMediaProvider(keys.openai));
3407
+ }
3408
+ if (keys.google) {
3409
+ this.registry.addProvider(new GoogleMediaProvider(keys.google));
3410
+ }
2753
3411
  if (keys.fal) {
2754
3412
  this.registry.addProvider(new FalProvider(keys.fal));
2755
3413
  }
@@ -2926,18 +3584,47 @@ var Noosphere = class {
2926
3584
  await this.tracker.record(event);
2927
3585
  }
2928
3586
  };
3587
+
3588
+ // src/index.ts
3589
+ import { agentLoop } from "@mariozechner/pi-ai";
3590
+ import { calculateCost, getModel, getModels as getModels2, getProviders as getProviders2 } from "@mariozechner/pi-ai";
3591
+ import { stream as stream2, complete as complete2, streamSimple, completeSimple } from "@mariozechner/pi-ai";
3592
+ import { setApiKey as setApiKey2, getApiKey } from "@mariozechner/pi-ai";
2929
3593
  export {
2930
3594
  AudioCraftProvider,
3595
+ GoogleMediaProvider,
2931
3596
  HfLocalProvider,
2932
3597
  Noosphere,
2933
3598
  NoosphereError,
2934
3599
  OllamaProvider,
2935
3600
  OpenAICompatProvider,
3601
+ OpenAIMediaProvider,
2936
3602
  PROVIDER_IDS,
2937
3603
  PROVIDER_LOGOS,
2938
3604
  WhisperLocalProvider,
3605
+ agentLoop,
3606
+ calculateCost,
3607
+ countTokens,
3608
+ countTokensAnthropic,
3609
+ countTokensCerebras,
3610
+ countTokensGoogle,
3611
+ countTokensGroq,
3612
+ countTokensMistral,
3613
+ countTokensOllama,
3614
+ countTokensOpenAI,
3615
+ countTokensOpenRouter,
3616
+ countTokensXai,
2939
3617
  detectOpenAICompatServers,
2940
3618
  getAllProviderLogos,
2941
- getProviderLogo
3619
+ getApiKey,
3620
+ getModel as getPiModel,
3621
+ getModels2 as getPiModels,
3622
+ getProviders2 as getPiProviders,
3623
+ getProviderLogo,
3624
+ complete2 as piComplete,
3625
+ completeSimple as piCompleteSimple,
3626
+ stream2 as piStream,
3627
+ streamSimple as piStreamSimple,
3628
+ setApiKey2 as setApiKey
2942
3629
  };
2943
3630
  //# sourceMappingURL=index.js.map